From: Tejun Heo <tj@kernel.org>
To: David Vernet <void@manifault.com>,
Andrea Righi <arighi@nvidia.com>,
Changwoo Min <changwoo@igalia.com>
Cc: sched-ext@lists.linux.dev, Emil Tsalapatis <emil@etsalapatis.com>,
linux-kernel@vger.kernel.org, Tejun Heo <tj@kernel.org>
Subject: [PATCH v3 sched_ext/for-7.3 34/36] sched_ext: Add scx_bpf_sub_kill() to evict a child sub-scheduler
Date: Mon, 6 Jul 2026 14:12:27 -1000 [thread overview]
Message-ID: <20260707001229.1410929-35-tj@kernel.org> (raw)
In-Reply-To: <20260707001229.1410929-1-tj@kernel.org>
A cid-form scheduler can grant caps to and revoke them from its child
sub-schedulers but has no way to tear one down. Add scx_bpf_sub_kill() to
evict a direct child with a printf-style reason that reaches the child's
scx_exit_info. No exit code is taken because the child is a separate
scheduler whose exit-code semantics the parent cannot know. The child and
its subtree are disabled through the usual async path under a new exit kind,
SCX_EXIT_PARENT_KILL.
The bstr formatting infrastructure in ext.c is exposed through internal.h
with scx_ prefixes so the kfunc, which lives in sub.c, can format the
reason.
Signed-off-by: Tejun Heo <tj@kernel.org>
---
kernel/sched/ext/ext.c | 28 +++++++-----
kernel/sched/ext/internal.h | 11 +++++
kernel/sched/ext/sub.c | 57 ++++++++++++++++++++++++
tools/sched_ext/include/scx/common.bpf.h | 18 ++++++++
4 files changed, 102 insertions(+), 12 deletions(-)
diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index c780acdc5f5c..9db4cb8ad92d 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -187,14 +187,8 @@ static const struct rhashtable_params dsq_hash_params = {
static LLIST_HEAD(dsqs_to_free);
-/* string formatting from BPF */
-struct scx_bstr_buf {
- u64 data[MAX_BPRINTF_VARARGS];
- char line[SCX_EXIT_MSG_LEN];
-};
-
-static DEFINE_RAW_SPINLOCK(scx_exit_bstr_buf_lock);
-static struct scx_bstr_buf scx_exit_bstr_buf;
+DEFINE_RAW_SPINLOCK(scx_exit_bstr_buf_lock);
+struct scx_bstr_buf scx_exit_bstr_buf;
/* ops debug dump */
static DEFINE_RAW_SPINLOCK(scx_dump_lock);
@@ -5705,6 +5699,8 @@ static const char *scx_exit_reason(enum scx_exit_kind kind)
return "disabled by sysrq-S";
case SCX_EXIT_PARENT:
return "parent exiting";
+ case SCX_EXIT_PARENT_KILL:
+ return "killed by parent scheduler";
case SCX_EXIT_ERROR:
return "runtime error";
case SCX_EXIT_ERROR_BPF:
@@ -9309,8 +9305,8 @@ static s32 __bstr_format(struct scx_sched *sch, u64 *data_buf, char *line_buf,
}
__printf(3, 0)
-static s32 bstr_format(struct scx_sched *sch, struct scx_bstr_buf *buf,
- char *fmt, unsigned long long *data, u32 data__sz)
+s32 scx_bstr_format(struct scx_sched *sch, struct scx_bstr_buf *buf,
+ char *fmt, unsigned long long *data, u32 data__sz)
{
return __bstr_format(sch, buf->data, buf->line, sizeof(buf->line),
fmt, data, data__sz);
@@ -9340,7 +9336,7 @@ __bpf_kfunc void scx_bpf_exit_bstr(s64 exit_code, char *fmt,
raw_spin_lock_irqsave(&scx_exit_bstr_buf_lock, flags);
sch = scx_prog_sched(aux);
if (likely(sch) &&
- bstr_format(sch, &scx_exit_bstr_buf, fmt, data, data__sz) >= 0)
+ scx_bstr_format(sch, &scx_exit_bstr_buf, fmt, data, data__sz) >= 0)
scx_exit(sch, SCX_EXIT_UNREG_BPF, exit_code, "%s", scx_exit_bstr_buf.line);
raw_spin_unlock_irqrestore(&scx_exit_bstr_buf_lock, flags);
}
@@ -9365,7 +9361,7 @@ __bpf_kfunc void scx_bpf_error_bstr(char *fmt, unsigned long long *data,
raw_spin_lock_irqsave(&scx_exit_bstr_buf_lock, flags);
sch = scx_prog_sched(aux);
if (likely(sch) &&
- bstr_format(sch, &scx_exit_bstr_buf, fmt, data, data__sz) >= 0)
+ scx_bstr_format(sch, &scx_exit_bstr_buf, fmt, data, data__sz) >= 0)
scx_exit(sch, SCX_EXIT_ERROR_BPF, 0, "%s", scx_exit_bstr_buf.line);
raw_spin_unlock_irqrestore(&scx_exit_bstr_buf_lock, flags);
}
@@ -10001,6 +9997,13 @@ __bpf_kfunc s32 scx_bpf_sub_caps(u64 cgroup_id, u64 caps, struct scx_cmask *out_
{
return -EOPNOTSUPP;
}
+
+__bpf_kfunc s32 scx_bpf_sub_kill_bstr(u64 cgroup_id, char *fmt,
+ unsigned long long *data, u32 data__sz,
+ const struct bpf_prog_aux *aux)
+{
+ return -EOPNOTSUPP;
+}
#endif /* !CONFIG_EXT_SUB_SCHED */
__bpf_kfunc_end_defs();
@@ -10050,6 +10053,7 @@ BTF_ID_FLAGS(func, scx_bpf_task_cgroup, KF_IMPLICIT_ARGS | KF_RCU | KF_ACQUIRE)
BTF_ID_FLAGS(func, scx_bpf_sub_grant, KF_IMPLICIT_ARGS)
BTF_ID_FLAGS(func, scx_bpf_sub_revoke, KF_IMPLICIT_ARGS)
BTF_ID_FLAGS(func, scx_bpf_sub_caps, KF_IMPLICIT_ARGS)
+BTF_ID_FLAGS(func, scx_bpf_sub_kill_bstr, KF_IMPLICIT_ARGS)
BTF_KFUNCS_END(scx_kfunc_ids_any)
static const struct btf_kfunc_id_set scx_kfunc_set_any = {
diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h
index 74160117465a..1c3cf4fc41bb 100644
--- a/kernel/sched/ext/internal.h
+++ b/kernel/sched/ext/internal.h
@@ -51,6 +51,7 @@ enum scx_exit_kind {
SCX_EXIT_UNREG_KERN, /* kernel-initiated unregistration */
SCX_EXIT_SYSRQ, /* requested by 'S' sysrq */
SCX_EXIT_PARENT, /* parent exiting */
+ SCX_EXIT_PARENT_KILL, /* killed by parent scheduler */
SCX_EXIT_ERROR = 1024, /* runtime error, error msg contains details */
SCX_EXIT_ERROR_BPF, /* ERROR but triggered through scx_bpf_error() */
@@ -1874,6 +1875,12 @@ struct scx_enable_cmd {
int ret;
};
+/* string formatting from BPF */
+struct scx_bstr_buf {
+ u64 data[MAX_BPRINTF_VARARGS];
+ char line[SCX_EXIT_MSG_LEN];
+};
+
extern struct scx_sched __rcu *scx_root;
DECLARE_PER_CPU(struct rq *, scx_locked_rq_state);
@@ -1934,10 +1941,14 @@ struct scx_sched *scx_alloc_and_add_sched(struct scx_enable_cmd *cmd,
int scx_validate_ops(struct scx_sched *sch, const struct sched_ext_ops *ops);
int scx_sched_sysfs_add(struct scx_sched *sch);
bool scx_is_descendant(struct scx_sched *sch, struct scx_sched *ancestor);
+__printf(3, 0) s32 scx_bstr_format(struct scx_sched *sch, struct scx_bstr_buf *buf,
+ char *fmt, unsigned long long *data, u32 data__sz);
extern raw_spinlock_t scx_sched_lock;
extern struct mutex scx_enable_mutex;
extern struct percpu_rw_semaphore scx_fork_rwsem;
+extern raw_spinlock_t scx_exit_bstr_buf_lock;
+extern struct scx_bstr_buf scx_exit_bstr_buf;
#ifdef CONFIG_EXT_SUB_SCHED
extern const struct rhashtable_params scx_sched_hash_params;
extern struct rhashtable scx_sched_hash;
diff --git a/kernel/sched/ext/sub.c b/kernel/sched/ext/sub.c
index 55cd1f9a597a..e550cfb366ad 100644
--- a/kernel/sched/ext/sub.c
+++ b/kernel/sched/ext/sub.c
@@ -1685,6 +1685,63 @@ __bpf_kfunc s32 scx_bpf_sub_caps(u64 cgroup_id, u64 caps, struct scx_cmask *out_
return 0;
}
+/**
+ * scx_bpf_sub_kill_bstr - Kill a direct child sub-scheduler
+ * @cgroup_id: cgroup id of the direct child to kill
+ * @fmt: reason message format string
+ * @data: format string parameters packaged using ___bpf_fill() macro
+ * @data__sz: @data len, must end in '__sz' for the verifier
+ * @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs
+ *
+ * Evict a direct child sub-scheduler, disabling it with the supplied reason.
+ * The child and its subtree are torn down asynchronously through the usual
+ * disable path.
+ *
+ * Unlike scx_bpf_exit(), no exit code is taken: the child is a separate
+ * scheduler with its own exit-code semantics, so a code chosen by the parent
+ * would have no defined meaning. The reason string carries the intent.
+ *
+ * Return 0 on success or -ENODEV if @cgroup_id names no sub-scheduler, which
+ * can race with the child detaching on its own and so is not a scheduler error.
+ * Naming a sched that exists but is not a direct child aborts the parent.
+ */
+__printf(2, 0)
+__bpf_kfunc s32 scx_bpf_sub_kill_bstr(u64 cgroup_id, char *fmt,
+ unsigned long long *data, u32 data__sz,
+ const struct bpf_prog_aux *aux)
+{
+ struct scx_sched *parent, *child;
+ s32 ret;
+
+ guard(rcu)();
+
+ parent = scx_prog_sched(aux);
+ if (unlikely(!parent))
+ return -ENODEV;
+
+ if (!scx_is_cid_type()) {
+ scx_error(parent, "sub-cap kfuncs require a cid-form scheduler");
+ return -EOPNOTSUPP;
+ }
+
+ child = scx_find_sub_sched(cgroup_id);
+ if (unlikely(!child))
+ return -ENODEV;
+
+ if (unlikely(scx_parent(child) != parent)) {
+ scx_error(parent, "%s: sub-%llu is not a direct child",
+ parent->cgrp_path, cgroup_id);
+ return -EINVAL;
+ }
+
+ guard(raw_spinlock_irqsave)(&scx_exit_bstr_buf_lock);
+ ret = scx_bstr_format(parent, &scx_exit_bstr_buf, fmt, data, data__sz);
+ if (ret < 0)
+ return ret;
+ scx_exit(child, SCX_EXIT_PARENT_KILL, 0, "%s", scx_exit_bstr_buf.line);
+ return 0;
+}
+
__bpf_kfunc_end_defs();
#endif /* CONFIG_EXT_SUB_SCHED */
diff --git a/tools/sched_ext/include/scx/common.bpf.h b/tools/sched_ext/include/scx/common.bpf.h
index 09c21602b2ed..acc2b131ea8f 100644
--- a/tools/sched_ext/include/scx/common.bpf.h
+++ b/tools/sched_ext/include/scx/common.bpf.h
@@ -119,6 +119,8 @@ s32 scx_bpf_sub_grant(u64 cgroup_id, u64 caps, const struct scx_cmask *cmask,
struct scx_cmask *denied) __ksym __weak;
void scx_bpf_sub_revoke(u64 cgroup_id, u64 caps, const struct scx_cmask *cmask) __ksym __weak;
s32 scx_bpf_sub_caps(u64 cgroup_id, u64 caps, struct scx_cmask *out) __ksym __weak;
+s32 scx_bpf_sub_kill_bstr(u64 cgroup_id, char *fmt,
+ unsigned long long *data, u32 data__sz) __ksym __weak;
/*
* Use the following as @it__iter when calling scx_bpf_dsq_move[_vtime]() from
@@ -165,6 +167,22 @@ void ___scx_bpf_bstr_format_checker(const char *fmt, ...) {}
___scx_bpf_bstr_format_checker(fmt, ##args); \
})
+/*
+ * scx_bpf_sub_kill() wraps the scx_bpf_sub_kill_bstr() kfunc with variadic
+ * arguments instead of an array of u64. It kills the direct child sub-scheduler
+ * @cgid, passing the formatted reason to its user space, and evaluates to the
+ * kfunc's return value. On a kernel without sub-scheduler support the kfunc is
+ * absent and it returns -EOPNOTSUPP.
+ */
+#define scx_bpf_sub_kill(cgid, fmt, args...) \
+({ \
+ scx_bpf_bstr_preamble(fmt, args) \
+ ___scx_bpf_bstr_format_checker(fmt, ##args); \
+ bpf_ksym_exists(scx_bpf_sub_kill_bstr) ? \
+ scx_bpf_sub_kill_bstr((cgid), ___fmt, ___param, \
+ sizeof(___param)) : -EOPNOTSUPP; \
+})
+
/*
* scx_bpf_error() wraps the scx_bpf_error_bstr() kfunc with variadic arguments
* instead of an array of u64. Invoking this macro will cause the scheduler to
--
2.54.0
next prev parent reply other threads:[~2026-07-07 0:13 UTC|newest]
Thread overview: 51+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-07 0:11 [PATCHSET v3 sched_ext/for-7.3] sched_ext: Capability-based CPU delegation for sub-schedulers Tejun Heo
2026-07-07 0:11 ` [PATCH v3 sched_ext/for-7.3 01/36] sched_ext: Add SCX_CALL_CID_OP_TASK() for cid-form op dispatch Tejun Heo
2026-07-07 0:11 ` [PATCH v3 sched_ext/for-7.3 02/36] sched_ext: Rename extra_enq_flags to remote_activate_enq_flags Tejun Heo
2026-07-07 0:11 ` [PATCH v3 sched_ext/for-7.3 03/36] sched_ext: Fix premature ops->priv publication in scx_alloc_and_add_sched() Tejun Heo
2026-07-07 0:11 ` [PATCH v3 sched_ext/for-7.3 04/36] tools/sched_ext: scx - Fix cmask_subset(), cmask_equal() and cmask_weight() Tejun Heo
2026-07-07 0:11 ` [PATCH v3 sched_ext/for-7.3 05/36] sched_ext: Use READ_ONCE/WRITE_ONCE in cmask word ops and drop _RACY variants Tejun Heo
2026-07-07 0:11 ` [PATCH v3 sched_ext/for-7.3 06/36] tools/sched_ext: scx_qmap - Use bare u64/u32/s32 integer types Tejun Heo
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 07/36] sched_ext: Reject direct slice and dsq_vtime writes for cid-form schedulers Tejun Heo
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 08/36] sched_ext: Make scx_bpf_kick_cid() return void Tejun Heo
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 09/36] sched_ext: Make the kick machinery per-sched Tejun Heo
2026-07-07 0:33 ` sashiko-bot
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 10/36] sched_ext: Add ops.init_cids() to finalize the cid layout before init Tejun Heo
2026-07-07 0:36 ` sashiko-bot
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 11/36] sched_ext: Add CID sharding Tejun Heo
2026-07-07 0:30 ` sashiko-bot
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 12/36] sched_ext: Add shard boundaries to scx_bpf_cid_override() Tejun Heo
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 13/36] sched_ext: Defer scx_sched kobj sysfs add into the enable workfns Tejun Heo
2026-07-07 0:31 ` sashiko-bot
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 14/36] sched_ext: Add per-shard scx_sched storage scaffolding Tejun Heo
2026-07-07 0:28 ` sashiko-bot
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 15/36] sched_ext: Add scx_cmask_ref for validated arena cmask access Tejun Heo
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 16/36] sched_ext: Build the set_cmask scratch from trusted geometry Tejun Heo
2026-07-07 0:30 ` sashiko-bot
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 17/36] sched_ext: RCU-protect the sub-sched tree's children/sibling lists Tejun Heo
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 18/36] sched_ext: Add scx_skip_subtree_pre() Tejun Heo
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 19/36] sched_ext: Add per-shard cap delegation for sub-schedulers Tejun Heo
2026-07-07 0:39 ` sashiko-bot
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 20/36] sched_ext: Add coalescing sub_caps_updated() notifier " Tejun Heo
2026-07-07 0:33 ` sashiko-bot
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 21/36] sched_ext: Maintain per-cpu effective cap copies for single-read checks Tejun Heo
2026-07-07 0:38 ` sashiko-bot
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 22/36] sched_ext: Add sub_ecaps_updated() effective-cap change notifier Tejun Heo
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 23/36] sched_ext: Generalize local-DSQ handling to rq-owned DSQs Tejun Heo
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 24/36] sched_ext: Add reject DSQ for cap-rejected dispatches Tejun Heo
2026-07-07 0:38 ` sashiko-bot
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 25/36] sched_ext: Add the SCX_CAP_ENQ_IMMED cap Tejun Heo
2026-07-07 0:32 ` sashiko-bot
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 26/36] sched_ext: Assign a unique id to each scheduler instance Tejun Heo
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 27/36] sched_ext: Route task slice writes through set_task_slice() Tejun Heo
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 28/36] sched_ext: Tie cpu occupancy to SCX_CAP_BASE through the task slice Tejun Heo
2026-07-07 0:38 ` sashiko-bot
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 29/36] sched_ext: Add the SCX_CAP_ENQ cap Tejun Heo
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 30/36] sched_ext: Gate kicks on SCX_CAP_BASE and preemption on SCX_CAP_PREEMPT Tejun Heo
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 31/36] sched_ext: Authorize remote-move inserts against the placing scheduler Tejun Heo
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 32/36] sched_ext: Route ops.update_idle() to sub-schedulers and re-notify owed scheds Tejun Heo
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 33/36] sched_ext: Replay ecaps notifications suppressed by bypass Tejun Heo
2026-07-07 0:12 ` Tejun Heo [this message]
2026-07-07 0:42 ` [PATCH v3 sched_ext/for-7.3 34/36] sched_ext: Add scx_bpf_sub_kill() to evict a child sub-scheduler sashiko-bot
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 35/36] tools/sched_ext: scx_qmap - Expand hierarchical sub-scheduling Tejun Heo
2026-07-07 0:41 ` sashiko-bot
2026-07-07 0:12 ` [PATCH v3 sched_ext/for-7.3 36/36] tools/sched_ext: scx_qmap - Add sub-sched cap fault injection Tejun Heo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260707001229.1410929-35-tj@kernel.org \
--to=tj@kernel.org \
--cc=arighi@nvidia.com \
--cc=changwoo@igalia.com \
--cc=emil@etsalapatis.com \
--cc=linux-kernel@vger.kernel.org \
--cc=sched-ext@lists.linux.dev \
--cc=void@manifault.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.