[PATCH sched_ext/for-7.3 12/32] sched_ext: Add per-shard scx_sched storage scaffolding
From: Tejun Heo
Date: Fri Jul 03 2026 - 04:18:22 EST
Add struct scx_pshard and sch->pshard[] indexed by shard_idx, each entry
allocated on its shard's NUMA node from scx_shard_node[si]. The struct
starts empty (one dummy field). Follow-up patches will grow it as
shard-local state lands. Only cid-type schedulers with an arena pool get
pshards.
Allocation happens after ops.init_cids() returns so any
scx_bpf_cid_override() it issues has finalized scx_nr_cid_shards and
scx_shard_node[]. sch->nr_pshards records the array size for the async RCU
free path, which may run after a later scheduler's scx_cid_init() has
rewritten the global.
Signed-off-by: Tejun Heo <tj@xxxxxxxxxx>
---
kernel/sched/ext/ext.c | 8 +++++++
kernel/sched/ext/internal.h | 18 ++++++++++++++++
kernel/sched/ext/sub.c | 42 +++++++++++++++++++++++++++++++++++++
kernel/sched/ext/sub.h | 4 ++++
4 files changed, 72 insertions(+)
diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index fcb8bf0d2422..c0a3a1ead283 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -4652,6 +4652,8 @@ static void scx_sched_free_rcu_work(struct work_struct *work)
free_pnode(sch->pnode[node]);
kfree(sch->pnode);
+ scx_free_pshards(sch);
+
rhashtable_walk_enter(&sch->dsq_hash, &rht_iter);
do {
rhashtable_walk_start(&rht_iter);
@@ -6730,6 +6732,12 @@ static void scx_root_enable_workfn(struct kthread_work *work)
goto err_disable;
}
+ ret = scx_alloc_pshards(sch);
+ if (ret) {
+ cpus_read_unlock();
+ goto err_disable;
+ }
+
if (sch->ops.init) {
ret = SCX_CALL_OP_RET(sch, init, NULL);
if (ret) {
diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h
index 7c6f4ed10cde..e79175fab862 100644
--- a/kernel/sched/ext/internal.h
+++ b/kernel/sched/ext/internal.h
@@ -1183,6 +1183,12 @@ struct scx_sched_pnode {
struct scx_dispatch_q global_dsq;
};
+#ifdef CONFIG_EXT_SUB_SCHED
+struct scx_pshard {
+ int _dummy; /* until the first real field lands */
+};
+#endif
+
struct scx_sched {
/*
* cpu-form and cid-form ops share field offsets up to .priv (verified
@@ -1230,6 +1236,9 @@ struct scx_sched {
*/
struct rhashtable dsq_hash;
struct scx_sched_pnode **pnode;
+#ifdef CONFIG_EXT_SUB_SCHED
+ struct scx_pshard **pshard; /* indexed by shard_idx */
+#endif
struct scx_sched_pcpu __percpu *pcpu;
u64 slice_dfl;
@@ -1245,6 +1254,15 @@ struct scx_sched {
u32 dsp_max_batch;
s32 level;
+#ifdef CONFIG_EXT_SUB_SCHED
+ /*
+ * pshard[] size captured at enable for the async RCU free path -
+ * scx_nr_cid_shards may be rewritten by a later scx_cid_init() before
+ * free runs. While sch is active, use the global.
+ */
+ u32 nr_pshards;
+#endif
+
/*
* Updates to the following warned bitfields can race causing RMW issues
* but it doesn't really matter.
diff --git a/kernel/sched/ext/sub.c b/kernel/sched/ext/sub.c
index e94a415ee10a..c87650f26b30 100644
--- a/kernel/sched/ext/sub.c
+++ b/kernel/sched/ext/sub.c
@@ -82,6 +82,48 @@ void set_cgroup_sched(struct cgroup *cgrp, struct scx_sched *sch)
rcu_assign_pointer(pos->scx_sched, sch);
}
+static void free_pshard(struct scx_pshard *pshard)
+{
+ kfree(pshard);
+}
+
+void scx_free_pshards(struct scx_sched *sch)
+{
+ s32 si;
+
+ if (!sch->pshard)
+ return;
+ for (si = 0; si < sch->nr_pshards; si++)
+ free_pshard(sch->pshard[si]);
+ kfree(sch->pshard);
+}
+
+static struct scx_pshard *alloc_pshard(struct scx_sched *sch, s32 shard_idx, s32 node)
+{
+ return kzalloc_node(sizeof(struct scx_pshard), GFP_KERNEL, node);
+}
+
+s32 scx_alloc_pshards(struct scx_sched *sch)
+{
+ s32 si;
+
+ if (!sch->is_cid_type || !sch->arena_pool)
+ return 0;
+
+ sch->pshard = kzalloc_objs(sch->pshard[0], scx_nr_cid_shards, GFP_KERNEL);
+ if (!sch->pshard)
+ return -ENOMEM;
+
+ sch->nr_pshards = scx_nr_cid_shards;
+
+ for (si = 0; si < scx_nr_cid_shards; si++) {
+ sch->pshard[si] = alloc_pshard(sch, si, scx_shard_node[si]);
+ if (!sch->pshard[si])
+ return -ENOMEM;
+ }
+ return 0;
+}
+
static DECLARE_WAIT_QUEUE_HEAD(scx_unlink_waitq);
void drain_descendants(struct scx_sched *sch)
diff --git a/kernel/sched/ext/sub.h b/kernel/sched/ext/sub.h
index 460a9fd196dc..9fa6b5c8be23 100644
--- a/kernel/sched/ext/sub.h
+++ b/kernel/sched/ext/sub.h
@@ -24,6 +24,8 @@ void drain_descendants(struct scx_sched *sch);
void scx_sub_disable(struct scx_sched *sch);
void scx_sub_enable_workfn(struct kthread_work *work);
bool scx_bpf_sub_dispatch(u64 cgroup_id, const struct bpf_prog_aux *aux);
+void scx_free_pshards(struct scx_sched *sch);
+s32 scx_alloc_pshards(struct scx_sched *sch);
#else /* CONFIG_EXT_SUB_SCHED */
@@ -33,6 +35,8 @@ static inline struct cgroup *sch_cgroup(struct scx_sched *sch) { return NULL; }
static inline void set_cgroup_sched(struct cgroup *cgrp, struct scx_sched *sch) {}
static inline void drain_descendants(struct scx_sched *sch) { }
static inline void scx_sub_disable(struct scx_sched *sch) { }
+static inline void scx_free_pshards(struct scx_sched *sch) {}
+static inline s32 scx_alloc_pshards(struct scx_sched *sch) { return 0; }
#endif /* CONFIG_EXT_SUB_SCHED */
--
2.54.0