mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
tools/sched_ext: scx_qmap - Consume cgroup weights through set_weight
With the set_* ops delivered to the parent's sched, a parent qmap instance now receives cgroup_set_weight for its child subs' attach points. Update the matching sub_sched_ctx weight and redistribute() in-kernel, and drop the userspace feed_weights() polling. This exercises the knob routing end to end. The self weight is fixed at 100: a cgroup's weight is its parent's knob and not the scheduler's own business. This drops the self-weight polling and the repartition PROG_RUN poke with it. sub_attach seeds the slot with the cgroup's current weight, read through bpf_cgroup_from_id(), so a weight set before the sub attaches is picked up. A write racing the attach can still be lost until the next value-changing cpu.weight write. Acceptable for a demo. While at it, add a traced ops.cgroup_move() so tests can observe move delivery. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
This commit is contained in:
parent
a6ec0b62c5
commit
29ac3ae9ab
|
|
@ -1019,10 +1019,32 @@ s32 BPF_STRUCT_OPS(qmap_cgroup_init, struct cgroup *cgrp, struct scx_cgroup_init
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void redistribute(void);
|
||||
|
||||
void BPF_STRUCT_OPS(qmap_cgroup_set_weight, struct cgroup *cgrp, u32 weight)
|
||||
{
|
||||
u64 cgid = cgrp->kn->id;
|
||||
s32 i;
|
||||
|
||||
QMAP_TOUCH_ARENA();
|
||||
|
||||
if (print_msgs)
|
||||
bpf_printk("CGRP SET %llu weight=%u", cgrp->kn->id, weight);
|
||||
bpf_printk("CGRP SET %llu weight=%u", cgid, weight);
|
||||
|
||||
/*
|
||||
* Knobs belong to the parent, so this op carries the child subs'
|
||||
* attach point weights. Adjust the matching sub's share of the cid
|
||||
* partition. Other cgroups don't participate in the split.
|
||||
*/
|
||||
for (i = 0; i < MAX_SUB_SCHEDS; i++) {
|
||||
if (qa.sub_sched_ctxs[i].cgroup_id != cgid)
|
||||
continue;
|
||||
if (qa.sub_sched_ctxs[i].weight != weight) {
|
||||
qa.sub_sched_ctxs[i].weight = weight;
|
||||
redistribute();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(qmap_cgroup_set_bandwidth, struct cgroup *cgrp,
|
||||
|
|
@ -1033,6 +1055,14 @@ void BPF_STRUCT_OPS(qmap_cgroup_set_bandwidth, struct cgroup *cgrp,
|
|||
cgrp->kn->id, period_us, quota_us, burst_us);
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(qmap_cgroup_move, struct task_struct *p,
|
||||
struct cgroup *from, struct cgroup *to)
|
||||
{
|
||||
if (print_msgs)
|
||||
bpf_printk("CGRP MOVE %d %llu -> %llu",
|
||||
p->pid, from->kn->id, to->kn->id);
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(qmap_update_idle, s32 cid, bool idle)
|
||||
{
|
||||
QMAP_TOUCH_ARENA();
|
||||
|
|
@ -1336,10 +1366,12 @@ __noinline void compute_partition(void)
|
|||
}
|
||||
|
||||
/*
|
||||
* Snapshot membership and weights so the sum_w and share loops agree.
|
||||
* A mid-compute change would otherwise wrap nr_shared negative.
|
||||
* Snapshot membership and weights so the sum_w and share loops agree. A
|
||||
* mid-compute change would otherwise wrap nr_shared negative. The self
|
||||
* weight is fixed at the default: a cgroup's weight is its parent's
|
||||
* knob, not the scheduler's own business.
|
||||
*/
|
||||
self_w = qa.self_weight ?: 100;
|
||||
self_w = 100;
|
||||
bpf_for(i, 0, MAX_SUB_SCHEDS) {
|
||||
cgid_snap[i] = qa.sub_sched_ctxs[i].cgroup_id;
|
||||
w_snap[i] = cgid_snap[i] ? (qa.sub_sched_ctxs[i].weight ?: 100) : 0;
|
||||
|
|
@ -1565,7 +1597,7 @@ __noinline void apply_partition(void)
|
|||
|
||||
/*
|
||||
* Recompute the split off the node's held caps and apply it. The contexts this
|
||||
* runs from (the sub-sched callbacks, the userspace poke, the rr timer) are not
|
||||
* runs from (the sub-sched and cgroup callbacks, the rr timer) are not
|
||||
* serialized by the kernel, so a single runner does the work. A caller that
|
||||
* finds the guard held leaves part_pending set; the holder drains it before
|
||||
* releasing, with the rr timer as a backstop.
|
||||
|
|
@ -1592,14 +1624,6 @@ static void redistribute(void)
|
|||
part_end();
|
||||
}
|
||||
|
||||
/* userspace pokes this (PROG_RUN) to resplit after a cpu.weight change */
|
||||
SEC("syscall")
|
||||
int repartition(void *ctx)
|
||||
{
|
||||
redistribute();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Userspace pokes this (PROG_RUN) to bring alloc_ns[] current before reading
|
||||
* it for the stats display. Skipping when the partition guard is held is
|
||||
|
|
@ -1849,6 +1873,33 @@ void BPF_STRUCT_OPS(qmap_exit, struct scx_exit_info *ei)
|
|||
UEI_RECORD(uei, ei);
|
||||
}
|
||||
|
||||
/*
|
||||
* Seed a new sub slot with the cgroup's current weight. The kernel delivers
|
||||
* ops.cgroup_set_weight() only on value-changing writes, so a weight set
|
||||
* before the sub attached would otherwise go unnoticed.
|
||||
*/
|
||||
static u32 cgrp_cur_weight(u64 cgid)
|
||||
{
|
||||
struct cgroup_subsys_state *css;
|
||||
struct cgroup *cgrp;
|
||||
u32 weight = 100;
|
||||
|
||||
cgrp = bpf_cgroup_from_id(cgid);
|
||||
if (!cgrp)
|
||||
return weight;
|
||||
|
||||
css = BPF_CORE_READ(cgrp, subsys[cpu_cgrp_id]);
|
||||
if (css) {
|
||||
struct task_group *tg = container_of(css, struct task_group, css);
|
||||
u32 w = BPF_CORE_READ(tg, scx.weight);
|
||||
|
||||
if (w)
|
||||
weight = w;
|
||||
}
|
||||
bpf_cgroup_release(cgrp);
|
||||
return weight;
|
||||
}
|
||||
|
||||
s32 BPF_STRUCT_OPS(qmap_sub_attach, struct scx_sub_attach_args *args)
|
||||
{
|
||||
s32 i;
|
||||
|
|
@ -1862,7 +1913,7 @@ s32 BPF_STRUCT_OPS(qmap_sub_attach, struct scx_sub_attach_args *args)
|
|||
continue;
|
||||
|
||||
qa.sub_sched_ctxs[i].cgroup_id = args->ops->sub_cgroup_id;
|
||||
qa.sub_sched_ctxs[i].weight = 100; /* until userspace feeds it */
|
||||
qa.sub_sched_ctxs[i].weight = cgrp_cur_weight(args->ops->sub_cgroup_id);
|
||||
qa.nr_sub_scheds++;
|
||||
bpf_printk("attaching sub-sched[%d] on %s", i, args->cgroup_path);
|
||||
redistribute();
|
||||
|
|
@ -1926,6 +1977,7 @@ SCX_OPS_CID_DEFINE(qmap_ops,
|
|||
.cgroup_init = (void *)qmap_cgroup_init,
|
||||
.cgroup_set_weight = (void *)qmap_cgroup_set_weight,
|
||||
.cgroup_set_bandwidth = (void *)qmap_cgroup_set_bandwidth,
|
||||
.cgroup_move = (void *)qmap_cgroup_move,
|
||||
.sub_attach = (void *)qmap_sub_attach,
|
||||
.sub_detach = (void *)qmap_sub_detach,
|
||||
.sub_caps_updated = (void *)qmap_sub_caps_updated,
|
||||
|
|
|
|||
|
|
@ -24,11 +24,6 @@
|
|||
#include "scx_qmap.h"
|
||||
#include "scx_qmap.bpf.skel.h"
|
||||
|
||||
/* kernfs file-handle type for open_by_handle_at(), from linux/exportfs.h */
|
||||
#ifndef FILEID_KERNFS
|
||||
#define FILEID_KERNFS 0xfe
|
||||
#endif
|
||||
|
||||
const char help_fmt[] =
|
||||
"A simple five-level FIFO queue sched_ext scheduler.\n"
|
||||
"\n"
|
||||
|
|
@ -72,7 +67,7 @@ const char help_fmt[] =
|
|||
" -I Turn on SCX_OPS_ALWAYS_ENQ_IMMED\n"
|
||||
" -F COUNT IMMED stress: force every COUNT'th enqueue to a busy local DSQ (use with -I)\n"
|
||||
" -C MODE cid-override test (shuffle|bad-dup|bad-range|bad-mono)\n"
|
||||
" -i SEC Stats and weight-refresh interval, seconds (default 5)\n"
|
||||
" -i SEC Stats interval, seconds (default 5)\n"
|
||||
" -R MS Round-robin period for time-shared cpus, ms (default 200)\n"
|
||||
" -J MODE Fault injection (wrong-cid: dispatch to a cid not held)\n"
|
||||
" -v Print libbpf debug messages\n"
|
||||
|
|
@ -93,83 +88,6 @@ static void sigint_handler(int dummy)
|
|||
exit_req = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open a cgroup directory directly from its id. In cgroup2 the cgroup id is the
|
||||
* kernfs node id, so a FILEID_KERNFS handle built from the id resolves to the
|
||||
* directory via open_by_handle_at() against the cgroup mount.
|
||||
*/
|
||||
static int open_cgroup_by_id(u64 cgid)
|
||||
{
|
||||
static int mnt_fd = -1;
|
||||
struct {
|
||||
struct file_handle fh;
|
||||
u64 id;
|
||||
} h;
|
||||
|
||||
if (mnt_fd < 0) {
|
||||
mnt_fd = open("/sys/fs/cgroup", O_RDONLY | O_DIRECTORY);
|
||||
if (mnt_fd < 0)
|
||||
return -1;
|
||||
}
|
||||
h.fh.handle_bytes = sizeof(h.id);
|
||||
h.fh.handle_type = FILEID_KERNFS;
|
||||
h.id = cgid;
|
||||
return open_by_handle_at(mnt_fd, &h.fh, O_RDONLY | O_DIRECTORY);
|
||||
}
|
||||
|
||||
/* read a cgroup's cpu.weight (1-10000) by id, 0 if unavailable */
|
||||
static u32 read_cgroup_weight(u64 cgid)
|
||||
{
|
||||
char buf[32];
|
||||
int dfd, wfd;
|
||||
u32 w = 0;
|
||||
ssize_t n;
|
||||
|
||||
dfd = open_cgroup_by_id(cgid);
|
||||
if (dfd < 0)
|
||||
return 0;
|
||||
wfd = openat(dfd, "cpu.weight", O_RDONLY);
|
||||
close(dfd);
|
||||
if (wfd < 0)
|
||||
return 0;
|
||||
n = read(wfd, buf, sizeof(buf) - 1);
|
||||
close(wfd);
|
||||
if (n > 0) {
|
||||
buf[n] = '\0';
|
||||
w = strtoul(buf, NULL, 10);
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
/* read each direct child's cpu.weight into the arena, true if any changed */
|
||||
static bool feed_weights(struct qmap_arena *qa)
|
||||
{
|
||||
bool changed = false;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_SUB_SCHEDS; i++) {
|
||||
u64 cgid = qa->sub_sched_ctxs[i].cgroup_id;
|
||||
u32 w;
|
||||
|
||||
if (!cgid)
|
||||
continue;
|
||||
/* racy against slot reuse but weight is advisory and self-corrects */
|
||||
w = read_cgroup_weight(cgid);
|
||||
if (w && w != qa->sub_sched_ctxs[i].weight) {
|
||||
qa->sub_sched_ctxs[i].weight = w;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
static void invoke_repartition(struct scx_qmap *skel)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_test_run_opts, opts);
|
||||
|
||||
bpf_prog_test_run_opts(bpf_program__fd(skel->progs.repartition), &opts);
|
||||
}
|
||||
|
||||
static void invoke_flush_alloc(struct scx_qmap *skel)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_test_run_opts, opts);
|
||||
|
|
@ -281,7 +199,7 @@ static void print_hier(struct qmap_arena *qa, struct hier_prev *prev, u64 own_cg
|
|||
|
||||
format_cid_ranges(qa, CID_SELF, ranges, sizeof(ranges));
|
||||
printf("hier : %-4s %10llu %4u %6.2f %8s %s\n", "self",
|
||||
(unsigned long long)own_cgid, qa->self_weight,
|
||||
(unsigned long long)own_cgid, 100,
|
||||
secs > 0 ? (qa->self_alloc_ns - prev->self_alloc_ns) / (secs * 1e9) : 0.0,
|
||||
"-", ranges);
|
||||
prev->self_alloc_ns = qa->self_alloc_ns;
|
||||
|
|
@ -505,8 +423,6 @@ int main(int argc, char **argv)
|
|||
while (!exit_req && !UEI_EXITED(skel, uei)) {
|
||||
long nr_enqueued = qa->nr_enqueued;
|
||||
long nr_dispatched = qa->nr_dispatched;
|
||||
u32 self_weight;
|
||||
bool repart;
|
||||
|
||||
printf("---- %s ----\n",
|
||||
tstamp(tbuf, sizeof(tbuf)));
|
||||
|
|
@ -531,20 +447,6 @@ int main(int argc, char **argv)
|
|||
qa->cpuperf_target_avg,
|
||||
qa->cpuperf_target_max);
|
||||
|
||||
self_weight = own_cgid ? read_cgroup_weight(own_cgid) : 100;
|
||||
if (!self_weight)
|
||||
self_weight = 100;
|
||||
|
||||
repart = feed_weights(qa);
|
||||
|
||||
if (self_weight != qa->self_weight) {
|
||||
qa->self_weight = self_weight;
|
||||
repart = true;
|
||||
}
|
||||
|
||||
if (repart)
|
||||
invoke_repartition(skel);
|
||||
|
||||
invoke_flush_alloc(skel);
|
||||
print_hier(qa, &hprev, own_cgid);
|
||||
fflush(stdout);
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ struct task_ctx;
|
|||
/* per-direct-child state for the sub-scheduler */
|
||||
struct sub_sched_ctx {
|
||||
u64 cgroup_id;
|
||||
u32 weight; /* cpu.weight, fed by userspace */
|
||||
u32 weight; /* cpu.weight, seeded at attach, then set_weight */
|
||||
u64 nr_dsps;
|
||||
struct qmap_cmask granted_cids; /* cids granted excl to this child */
|
||||
struct qmap_cmask prev_granted; /* last grant, for delta calculation */
|
||||
|
|
@ -147,7 +147,6 @@ struct qmap_arena {
|
|||
|
||||
struct sub_sched_ctx sub_sched_ctxs[MAX_SUB_SCHEDS]; /* per-child context */
|
||||
u64 nr_sub_scheds; /* number of attached children */
|
||||
u32 self_weight; /* this node's cpu.weight, fed by userspace */
|
||||
|
||||
/* bpf-internal per-cid state */
|
||||
u8 cid_shared[SCX_QMAP_MAX_CPUS]; /* per cid: 1 if held shared (ENQ_IMMED-only) */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user