mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 08:15:07 -04:00
sched_ext: Convert scx_bpf_cid_override() to __arena array arguments
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>
This commit is contained in:
@@ -449,10 +449,10 @@ __bpf_kfunc_start_defs();
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* scx_bpf_cid_override - Install an explicit cpu->cid mapping with shard info
|
* 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__arena: array of nr_cpu_ids s32 entries (cid for each cpu)
|
||||||
* @cpu_to_cid_src__sz: must be nr_cpu_ids * sizeof(s32) bytes
|
* @cpu_to_cid_cnt: number of entries, must be nr_cpu_ids
|
||||||
* @shard_start_src: array of first-cid-of-each-shard, strictly increasing from 0
|
* @shard_start__arena: array of first-cid-of-each-shard, one entry per shard
|
||||||
* @shard_start_src__sz: nr_shards * sizeof(s32) bytes
|
* @shard_start_cnt: number of shards
|
||||||
* @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs
|
* @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
|
* 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
|
* (core/LLC/node) is cleared and the shard layout is set from the input. On
|
||||||
* invalid input, abort the scheduler.
|
* invalid input, abort the scheduler.
|
||||||
*/
|
*/
|
||||||
__bpf_kfunc void scx_bpf_cid_override(const s32 *cpu_to_cid_src, u32 cpu_to_cid_src__sz,
|
__bpf_kfunc void scx_bpf_cid_override(const s32 *cpu_to_cid__arena, u32 cpu_to_cid_cnt,
|
||||||
const s32 *shard_start_src, u32 shard_start_src__sz,
|
const s32 *shard_start__arena, u32 shard_start_cnt,
|
||||||
const struct bpf_prog_aux *aux)
|
const struct bpf_prog_aux *aux)
|
||||||
{
|
{
|
||||||
cpumask_var_t seen __free(free_cpumask_var) = CPUMASK_VAR_NULL;
|
cpumask_var_t seen __free(free_cpumask_var) = CPUMASK_VAR_NULL;
|
||||||
u32 *node_counts __free(kfree) = 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();
|
u32 npossible = num_possible_cpus();
|
||||||
struct scx_cid_tables *tbls;
|
struct scx_cid_tables *tbls;
|
||||||
struct scx_sched *sch;
|
struct scx_sched *sch;
|
||||||
u32 nr_shards;
|
u32 nr_shards = shard_start_cnt;
|
||||||
bool alloced;
|
bool alloced;
|
||||||
s32 cpu, cid, si;
|
s32 cpu, cid, si;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GFP_KERNEL allocs must happen before the rcu read section. Snapshot
|
* 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.
|
* 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);
|
alloced = zalloc_cpumask_var(&seen, GFP_KERNEL);
|
||||||
node_counts = kcalloc(nr_node_ids, sizeof(*node_counts), 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);
|
if (cpu_to_cid_cnt == nr_cpu_ids)
|
||||||
shard_start = kmemdup(shard_start_src, shard_start_src__sz, GFP_KERNEL);
|
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)();
|
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);
|
lockdep_assert_held(&scx_enable_mutex);
|
||||||
tbls = scx_cid_tables;
|
tbls = scx_cid_tables;
|
||||||
|
|
||||||
|
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 (!nr_shards || nr_shards > npossible) {
|
||||||
|
scx_error(sch, "scx_bpf_cid_override: invalid shard_start count %u",
|
||||||
|
nr_shards);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!alloced || !node_counts || !cpu_to_cid || !shard_start) {
|
if (!alloced || !node_counts || !cpu_to_cid || !shard_start) {
|
||||||
scx_error(sch, "scx_bpf_cid_override: allocation failed");
|
scx_error(sch, "scx_bpf_cid_override: allocation failed");
|
||||||
return;
|
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);
|
|
||||||
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);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nr_shards = shard_start_src__sz / sizeof(s32);
|
|
||||||
|
|
||||||
/* validate shard_start[]: starts at 0, strictly increasing, in range */
|
/* validate shard_start[]: starts at 0, strictly increasing, in range */
|
||||||
if (shard_start[0] != 0) {
|
if (shard_start[0] != 0) {
|
||||||
scx_error(sch, "scx_bpf_cid_override: shard_start[0] must be 0, got %d",
|
scx_error(sch, "scx_bpf_cid_override: shard_start[0] must be 0, got %d",
|
||||||
|
|||||||
@@ -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
|
* v7.3: scx_bpf_cid_override() for explicit cid and shard mapping. Ignore if
|
||||||
* missing.
|
* missing.
|
||||||
*/
|
*/
|
||||||
void scx_bpf_cid_override___compat(const s32 *cpu_to_cid, u32 cpu_to_cid__sz,
|
void scx_bpf_cid_override___compat(const s32 __arena *cpu_to_cid__arena,
|
||||||
const s32 *shard_start, u32 shard_start__sz) __ksym __weak;
|
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,
|
static inline void scx_bpf_cid_override(const s32 __arena *cpu_to_cid, u32 cpu_to_cid_cnt,
|
||||||
const s32 *shard_start, u32 shard_start__sz)
|
const s32 __arena *shard_start, u32 shard_start_cnt)
|
||||||
{
|
{
|
||||||
if (bpf_ksym_exists(scx_bpf_cid_override___compat))
|
if (bpf_ksym_exists(scx_bpf_cid_override___compat))
|
||||||
scx_bpf_cid_override___compat(cpu_to_cid, cpu_to_cid__sz,
|
scx_bpf_cid_override___compat(cpu_to_cid, cpu_to_cid_cnt,
|
||||||
shard_start, shard_start__sz);
|
shard_start, shard_start_cnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -80,14 +80,6 @@ const volatile u64 round_robin_ns;
|
|||||||
*/
|
*/
|
||||||
const volatile u32 cid_override_mode;
|
const volatile u32 cid_override_mode;
|
||||||
const volatile u32 cid_override_nr_shards;
|
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);
|
UEI_DEFINE(uei);
|
||||||
|
|
||||||
@@ -1761,17 +1753,15 @@ s32 BPF_STRUCT_OPS_SLEEPABLE(qmap_init_cids)
|
|||||||
if (!cid_override_mode)
|
if (!cid_override_mode)
|
||||||
return 0;
|
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) {
|
if (nr_cpu_ids > SCX_QMAP_MAX_CPUS) {
|
||||||
scx_bpf_error("nr_cpu_ids=%u exceeds SCX_QMAP_MAX_CPUS=%d",
|
scx_bpf_error("nr_cpu_ids=%u exceeds SCX_QMAP_MAX_CPUS=%d",
|
||||||
nr_cpu_ids, SCX_QMAP_MAX_CPUS);
|
nr_cpu_ids, SCX_QMAP_MAX_CPUS);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
scx_bpf_cid_override((const s32 *)cid_override_cpu_to_cid,
|
scx_bpf_cid_override(qa.cid_override_cpu_to_cid, nr_cpu_ids,
|
||||||
nr_cpu_ids * sizeof(s32),
|
qa.cid_override_shard_start, cid_override_nr_shards);
|
||||||
(const s32 *)cid_override_shard_start,
|
|
||||||
cid_override_nr_shards * sizeof(s32));
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -242,6 +242,7 @@ int main(int argc, char **argv)
|
|||||||
char tbuf[32];
|
char tbuf[32];
|
||||||
u32 inject_mode = 0;
|
u32 inject_mode = 0;
|
||||||
u64 own_cgid = 0;
|
u64 own_cgid = 0;
|
||||||
|
s32 cid_override_shard_sz = 4;
|
||||||
|
|
||||||
libbpf_set_print(libbpf_print_fn);
|
libbpf_set_print(libbpf_print_fn);
|
||||||
signal(SIGINT, sigint_handler);
|
signal(SIGINT, sigint_handler);
|
||||||
@@ -328,8 +329,7 @@ int main(int argc, char **argv)
|
|||||||
break;
|
break;
|
||||||
case 'C': {
|
case 'C': {
|
||||||
u32 nr_cpus = libbpf_num_possible_cpus();
|
u32 nr_cpus = libbpf_num_possible_cpus();
|
||||||
u32 mode, i;
|
u32 mode;
|
||||||
s32 shard_sz = 4;
|
|
||||||
|
|
||||||
if (!strcmp(optarg, "shuffle"))
|
if (!strcmp(optarg, "shuffle"))
|
||||||
mode = QMAP_CID_OVR_SHUFFLE;
|
mode = QMAP_CID_OVR_SHUFFLE;
|
||||||
@@ -344,18 +344,7 @@ int main(int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
skel->rodata->cid_override_mode = mode;
|
skel->rodata->cid_override_mode = mode;
|
||||||
|
cid_override_shard_sz = 4;
|
||||||
/* 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;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* bad-mono needs >= 3 shards to build a 0-based but
|
* bad-mono needs >= 3 shards to build a 0-based but
|
||||||
@@ -368,21 +357,12 @@ int main(int argc, char **argv)
|
|||||||
nr_cpus);
|
nr_cpus);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
shard_sz = nr_cpus / 3;
|
cid_override_shard_sz = nr_cpus / 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* shards of shard_sz each */
|
/* shards of shard_sz each */
|
||||||
skel->rodata->cid_override_nr_shards = (nr_cpus + shard_sz - 1) / shard_sz;
|
skel->rodata->cid_override_nr_shards =
|
||||||
for (i = 0; i < skel->rodata->cid_override_nr_shards; i++)
|
(nr_cpus + cid_override_shard_sz - 1) / cid_override_shard_sz;
|
||||||
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;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'i':
|
case 'i':
|
||||||
@@ -428,9 +408,43 @@ int main(int argc, char **argv)
|
|||||||
skel->rodata->round_robin_ns = (u64)round_robin_ms * 1000000;
|
skel->rodata->round_robin_ns = (u64)round_robin_ms * 1000000;
|
||||||
|
|
||||||
SCX_OPS_LOAD(skel, qmap_ops, scx_qmap, uei);
|
SCX_OPS_LOAD(skel, qmap_ops, scx_qmap, uei);
|
||||||
link = SCX_OPS_ATTACH(skel, qmap_ops, scx_qmap);
|
|
||||||
|
|
||||||
qa = &skel->arena->qa;
|
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->test_error_cnt = test_error_cnt;
|
||||||
qa->inject_mode = inject_mode;
|
qa->inject_mode = inject_mode;
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,10 @@ struct qmap_arena {
|
|||||||
|
|
||||||
struct cpu_ctx cpu_ctxs[SCX_QMAP_MAX_CPUS];
|
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() */
|
/* task_ctx slab; allocated and threaded by qmap_init() */
|
||||||
struct task_ctx __arena *task_ctxs;
|
struct task_ctx __arena *task_ctxs;
|
||||||
struct task_ctx __arena *task_free_head;
|
struct task_ctx __arena *task_free_head;
|
||||||
|
|||||||
Reference in New Issue
Block a user