From: Tao Cui scx_bpf_sub_caps() flags the scheduler for exit when the target's caps storage isn't up yet. But the guard's own comment names a legitimate way to get there: a scheduler reading its own caps from ops.init_cids(), which runs before scx_alloc_pshards() publishes the storage. An init_cids() implementation that starts by querying its caps, a reasonable thing to do, kills the scheduler it is still initializing. Return -EBUSY instead: the caller sees a retryable "not ready yet" and the comment's expected scenario stays survivable. This mirrors how the cid path already handles the same class of pre-init window: reads before the tables are published test the table pointer and return -EINVAL instead of killing the scheduler. Fixes: 86094b95efcf ("sched_ext: Add per-shard cap delegation for sub-schedulers") Signed-off-by: Tao Cui --- kernel/sched/ext/sub.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/kernel/sched/ext/sub.c b/kernel/sched/ext/sub.c index d2da4d2322f8..55e637e7a54a 100644 --- a/kernel/sched/ext/sub.c +++ b/kernel/sched/ext/sub.c @@ -3081,8 +3081,10 @@ __bpf_kfunc void scx_bpf_sub_revoke(u64 cgroup_id, u64 caps, * child - the literal granted set. A sched can read only itself or a direct * child. * - * Return 0, -ENODEV if @cgroup_id names no direct child, or -EINVAL on bad - * inputs. + * Return 0, -ENODEV if @cgroup_id names no direct child or the caller has + * no scheduler, -EINVAL on bad inputs, -EOPNOTSUPP on the cpu form, or + * -EBUSY if the target's caps storage is not up yet (expected from a + * self-read in ops.init_cids()). */ __bpf_kfunc s32 scx_bpf_sub_caps(u64 cgroup_id, u64 caps, struct scx_cmask *out__arena, const struct bpf_prog_aux *aux) @@ -3124,15 +3126,14 @@ __bpf_kfunc s32 scx_bpf_sub_caps(u64 cgroup_id, u64 caps, struct scx_cmask *out_ /* * The target's caps storage may not be set up yet (e.g. a self-read - * during ops.init_cids()). Pairs with the publish in - * scx_alloc_pshards(): a non-NULL pshard has every element set and the - * acquire also orders the cid table reads below against it. + * during ops.init_cids()); return -EBUSY instead of aborting the + * scheduler. Pairs with the publish in scx_alloc_pshards(): a + * non-NULL pshard has every element set and the acquire also orders + * the cid table reads below against it. */ pshard = smp_load_acquire(&target->pshard); - if (unlikely(!pshard)) { - scx_error(sch, "scx_bpf_sub_caps() called before caps storage is initialized"); - return -ENODEV; - } + if (unlikely(!pshard)) + return -EBUSY; ret = scx_cmask_ref_init(sch, out__arena, &ref); if (ret) { -- 2.53.0 base-commit: 5b740a72b55c4267ba4d8010f17dde26b26f52d3