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. v3: Build and publish pshard[] fully-formed here rather than a later patch. v2: Free the partially-allocated pshard array on alloc failure. (sashiko AI)
Unified · Split
| @@ -4681,6 +4681,8 @@ static void scx_sched_free_rcu_work(struct work_struct *work) · 4680 unmodified lines | ||
| 4681 | 4681 | free_pnode(sch->pnode[node]); |
| 4682 | 4682 | kfree(sch->pnode); |
| 4683 | 4683 | |
| 4684 | + scx_free_pshards(sch); | |
| 4685 | + | |
| 4684 | 4686 | rhashtable_walk_enter(&sch->dsq_hash, &rht_iter); |
| 4685 | 4687 | do { |
| 4686 | 4688 | rhashtable_walk_start(&rht_iter); |
| @@ -6763,6 +6765,12 @@ static void scx_root_enable_workfn(struct kthread_work *work) · 2076 unmodified lines | ||
| 6763 | 6765 | goto err_disable; |
| 6764 | 6766 | } |
| 6765 | 6767 | |
| 6768 | + ret = scx_alloc_pshards(sch); | |
| 6769 | + if (ret) { | |
| 6770 | + cpus_read_unlock(); | |
| 6771 | + goto err_disable; | |
| 6772 | + } | |
| 6773 | + | |
| 6766 | 6774 | if (sch->ops.init) { |
| 6767 | 6775 | ret = SCX_CALL_OP_RET(sch, init, NULL); |
| 6768 | 6776 | if (ret) { |
| @@ -1183,6 +1183,12 @@ struct scx_sched_pnode { · 1182 unmodified lines | ||
| 1183 | 1183 | struct scx_dispatch_q global_dsq; |
| 1184 | 1184 | }; |
| 1185 | 1185 | |
| 1186 | +#ifdef CONFIG_EXT_SUB_SCHED | |
| 1187 | +struct scx_pshard { | |
| 1188 | + int _dummy; /* until the first real field lands */ | |
| 1189 | +}; | |
| 1190 | +#endif | |
| 1191 | + | |
| 1186 | 1192 | struct scx_sched { |
| 1187 | 1193 | /* |
| 1188 | 1194 | * cpu-form and cid-form ops share field offsets up to .priv (verified |
| @@ -1230,6 +1236,9 @@ struct scx_sched { · 41 unmodified lines | ||
| 1230 | 1236 | */ |
| 1231 | 1237 | struct rhashtable dsq_hash; |
| 1232 | 1238 | struct scx_sched_pnode **pnode; |
| 1239 | +#ifdef CONFIG_EXT_SUB_SCHED | |
| 1240 | + struct scx_pshard **pshard; /* indexed by shard_idx */ | |
| 1241 | +#endif | |
| 1233 | 1242 | struct scx_sched_pcpu __percpu *pcpu; |
| 1234 | 1243 | |
| 1235 | 1244 | u64 slice_dfl; |
| @@ -1245,6 +1254,15 @@ struct scx_sched { · 9 unmodified lines | ||
| 1245 | 1254 | u32 dsp_max_batch; |
| 1246 | 1255 | s32 level; |
| 1247 | 1256 | |
| 1257 | +#ifdef CONFIG_EXT_SUB_SCHED | |
| 1258 | + /* | |
| 1259 | + * pshard[] size captured at enable for the async RCU free path - | |
| 1260 | + * scx_nr_cid_shards may be rewritten by a later scx_cid_init() before | |
| 1261 | + * free runs. While sch is active, use the global. | |
| 1262 | + */ | |
| 1263 | + u32 nr_pshards; | |
| 1264 | +#endif | |
| 1265 | + | |
| 1248 | 1266 | /* |
| 1249 | 1267 | * Updates to the following warned bitfields can race causing RMW issues |
| 1250 | 1268 | * but it doesn't really matter. |
| @@ -82,6 +82,60 @@ void set_cgroup_sched(struct cgroup *cgrp, struct scx_sched *sch) · 81 unmodified lines | ||
| 82 | 82 | rcu_assign_pointer(pos->scx_sched, sch); |
| 83 | 83 | } |
| 84 | 84 | |
| 85 | +static void free_pshard(struct scx_pshard *pshard) | |
| 86 | +{ | |
| 87 | + kfree(pshard); | |
| 88 | +} | |
| 89 | + | |
| 90 | +void scx_free_pshards(struct scx_sched *sch) | |
| 91 | +{ | |
| 92 | + s32 si; | |
| 93 | + | |
| 94 | + if (!sch->pshard) | |
| 95 | + return; | |
| 96 | + for (si = 0; si < sch->nr_pshards; si++) | |
| 97 | + free_pshard(sch->pshard[si]); | |
| 98 | + kfree(sch->pshard); | |
| 99 | +} | |
| 100 | + | |
| 101 | +static struct scx_pshard *alloc_pshard(struct scx_sched *sch, s32 shard_idx, s32 node) | |
| 102 | +{ | |
| 103 | + return kzalloc_node(sizeof(struct scx_pshard), GFP_KERNEL, node); | |
| 104 | +} | |
| 105 | + | |
| 106 | +s32 scx_alloc_pshards(struct scx_sched *sch) | |
| 107 | +{ | |
| 108 | + struct scx_pshard **pshard; | |
| 109 | + s32 si; | |
| 110 | + | |
| 111 | + if (!sch->is_cid_type || !sch->arena_pool) | |
| 112 | + return 0; | |
| 113 | + | |
| 114 | + pshard = kzalloc_objs(pshard[0], scx_nr_cid_shards, GFP_KERNEL); | |
| 115 | + if (!pshard) | |
| 116 | + return -ENOMEM; | |
| 117 | + | |
| 118 | + for (si = 0; si < scx_nr_cid_shards; si++) { | |
| 119 | + pshard[si] = alloc_pshard(sch, si, scx_shard_node[si]); | |
| 120 | + if (!pshard[si]) { | |
| 121 | + while (--si >= 0) | |
| 122 | + free_pshard(pshard[si]); | |
| 123 | + kfree(pshard); | |
| 124 | + return -ENOMEM; | |
| 125 | + } | |
| 126 | + } | |
| 127 | + | |
| 128 | + sch->nr_pshards = scx_nr_cid_shards; | |
| 129 | + /* | |
| 130 | + * Publish only after every entry is built so a reader observing | |
| 131 | + * @sch->pshard never sees a partially-filled array. Pair the store | |
| 132 | + * with a barrier and READ_ONCE() on the read side. | |
| 133 | + */ | |
| 134 | + smp_wmb(); | |
| 135 | + WRITE_ONCE(sch->pshard, pshard); | |
| 136 | + return 0; | |
| 137 | +} | |
| 138 | + | |
| 85 | 139 | static DECLARE_WAIT_QUEUE_HEAD(scx_unlink_waitq); |
| 86 | 140 | |
| 87 | 141 | void drain_descendants(struct scx_sched *sch) |
| @@ -24,6 +24,8 @@ void drain_descendants(struct scx_sched *sch); · 23 unmodified lines | ||
| 24 | 24 | void scx_sub_disable(struct scx_sched *sch); |
| 25 | 25 | void scx_sub_enable_workfn(struct kthread_work *work); |
| 26 | 26 | bool scx_bpf_sub_dispatch(u64 cgroup_id, const struct bpf_prog_aux *aux); |
| 27 | +void scx_free_pshards(struct scx_sched *sch); | |
| 28 | +s32 scx_alloc_pshards(struct scx_sched *sch); | |
| 27 | 29 | |
| 28 | 30 | static inline const char *sch_cgrp_path(struct scx_sched *sch) |
| 29 | 31 | { |
| @@ -39,6 +41,8 @@ static inline const char *sch_cgrp_path(struct scx_sched *sch) { return "/"; } · 9 unmodified lines | ||
| 39 | 41 | static inline void set_cgroup_sched(struct cgroup *cgrp, struct scx_sched *sch) {} |
| 40 | 42 | static inline void drain_descendants(struct scx_sched *sch) { } |
| 41 | 43 | static inline void scx_sub_disable(struct scx_sched *sch) { } |
| 44 | +static inline void scx_free_pshards(struct scx_sched *sch) {} | |
| 45 | +static inline s32 scx_alloc_pshards(struct scx_sched *sch) { return 0; } | |
| 42 | 46 | |
| 43 | 47 | #endif /* CONFIG_EXT_SUB_SCHED */ |
| 44 | 48 | |