[PATCH v5 sched_ext/for-7.3 33/33] tools/sched_ext: scx_qmap - Add sub-sched cap fault injection
Tejun Heo <[email protected]>
| Newsgroups | dev.linux.lists.sched-ext,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Add a fault-injection mode to the scx_qmap sub-scheduler that deliberately dispatches one of its own tasks to a cid it does not hold. The kernel cap check must reject it and re-enqueue with SCX_TASK_REENQ_CAP, so the nr_inject_attempts counter tracks nr_reenq_cap one to one, exercising the delivery-time cap enforcement. Signed-off-by: Tejun Heo <[email protected]> --- tools/sched_ext/scx_qmap.bpf.c | 40 ++++++++++++++++++++++++++++++++++ tools/sched_ext/scx_qmap.c | 20 +++++++++++++---- tools/sched_ext/scx_qmap.h | 8 +++++++ 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/tools/sched_ext/scx_qmap.bpf.c b/tools/sched_ext/scx_qmap.bpf.c index 49840c49d1fa..66c53182cb02 100644 --- a/tools/sched_ext/scx_qmap.bpf.c +++ b/tools/sched_ext/scx_qmap.bpf.c @@ -378,6 +378,24 @@ static u64 needs_immed(s32 cid) return qa.cid_shared[cid] ? SCX_ENQ_IMMED : 0; } +/* first cid this node does NOT hold for fault injection, -1 if none */ +static s32 first_unavail_cid(void) +{ + s32 nr_cids = qa.nr_cids, c; + + if (nr_cids > SCX_QMAP_MAX_CPUS) { + scx_bpf_error("-ERANGE"); + return -1; + } + + bpf_for(c, 0, nr_cids) { + if (!cmask_test(c, &qa.held_excl.mask) && + !cmask_test(c, &qa.held_shared.mask)) + return c; + } + return -1; +} + static int weight_to_idx(u32 weight) { /* Coarsely map the compound weight to a FIFO. */ @@ -454,6 +472,28 @@ void BPF_STRUCT_OPS(qmap_enqueue, struct task_struct *p, u64 enq_flags) } } + /* + * Fault injection: deliberately dispatch one of our own tasks to a cid + * we don't hold. The kernel cap check must reject it and re-enqueue + * with SCX_TASK_REENQ_CAP, so nr_inject_attempts tracks nr_reenq_cap + * and proves delivery-time enforcement. Throttled. + */ + if (qa.inject_mode == QMAP_INJ_WRONG_CID && p->nr_cpus_allowed > 1 && + !(enq_flags & SCX_ENQ_REENQ)) { + static u32 inj_cnt; + + if (!(++inj_cnt % 64)) { + s32 bad = first_unavail_cid(); + + if (bad >= 0 && cmask_test(bad, &taskc->cpus_allowed)) { + __sync_fetch_and_add(&qa.nr_inject_attempts, 1); + scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL_ON | bad, + slice_ns, enq_flags); + return; + } + } + } + /* * IMMED stress testing: Every immed_stress_nth'th enqueue, dispatch * directly to prev_cpu's local DSQ even when busy to force dsq->nr > 1 diff --git a/tools/sched_ext/scx_qmap.c b/tools/sched_ext/scx_qmap.c index 8249229f25bd..dda3ddf5b749 100644 --- a/tools/sched_ext/scx_qmap.c +++ b/tools/sched_ext/scx_qmap.c @@ -52,7 +52,7 @@ const char help_fmt[] = "\n" "Usage: %s [-s SLICE_US] [-e COUNT] [-t COUNT] [-T COUNT] [-l COUNT] [-b COUNT]\n" " [-N COUNT] [-P] [-M] [-H] [-c CG_PATH] [-d PID] [-D LEN] [-S] [-p] [-I]\n" -" [-F COUNT] [-i SEC] [-R MS] [-v]\n" +" [-F COUNT] [-i SEC] [-R MS] [-J MODE] [-v]\n" "\n" " -s SLICE_US Override slice duration\n" " -e COUNT Trigger scx_bpf_error() after COUNT enqueues\n" @@ -74,6 +74,7 @@ const char help_fmt[] = " -C MODE cid-override test (shuffle|bad-dup|bad-range|bad-mono)\n" " -i SEC Stats and weight-refresh 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" " -h Display this help and exit\n"; @@ -184,6 +185,7 @@ struct hier_prev { u64 nr_dsps[MAX_SUB_SCHEDS]; u64 nr_reenq_cap; u64 nr_reenq_immed; + u64 nr_inject_attempts; }; /* current wall-clock time as "HH:MM:SS" for the startup and interval headers */ @@ -265,12 +267,14 @@ static void print_hier(struct qmap_arena *qa, struct hier_prev *prev, u64 own_cg } format_cid_ranges(qa, CID_SHARED, ranges, sizeof(ranges)); - printf("hier : nsub=%llu excl=%u shared=%s rr=%s reenq cap/immed +%llu/+%llu\n", + printf("hier : nsub=%llu excl=%u shared=%s rr=%s reenq cap/immed +%llu/+%llu inj=+%llu\n", (unsigned long long)qa->nr_sub_scheds, qa->part.nr_excl, ranges, rr, (unsigned long long)(qa->nr_reenq_cap - prev->nr_reenq_cap), - (unsigned long long)(qa->nr_reenq_immed - prev->nr_reenq_immed)); + (unsigned long long)(qa->nr_reenq_immed - prev->nr_reenq_immed), + (unsigned long long)(qa->nr_inject_attempts - prev->nr_inject_attempts)); prev->nr_reenq_cap = qa->nr_reenq_cap; prev->nr_reenq_immed = qa->nr_reenq_immed; + prev->nr_inject_attempts = qa->nr_inject_attempts; printf("hier : %-4s %10s %4s %6s %8s %s\n", "", "cgroup", "w", "alloc", "disp/s", "cids"); @@ -311,6 +315,7 @@ int main(int argc, char **argv) struct hier_prev hprev = {}; const char *sub_cg_path = NULL; char tbuf[32]; + u32 inject_mode = 0; u64 own_cgid = 0; libbpf_set_print(libbpf_print_fn); @@ -331,7 +336,7 @@ int main(int argc, char **argv) skel->rodata->slice_ns = __COMPAT_ENUM_OR_ZERO("scx_public_consts", "SCX_SLICE_DFL"); skel->rodata->max_tasks = 16384; - while ((opt = getopt(argc, argv, "s:e:t:T:l:b:N:PMHc:d:D:SpIF:C:i:R:vh")) != -1) { + while ((opt = getopt(argc, argv, "s:e:t:T:l:b:N:PMHc:d:D:SpIF:C:i:R:J:vh")) != -1) { switch (opt) { case 's': skel->rodata->slice_ns = strtoull(optarg, NULL, 0) * 1000; @@ -465,6 +470,12 @@ int main(int argc, char **argv) if (round_robin_ms < 10) round_robin_ms = 10; break; + case 'J': + if (!strcmp(optarg, "wrong-cid")) + inject_mode = QMAP_INJ_WRONG_CID; + else + inject_mode = strtoul(optarg, NULL, 0); + break; case 'v': verbose = true; break; @@ -481,6 +492,7 @@ int main(int argc, char **argv) qa = &skel->arena->qa; qa->test_error_cnt = test_error_cnt; + qa->inject_mode = inject_mode; if (sub_cg_path) printf("%s scx_qmap started: sub-scheduler on %s, stats every %ds\n", diff --git a/tools/sched_ext/scx_qmap.h b/tools/sched_ext/scx_qmap.h index 5d44c11c75dd..87642d21fce3 100644 --- a/tools/sched_ext/scx_qmap.h +++ b/tools/sched_ext/scx_qmap.h @@ -60,6 +60,12 @@ struct qmap_fifo { s32 idx; }; +/* -J fault-injection modes. Selects inject_mode in struct qmap_arena. */ +enum qmap_inject { + QMAP_INJ_OFF = 0, + QMAP_INJ_WRONG_CID = 1, /* dispatch to a cid we don't hold */ +}; + /* * scx_cmask's are embedded in struct qmap_arena with inline backing storage. * The bpf side uses &field.mask with the normal cmask_* helpers. Userspace @@ -167,6 +173,8 @@ struct qmap_arena { /* bpf -> userspace: stats */ u64 nr_reenq_cap; /* SCX_TASK_REENQ_CAP bounces */ u64 nr_reenq_immed; /* SCX_TASK_REENQ_IMMED bounces */ + u64 nr_inject_attempts; /* fault-injection: dispatches to an unheld cid */ + u32 inject_mode; /* fault-injection mode (QMAP_INJ_*) */ }; #endif /* __SCX_QMAP_H */ -- 2.55.0