[PATCH] sched_ext: allow ops.cgroup_set_bandwidth() to be sleepable
Changwoo Min <[email protected]>
| Newsgroups | dev.linux.lists.sched-ext,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
ops.cgroup_set_bandwidth() is delivered from scx_group_set_bandwidth(), which runs from the cpu.max cgroup interface write path (tg_set_bandwidth()) in process context. tg_set_cfs_bandwidth() has already returned by then, so its cpus_read_lock and cfs_constraints_mutex are released, and the only lock held is percpu_down_read(&scx_cgroup_ops_rwsem), whose read side may sleep. The call site is therefore sleepable, like ops.cgroup_init(). bpf_scx_check_member() rejects a sleepable program on any member not on its allow-list, so a BPF scheduler cannot allocate -- which is sleepable -- when a cgroup gains a cpu.max limit at runtime; it must instead pre-reserve memory for a callback that cannot allocate. Add cgroup_set_bandwidth() to the allow-list so the callback can allocate on demand, and document that it may block. A scheduler must decide at load time whether to mark the callback sleepable, but the allow-list entry is a verifier property with no symbol to probe. Add scx_cgroup_set_bandwidth_may_sleep() as a marker whose presence in the kernel's BTF lets userspace detect this support; it has no callers and does nothing. Signed-off-by: Changwoo Min <[email protected]> --- kernel/sched/ext/ext.c | 10 ++++++++++ kernel/sched/ext/ext.h | 1 + kernel/sched/ext/internal.h | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 10af28a9f2c0..e25a2e9f4eca 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -4960,6 +4960,15 @@ void scx_group_set_bandwidth(struct task_group *tg, percpu_up_read(&scx_cgroup_ops_rwsem); } + +/* + * Capability marker for userspace. The sleepable allowance for + * ops.cgroup_set_bandwidth() (see bpf_scx_check_member()) is a verifier + * property with no other symbol a scheduler can probe, so this no-op function + * exists solely so its presence in the kernel's BTF can be detected. It has no + * callers; __used keeps it from being optimized away. + */ +__used void scx_cgroup_set_bandwidth_may_sleep(void) {} #endif /* CONFIG_EXT_GROUP_SCHED */ #if defined(CONFIG_EXT_GROUP_SCHED) || defined(CONFIG_EXT_SUB_SCHED) @@ -8079,6 +8088,7 @@ static int bpf_scx_check_member(const struct btf_type *t, case offsetof(struct sched_ext_ops, cgroup_init): case offsetof(struct sched_ext_ops, cgroup_exit): case offsetof(struct sched_ext_ops, cgroup_prep_move): + case offsetof(struct sched_ext_ops, cgroup_set_bandwidth): #endif case offsetof(struct sched_ext_ops, cpu_online): case offsetof(struct sched_ext_ops, cpu_offline): diff --git a/kernel/sched/ext/ext.h b/kernel/sched/ext/ext.h index 0b7fc46aee08..e6fcfadc25aa 100644 --- a/kernel/sched/ext/ext.h +++ b/kernel/sched/ext/ext.h @@ -81,6 +81,7 @@ void scx_cgroup_cancel_attach(struct cgroup_taskset *tset); void scx_group_set_weight(struct task_group *tg, unsigned long cgrp_weight); void scx_group_set_idle(struct task_group *tg, bool idle); void scx_group_set_bandwidth(struct task_group *tg, u64 period_us, u64 quota_us, u64 burst_us); +void scx_cgroup_set_bandwidth_may_sleep(void); #else /* CONFIG_EXT_GROUP_SCHED */ static inline void scx_tg_init(struct task_group *tg) {} static inline int scx_tg_online(struct task_group *tg) { return 0; } diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h index 27bbf5e04d90..e2d553d13c49 100644 --- a/kernel/sched/ext/internal.h +++ b/kernel/sched/ext/internal.h @@ -753,7 +753,7 @@ struct sched_ext_ops { * @burst_us: bandwidth control burst * * Update @cgrp's bandwidth control parameters. This is from the cpu.max - * cgroup interface. + * cgroup interface. This operation may block. * * @quota_us / @period_us determines the CPU bandwidth @cgrp is entitled * to. For example, if @period_us is 1_000_000 and @quota_us is -- 2.55.0