[PATCH 8/9] sched_ext: scx_qmap: Add proxy execution support
From: Andrea Righi
Date: Mon Jul 06 2026 - 03:33:36 EST
Add a -B option to opt scx_qmap into queueing mutex-blocked tasks for
proxy execution. Without the option, SCX_OPS_ENQ_BLOCKED remains clear
and mutex waiters block normally. With -B, set SCX_OPS_ENQ_BLOCKED and
enable the corresponding BPF-side blocked-donor admission policy.
When qmap_enqueue() receives SCX_ENQ_BLOCKED for a donor with runtime
budget remaining, insert it directly into the local DSQ of its current
cid without refilling its slice. This lets proxy execution continue
using the donor's existing budget.
Once the donor exhausts its slice, let it proceed through qmap's normal
enqueue path. This applies the same placement and slice refill decisions
as other enqueues instead of imposing a proxy-specific policy.
Signed-off-by: Andrea Righi <arighi@xxxxxxxxxx>
---
tools/sched_ext/scx_qmap.bpf.c | 13 +++++++++++++
tools/sched_ext/scx_qmap.c | 9 +++++++--
2 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/tools/sched_ext/scx_qmap.bpf.c b/tools/sched_ext/scx_qmap.bpf.c
index fd9a82a676278..2ba3e2a4f625f 100644
--- a/tools/sched_ext/scx_qmap.bpf.c
+++ b/tools/sched_ext/scx_qmap.bpf.c
@@ -49,6 +49,7 @@ const volatile u64 sub_cgroup_id;
const volatile s32 disallow_tgid;
const volatile bool suppress_dump;
const volatile bool always_enq_immed;
+const volatile bool enq_blocked;
const volatile u32 immed_stress_nth;
const volatile u32 max_tasks;
@@ -396,6 +397,18 @@ void BPF_STRUCT_OPS(qmap_enqueue, struct task_struct *p, u64 enq_flags)
*/
taskc->core_sched_seq = qa.core_sched_tail_seqs[idx]++;
+ /*
+ * Admit a blocked donor with runtime budget left by inserting it into
+ * its current cid's local DSQ, reusing its remaining slice. If the
+ * slice is exhausted, fall through the normal enqueue path.
+ */
+ if (enq_blocked &&
+ (enq_flags & SCX_ENQ_BLOCKED) && p->scx.slice) {
+ scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL_ON | scx_bpf_task_cid(p),
+ 0, 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 67ddd483a4c75..236a9c9bb13f6 100644
--- a/tools/sched_ext/scx_qmap.c
+++ b/tools/sched_ext/scx_qmap.c
@@ -23,7 +23,7 @@ const char help_fmt[] =
"See the top-level comment in .bpf.c for more details.\n"
"\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"
+" [-N COUNT] [-P] [-M] [-H] [-c CG_PATH] [-d PID] [-D LEN] [-S] [-p] [-I] [-B]\n"
" [-F COUNT] [-v]\n"
"\n"
" -s SLICE_US Override slice duration\n"
@@ -42,6 +42,7 @@ const char help_fmt[] =
" -S Suppress qmap-specific debug dump\n"
" -p Switch only tasks on SCHED_EXT policy instead of all\n"
" -I Turn on SCX_OPS_ALWAYS_ENQ_IMMED\n"
+" -B Turn on SCX_OPS_ENQ_BLOCKED\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)\n"
" -v Print libbpf debug messages\n"
@@ -89,7 +90,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:vh")) != -1) {
+ while ((opt = getopt(argc, argv, "s:e:t:T:l:b:N:PMHc:d:D:SpIBF:C:vh")) != -1) {
switch (opt) {
case 's':
skel->rodata->slice_ns = strtoull(optarg, NULL, 0) * 1000;
@@ -149,6 +150,10 @@ int main(int argc, char **argv)
skel->rodata->always_enq_immed = true;
skel->struct_ops.qmap_ops->flags |= SCX_OPS_ALWAYS_ENQ_IMMED;
break;
+ case 'B':
+ skel->rodata->enq_blocked = true;
+ skel->struct_ops.qmap_ops->flags |= SCX_OPS_ENQ_BLOCKED;
+ break;
case 'F':
skel->rodata->immed_stress_nth = strtoul(optarg, NULL, 0);
break;
--
2.55.0