Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

selftests/sched_ext: Add test for scx_bpf_select_cpu_and()

Add a selftest to validate the behavior of the built-in idle CPU
selection policy applied to a subset of allowed CPUs, using
scx_bpf_select_cpu_and().

Signed-off-by: Andrea Righi <arighi@nvidia.com>
Signed-off-by: Tejun Heo <tj@kernel.org>

authored by

Andrea Righi and committed by
Tejun Heo
01d541ba 683d2d0f

+179
+1
tools/testing/selftests/sched_ext/Makefile
··· 173 173 maybe_null \ 174 174 minimal \ 175 175 numa \ 176 + allowed_cpus \ 176 177 prog_run \ 177 178 reload_loop \ 178 179 select_cpu_dfl \
+121
tools/testing/selftests/sched_ext/allowed_cpus.bpf.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * A scheduler that validates the behavior of scx_bpf_select_cpu_and() by 4 + * selecting idle CPUs strictly within a subset of allowed CPUs. 5 + * 6 + * Copyright (c) 2025 Andrea Righi <arighi@nvidia.com> 7 + */ 8 + 9 + #include <scx/common.bpf.h> 10 + 11 + char _license[] SEC("license") = "GPL"; 12 + 13 + UEI_DEFINE(uei); 14 + 15 + private(PREF_CPUS) struct bpf_cpumask __kptr * allowed_cpumask; 16 + 17 + static void 18 + validate_idle_cpu(const struct task_struct *p, const struct cpumask *allowed, s32 cpu) 19 + { 20 + if (scx_bpf_test_and_clear_cpu_idle(cpu)) 21 + scx_bpf_error("CPU %d should be marked as busy", cpu); 22 + 23 + if (bpf_cpumask_subset(allowed, p->cpus_ptr) && 24 + !bpf_cpumask_test_cpu(cpu, allowed)) 25 + scx_bpf_error("CPU %d not in the allowed domain for %d (%s)", 26 + cpu, p->pid, p->comm); 27 + } 28 + 29 + s32 BPF_STRUCT_OPS(allowed_cpus_select_cpu, 30 + struct task_struct *p, s32 prev_cpu, u64 wake_flags) 31 + { 32 + const struct cpumask *allowed; 33 + s32 cpu; 34 + 35 + allowed = cast_mask(allowed_cpumask); 36 + if (!allowed) { 37 + scx_bpf_error("allowed domain not initialized"); 38 + return -EINVAL; 39 + } 40 + 41 + /* 42 + * Select an idle CPU strictly within the allowed domain. 43 + */ 44 + cpu = scx_bpf_select_cpu_and(p, prev_cpu, wake_flags, allowed, 0); 45 + if (cpu >= 0) { 46 + validate_idle_cpu(p, allowed, cpu); 47 + scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL, SCX_SLICE_DFL, 0); 48 + 49 + return cpu; 50 + } 51 + 52 + return prev_cpu; 53 + } 54 + 55 + void BPF_STRUCT_OPS(allowed_cpus_enqueue, struct task_struct *p, u64 enq_flags) 56 + { 57 + const struct cpumask *allowed; 58 + s32 prev_cpu = scx_bpf_task_cpu(p), cpu; 59 + 60 + scx_bpf_dsq_insert(p, SCX_DSQ_GLOBAL, SCX_SLICE_DFL, 0); 61 + 62 + allowed = cast_mask(allowed_cpumask); 63 + if (!allowed) { 64 + scx_bpf_error("allowed domain not initialized"); 65 + return; 66 + } 67 + 68 + /* 69 + * Use scx_bpf_select_cpu_and() to proactively kick an idle CPU 70 + * within @allowed_cpumask, usable by @p. 71 + */ 72 + cpu = scx_bpf_select_cpu_and(p, prev_cpu, 0, allowed, 0); 73 + if (cpu >= 0) { 74 + validate_idle_cpu(p, allowed, cpu); 75 + scx_bpf_kick_cpu(cpu, SCX_KICK_IDLE); 76 + } 77 + } 78 + 79 + s32 BPF_STRUCT_OPS_SLEEPABLE(allowed_cpus_init) 80 + { 81 + struct bpf_cpumask *mask; 82 + 83 + mask = bpf_cpumask_create(); 84 + if (!mask) 85 + return -ENOMEM; 86 + 87 + mask = bpf_kptr_xchg(&allowed_cpumask, mask); 88 + if (mask) 89 + bpf_cpumask_release(mask); 90 + 91 + bpf_rcu_read_lock(); 92 + 93 + /* 94 + * Assign the first online CPU to the allowed domain. 95 + */ 96 + mask = allowed_cpumask; 97 + if (mask) { 98 + const struct cpumask *online = scx_bpf_get_online_cpumask(); 99 + 100 + bpf_cpumask_set_cpu(bpf_cpumask_first(online), mask); 101 + scx_bpf_put_cpumask(online); 102 + } 103 + 104 + bpf_rcu_read_unlock(); 105 + 106 + return 0; 107 + } 108 + 109 + void BPF_STRUCT_OPS(allowed_cpus_exit, struct scx_exit_info *ei) 110 + { 111 + UEI_RECORD(uei, ei); 112 + } 113 + 114 + SEC(".struct_ops.link") 115 + struct sched_ext_ops allowed_cpus_ops = { 116 + .select_cpu = (void *)allowed_cpus_select_cpu, 117 + .enqueue = (void *)allowed_cpus_enqueue, 118 + .init = (void *)allowed_cpus_init, 119 + .exit = (void *)allowed_cpus_exit, 120 + .name = "allowed_cpus", 121 + };
+57
tools/testing/selftests/sched_ext/allowed_cpus.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (c) 2025 Andrea Righi <arighi@nvidia.com> 4 + */ 5 + #include <bpf/bpf.h> 6 + #include <scx/common.h> 7 + #include <sys/wait.h> 8 + #include <unistd.h> 9 + #include "allowed_cpus.bpf.skel.h" 10 + #include "scx_test.h" 11 + 12 + static enum scx_test_status setup(void **ctx) 13 + { 14 + struct allowed_cpus *skel; 15 + 16 + skel = allowed_cpus__open(); 17 + SCX_FAIL_IF(!skel, "Failed to open"); 18 + SCX_ENUM_INIT(skel); 19 + SCX_FAIL_IF(allowed_cpus__load(skel), "Failed to load skel"); 20 + 21 + *ctx = skel; 22 + 23 + return SCX_TEST_PASS; 24 + } 25 + 26 + static enum scx_test_status run(void *ctx) 27 + { 28 + struct allowed_cpus *skel = ctx; 29 + struct bpf_link *link; 30 + 31 + link = bpf_map__attach_struct_ops(skel->maps.allowed_cpus_ops); 32 + SCX_FAIL_IF(!link, "Failed to attach scheduler"); 33 + 34 + /* Just sleeping is fine, plenty of scheduling events happening */ 35 + sleep(1); 36 + 37 + SCX_EQ(skel->data->uei.kind, EXIT_KIND(SCX_EXIT_NONE)); 38 + bpf_link__destroy(link); 39 + 40 + return SCX_TEST_PASS; 41 + } 42 + 43 + static void cleanup(void *ctx) 44 + { 45 + struct allowed_cpus *skel = ctx; 46 + 47 + allowed_cpus__destroy(skel); 48 + } 49 + 50 + struct scx_test allowed_cpus = { 51 + .name = "allowed_cpus", 52 + .description = "Verify scx_bpf_select_cpu_and()", 53 + .setup = setup, 54 + .run = run, 55 + .cleanup = cleanup, 56 + }; 57 + REGISTER_SCX_TEST(&allowed_cpus)