sched_ext: Allow ops.cgroup_set_bandwidth() to be sleepable

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. scx_group_set_bandwidth() holds
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
a compatibility marker whose presence in the kernel's BTF lets userspace detect
this support: DEFINE_SCX_COMPAT_MARKER() emits an empty, callerless function,
here scx_compat_marker_cgroup_set_bandwidth_may_sleep(). It is __used
__retain so neither the compiler nor the linker (under
CONFIG_LD_DEAD_CODE_DATA_ELIMINATION) drops it. The markers share the
scx_compat_marker_ prefix and are collected near the end of ext.c so more
can be added as further capabilities appear.

Signed-off-by: Changwoo Min <changwoo@igalia.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
This commit is contained in:
Changwoo Min 2026-08-19 01:04:29 +09:00 committed by Tejun Heo
parent fab183d632
commit 5f01293930
2 changed files with 36 additions and 1 deletions

View File

@ -8079,6 +8079,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):
@ -11041,3 +11042,16 @@ static int __init scx_init(void)
return 0;
}
__initcall(scx_init);
/*
* Compatibility markers for userspace. Existence of a marker function
* represents that the kernel supports that sched-ext feature.
*/
/*
* scx_compat_marker_cgroup_set_bandwidth_may_sleep: advertises that
* ops.cgroup_set_bandwidth() may be implemented as a sleepable callback.
*/
#ifdef CONFIG_EXT_GROUP_SCHED
DEFINE_SCX_COMPAT_MARKER(cgroup_set_bandwidth_may_sleep);
#endif /* CONFIG_EXT_GROUP_SCHED */

View File

@ -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
@ -2001,6 +2001,27 @@ struct scx_bstr_buf {
char line[SCX_EXIT_MSG_LEN];
};
/* Internal helper for DEFINE_SCX_COMPAT_MARKER(). */
#define DECLARE_SCX_COMPAT_MARKER(func) \
extern void scx_compat_marker_##func(void)
/**
* DEFINE_SCX_COMPAT_MARKER() - define a userspace capability marker
* @func: marker suffix; the defined symbol is scx_compat_marker_@func
*
* Emit an empty, callerless function that is retained in the kernel's BTF.
* Its presence is part of the kernel<->userspace contract: userspace probes
* scx_compat_marker_@func (e.g. via BTF) to detect that this kernel supports
* the corresponding feature.
*
* The leading declaration suppresses the missing-prototype warning; the
* trailing declaration consumes the semicolon at the use site.
*/
#define DEFINE_SCX_COMPAT_MARKER(func) \
DECLARE_SCX_COMPAT_MARKER(func); \
__used __retain void scx_compat_marker_##func(void) {} \
DECLARE_SCX_COMPAT_MARKER(func)
extern struct scx_sched __rcu *scx_root;
DECLARE_PER_CPU(struct rq *, scx_locked_rq_state);