From: Tejun Heo <tj@kernel.org>
To: David Vernet <void@manifault.com>,
Andrea Righi <arighi@nvidia.com>,
Changwoo Min <changwoo@igalia.com>
Cc: Kumar Kartikeya Dwivedi <memxor@gmail.com>,
sched-ext@lists.linux.dev, Emil Tsalapatis <emil@etsalapatis.com>,
linux-kernel@vger.kernel.org, Tejun Heo <tj@kernel.org>
Subject: [PATCH 3/3] sched_ext: Convert scx_bpf_cid_override() to __arena array arguments
Date: Wed, 12 Aug 2026 11:16:09 -1000 [thread overview]
Message-ID: <20260812211609.2495583-4-tj@kernel.org> (raw)
In-Reply-To: <20260812211609.2495583-1-tj@kernel.org>
scx_bpf_cid_override() predates the cid-form arena transition and takes its
arrays as verifier-checked mem+size buffers, forcing scx_qmap to keep the
cpu_to_cid and shard_start arrays in writable bss while the rest of its
state lives in the arena. Unify on arena arguments before cid-form
schedulers start seeing real use.
BPF now translates between BPF and kernel arena addresses for __arena
arguments. Take the arrays as __arena arguments, with the counts passed in
entries. The counts now size the snapshot copies and are bounds-checked
before them.
scx_qmap moves the arrays into struct qmap_arena. As the arena is mmapped at
load, the loader populates them between load and attach instead of before
load.
The arena argument address translation is currently implemented only on
x86-64. Schedulers calling this kfunc load only there for now.
Signed-off-by: Tejun Heo <tj@kernel.org>
---
kernel/sched/ext/cid.c | 49 +++++++++--------
tools/sched_ext/include/scx/compat.bpf.h | 14 ++---
tools/sched_ext/scx_qmap.bpf.c | 16 ++----
tools/sched_ext/scx_qmap.c | 68 ++++++++++++++----------
tools/sched_ext/scx_qmap.h | 4 ++
5 files changed, 84 insertions(+), 67 deletions(-)
diff --git a/kernel/sched/ext/cid.c b/kernel/sched/ext/cid.c
index 11fe9be80f1e..39f88deb94bc 100644
--- a/kernel/sched/ext/cid.c
+++ b/kernel/sched/ext/cid.c
@@ -449,10 +449,10 @@ __bpf_kfunc_start_defs();
/**
* scx_bpf_cid_override - Install an explicit cpu->cid mapping with shard info
- * @cpu_to_cid_src: array of nr_cpu_ids s32 entries (cid for each cpu)
- * @cpu_to_cid_src__sz: must be nr_cpu_ids * sizeof(s32) bytes
- * @shard_start_src: array of first-cid-of-each-shard, strictly increasing from 0
- * @shard_start_src__sz: nr_shards * sizeof(s32) bytes
+ * @cpu_to_cid__arena: array of nr_cpu_ids s32 entries (cid for each cpu)
+ * @cpu_to_cid_cnt: number of entries, must be nr_cpu_ids
+ * @shard_start__arena: array of first-cid-of-each-shard, one entry per shard
+ * @shard_start_cnt: number of shards
* @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs
*
* May only be called from ops.init_cids() of the root scheduler. Replace the
@@ -464,9 +464,9 @@ __bpf_kfunc_start_defs();
* (core/LLC/node) is cleared and the shard layout is set from the input. On
* invalid input, abort the scheduler.
*/
-__bpf_kfunc void scx_bpf_cid_override(const s32 *cpu_to_cid_src, u32 cpu_to_cid_src__sz,
- const s32 *shard_start_src, u32 shard_start_src__sz,
- const struct bpf_prog_aux *aux)
+__bpf_kfunc void scx_bpf_cid_override(const s32 *cpu_to_cid__arena, u32 cpu_to_cid_cnt,
+ const s32 *shard_start__arena, u32 shard_start_cnt,
+ const struct bpf_prog_aux *aux)
{
cpumask_var_t seen __free(free_cpumask_var) = CPUMASK_VAR_NULL;
u32 *node_counts __free(kfree) = NULL;
@@ -475,19 +475,28 @@ __bpf_kfunc void scx_bpf_cid_override(const s32 *cpu_to_cid_src, u32 cpu_to_cid_
u32 npossible = num_possible_cpus();
struct scx_cid_tables *tbls;
struct scx_sched *sch;
- u32 nr_shards;
+ u32 nr_shards = shard_start_cnt;
bool alloced;
s32 cpu, cid, si;
/*
* GFP_KERNEL allocs must happen before the rcu read section. Snapshot
- * the BPF-supplied arrays so a concurrent map mutation can't change
+ * the BPF-supplied arrays so a concurrent arena write can't change
* them between validation and use.
+ *
+ * The BPF-supplied counts size the snapshots and thus the arena reads.
+ * Gate the copies on the count bounds, reported below once @sch is
+ * available. The bounded reads, at most 32KB, stay within the guard
+ * region that arena fault recovery covers.
*/
alloced = zalloc_cpumask_var(&seen, GFP_KERNEL);
node_counts = kcalloc(nr_node_ids, sizeof(*node_counts), GFP_KERNEL);
- cpu_to_cid = kmemdup(cpu_to_cid_src, cpu_to_cid_src__sz, GFP_KERNEL);
- shard_start = kmemdup(shard_start_src, shard_start_src__sz, GFP_KERNEL);
+ if (cpu_to_cid_cnt == nr_cpu_ids)
+ cpu_to_cid = kmemdup(cpu_to_cid__arena, cpu_to_cid_cnt * sizeof(s32),
+ GFP_KERNEL);
+ if (nr_shards && nr_shards <= npossible)
+ shard_start = kmemdup(shard_start__arena, nr_shards * sizeof(s32),
+ GFP_KERNEL);
guard(rcu)();
@@ -499,25 +508,23 @@ __bpf_kfunc void scx_bpf_cid_override(const s32 *cpu_to_cid_src, u32 cpu_to_cid_
lockdep_assert_held(&scx_enable_mutex);
tbls = scx_cid_tables;
- if (!alloced || !node_counts || !cpu_to_cid || !shard_start) {
- scx_error(sch, "scx_bpf_cid_override: allocation failed");
+ if (cpu_to_cid_cnt != nr_cpu_ids) {
+ scx_error(sch, "scx_bpf_cid_override: cpu_to_cid expected %u entries, got %u",
+ nr_cpu_ids, cpu_to_cid_cnt);
return;
}
- if (cpu_to_cid_src__sz != nr_cpu_ids * sizeof(s32)) {
- scx_error(sch, "scx_bpf_cid_override: cpu_to_cid expected %zu bytes, got %u",
- nr_cpu_ids * sizeof(s32), cpu_to_cid_src__sz);
+ if (!nr_shards || nr_shards > npossible) {
+ scx_error(sch, "scx_bpf_cid_override: invalid shard_start count %u",
+ nr_shards);
return;
}
- if (!shard_start_src__sz || shard_start_src__sz % sizeof(s32)) {
- scx_error(sch, "scx_bpf_cid_override: invalid shard_start size %u",
- shard_start_src__sz);
+ if (!alloced || !node_counts || !cpu_to_cid || !shard_start) {
+ scx_error(sch, "scx_bpf_cid_override: allocation failed");
return;
}
- nr_shards = shard_start_src__sz / sizeof(s32);
-
/* validate shard_start[]: starts at 0, strictly increasing, in range */
if (shard_start[0] != 0) {
scx_error(sch, "scx_bpf_cid_override: shard_start[0] must be 0, got %d",
diff --git a/tools/sched_ext/include/scx/compat.bpf.h b/tools/sched_ext/include/scx/compat.bpf.h
index 1b45655a64a3..7c735eb09c07 100644
--- a/tools/sched_ext/include/scx/compat.bpf.h
+++ b/tools/sched_ext/include/scx/compat.bpf.h
@@ -125,15 +125,17 @@ static inline bool scx_bpf_sub_dispatch(u64 cgroup_id)
* v7.3: scx_bpf_cid_override() for explicit cid and shard mapping. Ignore if
* missing.
*/
-void scx_bpf_cid_override___compat(const s32 *cpu_to_cid, u32 cpu_to_cid__sz,
- const s32 *shard_start, u32 shard_start__sz) __ksym __weak;
+void scx_bpf_cid_override___compat(const s32 __arena *cpu_to_cid__arena,
+ u32 cpu_to_cid_cnt,
+ const s32 __arena *shard_start__arena,
+ u32 shard_start_cnt) __ksym __weak;
-static inline void scx_bpf_cid_override(const s32 *cpu_to_cid, u32 cpu_to_cid__sz,
- const s32 *shard_start, u32 shard_start__sz)
+static inline void scx_bpf_cid_override(const s32 __arena *cpu_to_cid, u32 cpu_to_cid_cnt,
+ const s32 __arena *shard_start, u32 shard_start_cnt)
{
if (bpf_ksym_exists(scx_bpf_cid_override___compat))
- scx_bpf_cid_override___compat(cpu_to_cid, cpu_to_cid__sz,
- shard_start, shard_start__sz);
+ scx_bpf_cid_override___compat(cpu_to_cid, cpu_to_cid_cnt,
+ shard_start, shard_start_cnt);
}
/**
diff --git a/tools/sched_ext/scx_qmap.bpf.c b/tools/sched_ext/scx_qmap.bpf.c
index 61dc748742a2..d9ca9106a5ce 100644
--- a/tools/sched_ext/scx_qmap.bpf.c
+++ b/tools/sched_ext/scx_qmap.bpf.c
@@ -80,14 +80,6 @@ const volatile u64 round_robin_ns;
*/
const volatile u32 cid_override_mode;
const volatile u32 cid_override_nr_shards;
-/*
- * Arrays live in bss (writable) because scx_bpf_cid_override()'s BPF
- * verifier signature treats its len-paired pointers as read/write - rodata
- * fails verification with "write into map forbidden". Userspace populates
- * them before SCX_OPS_LOAD, same as rodata, and nothing writes them after.
- */
-s32 cid_override_cpu_to_cid[SCX_QMAP_MAX_CPUS];
-s32 cid_override_shard_start[SCX_QMAP_MAX_CPUS];
UEI_DEFINE(uei);
@@ -1761,17 +1753,15 @@ s32 BPF_STRUCT_OPS_SLEEPABLE(qmap_init_cids)
if (!cid_override_mode)
return 0;
- /* bound the count so the verifier accepts cpu_to_cid's mem/len pair */
+ /* the arena arrays are sized SCX_QMAP_MAX_CPUS */
if (nr_cpu_ids > SCX_QMAP_MAX_CPUS) {
scx_bpf_error("nr_cpu_ids=%u exceeds SCX_QMAP_MAX_CPUS=%d",
nr_cpu_ids, SCX_QMAP_MAX_CPUS);
return -EINVAL;
}
- scx_bpf_cid_override((const s32 *)cid_override_cpu_to_cid,
- nr_cpu_ids * sizeof(s32),
- (const s32 *)cid_override_shard_start,
- cid_override_nr_shards * sizeof(s32));
+ scx_bpf_cid_override(qa.cid_override_cpu_to_cid, nr_cpu_ids,
+ qa.cid_override_shard_start, cid_override_nr_shards);
return 0;
}
diff --git a/tools/sched_ext/scx_qmap.c b/tools/sched_ext/scx_qmap.c
index 988b6931633e..5bb5f687e579 100644
--- a/tools/sched_ext/scx_qmap.c
+++ b/tools/sched_ext/scx_qmap.c
@@ -242,6 +242,7 @@ int main(int argc, char **argv)
char tbuf[32];
u32 inject_mode = 0;
u64 own_cgid = 0;
+ s32 cid_override_shard_sz = 4;
libbpf_set_print(libbpf_print_fn);
signal(SIGINT, sigint_handler);
@@ -328,8 +329,7 @@ int main(int argc, char **argv)
break;
case 'C': {
u32 nr_cpus = libbpf_num_possible_cpus();
- u32 mode, i;
- s32 shard_sz = 4;
+ u32 mode;
if (!strcmp(optarg, "shuffle"))
mode = QMAP_CID_OVR_SHUFFLE;
@@ -344,18 +344,7 @@ int main(int argc, char **argv)
return 1;
}
skel->rodata->cid_override_mode = mode;
-
- /* shuffle: reversed cpu_to_cid; others: identity */
- for (i = 0; i < nr_cpus; i++) {
- if (mode == QMAP_CID_OVR_SHUFFLE)
- skel->bss->cid_override_cpu_to_cid[i] = nr_cpus - 1 - i;
- else
- skel->bss->cid_override_cpu_to_cid[i] = i;
- }
- if (mode == QMAP_CID_OVR_BAD_DUP && nr_cpus >= 2)
- skel->bss->cid_override_cpu_to_cid[1] = 0;
- if (mode == QMAP_CID_OVR_BAD_RANGE)
- skel->bss->cid_override_cpu_to_cid[0] = (s32)nr_cpus;
+ cid_override_shard_sz = 4;
/*
* bad-mono needs >= 3 shards to build a 0-based but
@@ -368,21 +357,12 @@ int main(int argc, char **argv)
nr_cpus);
return 1;
}
- shard_sz = nr_cpus / 3;
+ cid_override_shard_sz = nr_cpus / 3;
}
/* shards of shard_sz each */
- skel->rodata->cid_override_nr_shards = (nr_cpus + shard_sz - 1) / shard_sz;
- for (i = 0; i < skel->rodata->cid_override_nr_shards; i++)
- skel->bss->cid_override_shard_start[i] = i * shard_sz;
-
- if (mode == QMAP_CID_OVR_BAD_MONO) {
- /* swap [1] and [2] to break monotonicity */
- s32 tmp = skel->bss->cid_override_shard_start[1];
- skel->bss->cid_override_shard_start[1] =
- skel->bss->cid_override_shard_start[2];
- skel->bss->cid_override_shard_start[2] = tmp;
- }
+ skel->rodata->cid_override_nr_shards =
+ (nr_cpus + cid_override_shard_sz - 1) / cid_override_shard_sz;
break;
}
case 'i':
@@ -428,9 +408,43 @@ int main(int argc, char **argv)
skel->rodata->round_robin_ns = (u64)round_robin_ms * 1000000;
SCX_OPS_LOAD(skel, qmap_ops, scx_qmap, uei);
- link = SCX_OPS_ATTACH(skel, qmap_ops, scx_qmap);
qa = &skel->arena->qa;
+
+ /*
+ * The cid-override arrays live in the arena, which is mmapped at load.
+ * Populate them before qmap_init_cids() consumes them at attach.
+ */
+ if (skel->rodata->cid_override_mode) {
+ u32 mode = skel->rodata->cid_override_mode;
+ u32 nr_cpus = libbpf_num_possible_cpus();
+ u32 i;
+
+ /* shuffle: reversed cpu_to_cid; others: identity */
+ for (i = 0; i < nr_cpus; i++) {
+ if (mode == QMAP_CID_OVR_SHUFFLE)
+ qa->cid_override_cpu_to_cid[i] = nr_cpus - 1 - i;
+ else
+ qa->cid_override_cpu_to_cid[i] = i;
+ }
+ if (mode == QMAP_CID_OVR_BAD_DUP && nr_cpus >= 2)
+ qa->cid_override_cpu_to_cid[1] = 0;
+ if (mode == QMAP_CID_OVR_BAD_RANGE)
+ qa->cid_override_cpu_to_cid[0] = (s32)nr_cpus;
+
+ for (i = 0; i < skel->rodata->cid_override_nr_shards; i++)
+ qa->cid_override_shard_start[i] = i * cid_override_shard_sz;
+
+ if (mode == QMAP_CID_OVR_BAD_MONO) {
+ /* swap [1] and [2] to break monotonicity */
+ s32 tmp = qa->cid_override_shard_start[1];
+ qa->cid_override_shard_start[1] = qa->cid_override_shard_start[2];
+ qa->cid_override_shard_start[2] = tmp;
+ }
+ }
+
+ link = SCX_OPS_ATTACH(skel, qmap_ops, scx_qmap);
+
qa->test_error_cnt = test_error_cnt;
qa->inject_mode = inject_mode;
diff --git a/tools/sched_ext/scx_qmap.h b/tools/sched_ext/scx_qmap.h
index c8f602d58ca3..c78d61806b39 100644
--- a/tools/sched_ext/scx_qmap.h
+++ b/tools/sched_ext/scx_qmap.h
@@ -131,6 +131,10 @@ struct qmap_arena {
struct cpu_ctx cpu_ctxs[SCX_QMAP_MAX_CPUS];
+ /* cid-override test input, populated by the loader before attach */
+ __s32 cid_override_cpu_to_cid[SCX_QMAP_MAX_CPUS];
+ __s32 cid_override_shard_start[SCX_QMAP_MAX_CPUS];
+
/* task_ctx slab; allocated and threaded by qmap_init() */
struct task_ctx __arena *task_ctxs;
struct task_ctx __arena *task_free_head;
--
2.55.0
prev parent reply other threads:[~2026-08-12 21:16 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-12 21:16 [PATCHSET sched_ext/for-7.3-arena-args] sched_ext: Convert arena-pointer interfaces to __arena arguments Tejun Heo
2026-08-12 21:16 ` [PATCH 1/3] sched_ext: Pass kernel arena pointers to ops_cid callbacks Tejun Heo
2026-08-12 21:16 ` [PATCH 2/3] sched_ext: Convert sub-cap kfuncs to __arena cmask arguments Tejun Heo
2026-08-12 21:16 ` Tejun Heo [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260812211609.2495583-4-tj@kernel.org \
--to=tj@kernel.org \
--cc=arighi@nvidia.com \
--cc=changwoo@igalia.com \
--cc=emil@etsalapatis.com \
--cc=linux-kernel@vger.kernel.org \
--cc=memxor@gmail.com \
--cc=sched-ext@lists.linux.dev \
--cc=void@manifault.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.