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 | free_pnode(sch->pnode[node]); | 4681 | free_pnode(sch->pnode[node]); |
| 4682 | kfree(sch->pnode); | 4682 | kfree(sch->pnode); |
| 4683 | 4683 | ||
| 4684 | scx_free_pshards(sch); | ||
| 4685 | |||
| 4684 | rhashtable_walk_enter(&sch->dsq_hash, &rht_iter); | 4686 | rhashtable_walk_enter(&sch->dsq_hash, &rht_iter); |
| 4685 | do { | 4687 | do { |
| 4686 | rhashtable_walk_start(&rht_iter); | 4688 | rhashtable_walk_start(&rht_iter); |
| @@ -6763,6 +6765,12 @@ static void scx_root_enable_workfn(struct kthread_work *work) · 2076 unmodified lines | |||
| 6763 | goto err_disable; | 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 | if (sch->ops.init) { | 6774 | if (sch->ops.init) { |
| 6767 | ret = SCX_CALL_OP_RET(sch, init, NULL); | 6775 | ret = SCX_CALL_OP_RET(sch, init, NULL); |
| 6768 | if (ret) { | 6776 | if (ret) { |
| @@ -1183,6 +1183,12 @@ struct scx_sched_pnode { · 1182 unmodified lines | |||
| 1183 | struct scx_dispatch_q global_dsq; | 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 | struct scx_sched { | 1192 | struct scx_sched { |
| 1187 | /* | 1193 | /* |
| 1188 | * cpu-form and cid-form ops share field offsets up to .priv (verified | 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 | struct rhashtable dsq_hash; | 1237 | struct rhashtable dsq_hash; |
| 1232 | struct scx_sched_pnode **pnode; | 1238 | struct scx_sched_pnode **pnode; |
| 1239 | #ifdef CONFIG_EXT_SUB_SCHED | ||
| 1240 | struct scx_pshard **pshard; /* indexed by shard_idx */ | ||
| 1241 | #endif | ||
| 1233 | struct scx_sched_pcpu __percpu *pcpu; | 1242 | struct scx_sched_pcpu __percpu *pcpu; |
| 1234 | 1243 | ||
| 1235 | u64 slice_dfl; | 1244 | u64 slice_dfl; |
| @@ -1245,6 +1254,15 @@ struct scx_sched { · 9 unmodified lines | |||
| 1245 | u32 dsp_max_batch; | 1254 | u32 dsp_max_batch; |
| 1246 | s32 level; | 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 | * Updates to the following warned bitfields can race causing RMW issues | 1267 | * Updates to the following warned bitfields can race causing RMW issues |
| 1250 | * but it doesn't really matter. | 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 | rcu_assign_pointer(pos->scx_sched, sch); | 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 | static DECLARE_WAIT_QUEUE_HEAD(scx_unlink_waitq); | 139 | static DECLARE_WAIT_QUEUE_HEAD(scx_unlink_waitq); |
| 86 | 140 | ||
| 87 | void drain_descendants(struct scx_sched *sch) | 141 | void drain_descendants(struct scx_sched *sch) |
| @@ -24,6 +24,8 @@ void drain_descendants(struct scx_sched *sch); · 23 unmodified lines | |||
| 24 | void scx_sub_disable(struct scx_sched *sch); | 24 | void scx_sub_disable(struct scx_sched *sch); |
| 25 | void scx_sub_enable_workfn(struct kthread_work *work); | 25 | void scx_sub_enable_workfn(struct kthread_work *work); |
| 26 | bool scx_bpf_sub_dispatch(u64 cgroup_id, const struct bpf_prog_aux *aux); | 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 | static inline const char *sch_cgrp_path(struct scx_sched *sch) | 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 | static inline void set_cgroup_sched(struct cgroup *cgrp, struct scx_sched *sch) {} | 41 | static inline void set_cgroup_sched(struct cgroup *cgrp, struct scx_sched *sch) {} |
| 40 | static inline void drain_descendants(struct scx_sched *sch) { } | 42 | static inline void drain_descendants(struct scx_sched *sch) { } |
| 41 | static inline void scx_sub_disable(struct scx_sched *sch) { } | 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 | #endif /* CONFIG_EXT_SUB_SCHED */ | 47 | #endif /* CONFIG_EXT_SUB_SCHED */ |
| 44 | 48 | ||