History

sched/fair: Allocate cfs_tg_state with percpu allocator

To remove the cfs_rq pointer array in task_group, allocate the combined cfs_rq and sched_entity using the per-cpu allocator. This patch implements the following: - Changes task_group->cfs_rq from 'struct cfs_rq **' to 'struct cfs_rq __percpu *'. - Updates memory allocation in alloc_fair_sched_group() and free_fair_sched_group() to use alloc_percpu() and free_percpu() respectively. - Uses the inline accessor tg_cfs_rq(tg, cpu) with per_cpu_ptr() to retrieve the pointer to cfs_rq for the given task group and CPU. - Replaces direct accesses tg->cfs_rq[cpu] with calls to the new tg_cfs_rq(tg, cpu) helper. - Handles the root_task_group: since struct rq is already a per-cpu variable (runqueues), its embedded cfs_rq (rq->cfs) is also per-cpu. Therefore, we assign root_task_group.cfs_rq = &runqueues.cfs. - Cleanup the code in initializing the root task group. This change places each CPU's cfs_rq and sched_entity in its local per-cpu memory area to remove the per-task_group pointer arrays.

Zecheng Li committed 5 months ago

Commit b8fea7a1 parent 89e1f673 files · +45 −58Browse files

Unified · Split

3 files
  1. kernel/sched/core.c +13 −22
  2. kernel/sched/fair.c +22 −32
  3. kernel/sched/sched.h +10 −4
kernel/sched/core.c+13−22
@@ -8907,7 +8907,7 @@ static struct kmem_cache *task_group_cache __ro_after_init; · 8906 unmodified lines
8907 8907
8908void __init sched_init(void)8908void __init sched_init(void)
8909{8909{
8910 unsigned long ptr = 0;8910 unsigned long __maybe_unused ptr = 0;
8911 int i;8911 int i;
8912 8912
8913 /* Make sure the linker didn't screw up */8913 /* Make sure the linker didn't screw up */
@@ -8923,33 +8923,24 @@ void __init sched_init(void) · 9 unmodified lines
8923 wait_bit_init();8923 wait_bit_init();
8924 8924
8925#ifdef CONFIG_FAIR_GROUP_SCHED8925#ifdef CONFIG_FAIR_GROUP_SCHED
8926 ptr += nr_cpu_ids * sizeof(void **);8926 root_task_group.cfs_rq = &runqueues.cfs;
8927#endif
8928#ifdef CONFIG_RT_GROUP_SCHED
8929 ptr += 2 * nr_cpu_ids * sizeof(void **);
8930#endif
8931 if (ptr) {
8932 ptr = (unsigned long)kzalloc(ptr, GFP_NOWAIT);
8933 8927
8934#ifdef CONFIG_FAIR_GROUP_SCHED8928 root_task_group.shares = ROOT_TASK_GROUP_LOAD;
8935 root_task_group.cfs_rq = (struct cfs_rq **)ptr;8929 init_cfs_bandwidth(&root_task_group.cfs_bandwidth, NULL);
8936 ptr += nr_cpu_ids * sizeof(void **);
8937
8938 root_task_group.shares = ROOT_TASK_GROUP_LOAD;
8939 init_cfs_bandwidth(&root_task_group.cfs_bandwidth, NULL);
8940#endif /* CONFIG_FAIR_GROUP_SCHED */8930#endif /* CONFIG_FAIR_GROUP_SCHED */
8941#ifdef CONFIG_EXT_GROUP_SCHED8931#ifdef CONFIG_EXT_GROUP_SCHED
8942 scx_tg_init(&root_task_group);8932 scx_tg_init(&root_task_group);
8943#endif /* CONFIG_EXT_GROUP_SCHED */8933#endif /* CONFIG_EXT_GROUP_SCHED */
8944#ifdef CONFIG_RT_GROUP_SCHED8934#ifdef CONFIG_RT_GROUP_SCHED
8945 root_task_group.rt_se = (struct sched_rt_entity **)ptr;8935 ptr += 2 * nr_cpu_ids * sizeof(void **);
8946 ptr += nr_cpu_ids * sizeof(void **);8936 ptr = (unsigned long)kzalloc(ptr, GFP_NOWAIT);
8937 root_task_group.rt_se = (struct sched_rt_entity **)ptr;
8938 ptr += nr_cpu_ids * sizeof(void **);
8947 8939
8948 root_task_group.rt_rq = (struct rt_rq **)ptr;8940 root_task_group.rt_rq = (struct rt_rq **)ptr;
8949 ptr += nr_cpu_ids * sizeof(void **);8941 ptr += nr_cpu_ids * sizeof(void **);
8950 8942
8951#endif /* CONFIG_RT_GROUP_SCHED */8943#endif /* CONFIG_RT_GROUP_SCHED */
8952 }
8953 8944
8954 init_defrootdomain();8945 init_defrootdomain();
8955 8946
@@ -9864,7 +9855,7 @@ static int tg_set_cfs_bandwidth(struct task_group *tg, · 908 unmodified lines
9864 }9855 }
9865 9856
9866 for_each_online_cpu(i) {9857 for_each_online_cpu(i) {
9867 struct cfs_rq *cfs_rq = tg->cfs_rq[i];9858 struct cfs_rq *cfs_rq = tg_cfs_rq(tg, i);
9868 struct rq *rq = cfs_rq->rq;9859 struct rq *rq = cfs_rq->rq;
9869 9860
9870 guard(rq_lock_irq)(rq);9861 guard(rq_lock_irq)(rq);
@@ -10032,7 +10023,7 @@ static u64 throttled_time_self(struct task_group *tg) · 161 unmodified lines
10032 u64 total = 0;10023 u64 total = 0;
10033 10024
10034 for_each_possible_cpu(i) {10025 for_each_possible_cpu(i) {
10035 total += READ_ONCE(tg->cfs_rq[i]->throttled_clock_self_time);10026 total += READ_ONCE(tg_cfs_rq(tg, i)->throttled_clock_self_time);
10036 }10027 }
10037 10028
10038 return total;10029 return total;
kernel/sched/fair.c+22−32
@@ -334,7 +334,7 @@ static inline bool list_add_leaf_cfs_rq(struct cfs_rq *cfs_rq) · 333 unmodified lines
334 * to a tree or when we reach the top of the tree334 * to a tree or when we reach the top of the tree
335 */335 */
336 if (cfs_rq->tg->parent &&336 if (cfs_rq->tg->parent &&
337 cfs_rq->tg->parent->cfs_rq[cpu]->on_list) {337 tg_cfs_rq(cfs_rq->tg->parent, cpu)->on_list) {
338 /*338 /*
339 * If parent is already on the list, we add the child339 * If parent is already on the list, we add the child
340 * just before. Thanks to circular linked property of340 * just before. Thanks to circular linked property of
@@ -342,7 +342,7 @@ static inline bool list_add_leaf_cfs_rq(struct cfs_rq *cfs_rq) · 1 unmodified line
342 * of the list that starts by parent.342 * of the list that starts by parent.
343 */343 */
344 list_add_tail_rcu(&cfs_rq->leaf_cfs_rq_list,344 list_add_tail_rcu(&cfs_rq->leaf_cfs_rq_list,
345 &(cfs_rq->tg->parent->cfs_rq[cpu]->leaf_cfs_rq_list));345 &(tg_cfs_rq(cfs_rq->tg->parent, cpu)->leaf_cfs_rq_list));
346 /*346 /*
347 * The branch is now connected to its tree so we can347 * The branch is now connected to its tree so we can
348 * reset tmp_alone_branch to the beginning of the348 * reset tmp_alone_branch to the beginning of the
@@ -5037,7 +5037,7 @@ static void __maybe_unused clear_tg_offline_cfs_rqs(struct rq *rq) · 4688 unmodified lines
5037 5037
5038 rcu_read_lock();5038 rcu_read_lock();
5039 list_for_each_entry_rcu(tg, &task_groups, list) {5039 list_for_each_entry_rcu(tg, &task_groups, list) {
5040 struct cfs_rq *cfs_rq = tg->cfs_rq[cpu_of(rq)];5040 struct cfs_rq *cfs_rq = tg_cfs_rq(tg, cpu_of(rq));
5041 5041
5042 clear_tg_load_avg(cfs_rq);5042 clear_tg_load_avg(cfs_rq);
5043 }5043 }
@@ -6594,7 +6594,7 @@ static inline int throttled_hierarchy(struct cfs_rq *cfs_rq) · 1550 unmodified lines
6594 6594
6595static inline int lb_throttled_hierarchy(struct task_struct *p, int dst_cpu)6595static inline int lb_throttled_hierarchy(struct task_struct *p, int dst_cpu)
6596{6596{
6597 return throttled_hierarchy(task_group(p)->cfs_rq[dst_cpu]);6597 return throttled_hierarchy(tg_cfs_rq(task_group(p), dst_cpu));
6598}6598}
6599 6599
6600static inline bool task_is_throttled(struct task_struct *p)6600static inline bool task_is_throttled(struct task_struct *p)
@@ -6740,7 +6740,7 @@ static void enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags); · 139 unmodified lines
6740static int tg_unthrottle_up(struct task_group *tg, void *data)6740static int tg_unthrottle_up(struct task_group *tg, void *data)
6741{6741{
6742 struct rq *rq = data;6742 struct rq *rq = data;
6743 struct cfs_rq *cfs_rq = tg->cfs_rq[cpu_of(rq)];6743 struct cfs_rq *cfs_rq = tg_cfs_rq(tg, cpu_of(rq));
6744 struct task_struct *p, *tmp;6744 struct task_struct *p, *tmp;
6745 6745
6746 if (--cfs_rq->throttle_count)6746 if (--cfs_rq->throttle_count)
@@ -6811,7 +6811,7 @@ static void record_throttle_clock(struct cfs_rq *cfs_rq) · 64 unmodified lines
6811static int tg_throttle_down(struct task_group *tg, void *data)6811static int tg_throttle_down(struct task_group *tg, void *data)
6812{6812{
6813 struct rq *rq = data;6813 struct rq *rq = data;
6814 struct cfs_rq *cfs_rq = tg->cfs_rq[cpu_of(rq)];6814 struct cfs_rq *cfs_rq = tg_cfs_rq(tg, cpu_of(rq));
6815 6815
6816 if (cfs_rq->throttle_count++)6816 if (cfs_rq->throttle_count++)
6817 return 0;6817 return 0;
@@ -7285,8 +7285,8 @@ static void sync_throttle(struct task_group *tg, int cpu) · 467 unmodified lines
7285 if (!tg->parent)7285 if (!tg->parent)
7286 return;7286 return;
7287 7287
7288 cfs_rq = tg->cfs_rq[cpu];7288 cfs_rq = tg_cfs_rq(tg, cpu);
7289 pcfs_rq = tg->parent->cfs_rq[cpu];7289 pcfs_rq = tg_cfs_rq(tg->parent, cpu);
7290 7290
7291 cfs_rq->throttle_count = pcfs_rq->throttle_count;7291 cfs_rq->throttle_count = pcfs_rq->throttle_count;
7292 cfs_rq->throttled_clock_pelt = rq_clock_pelt(cpu_rq(cpu));7292 cfs_rq->throttled_clock_pelt = rq_clock_pelt(cpu_rq(cpu));
@@ -7478,7 +7478,7 @@ static void __maybe_unused update_runtime_enabled(struct rq *rq) · 185 unmodified lines
7478 rcu_read_lock();7478 rcu_read_lock();
7479 list_for_each_entry_rcu(tg, &task_groups, list) {7479 list_for_each_entry_rcu(tg, &task_groups, list) {
7480 struct cfs_bandwidth *cfs_b = &tg->cfs_bandwidth;7480 struct cfs_bandwidth *cfs_b = &tg->cfs_bandwidth;
7481 struct cfs_rq *cfs_rq = tg->cfs_rq[cpu_of(rq)];7481 struct cfs_rq *cfs_rq = tg_cfs_rq(tg, cpu_of(rq));
7482 7482
7483 raw_spin_lock(&cfs_b->lock);7483 raw_spin_lock(&cfs_b->lock);
7484 cfs_rq->runtime_enabled = cfs_b->quota != RUNTIME_INF;7484 cfs_rq->runtime_enabled = cfs_b->quota != RUNTIME_INF;
@@ -7507,7 +7507,7 @@ static void __maybe_unused unthrottle_offline_cfs_rqs(struct rq *rq) · 22 unmodified lines
7507 7507
7508 rcu_read_lock();7508 rcu_read_lock();
7509 list_for_each_entry_rcu(tg, &task_groups, list) {7509 list_for_each_entry_rcu(tg, &task_groups, list) {
7510 struct cfs_rq *cfs_rq = tg->cfs_rq[cpu_of(rq)];7510 struct cfs_rq *cfs_rq = tg_cfs_rq(tg, cpu_of(rq));
7511 7511
7512 if (!cfs_rq->runtime_enabled)7512 if (!cfs_rq->runtime_enabled)
7513 continue;7513 continue;
@@ -10382,7 +10382,7 @@ static inline int task_is_ineligible_on_dst_cpu(struct task_struct *p, int dest_ · 2868 unmodified lines
10382 struct cfs_rq *dst_cfs_rq;10382 struct cfs_rq *dst_cfs_rq;
10383 10383
10384#ifdef CONFIG_FAIR_GROUP_SCHED10384#ifdef CONFIG_FAIR_GROUP_SCHED
10385 dst_cfs_rq = task_group(p)->cfs_rq[dest_cpu];10385 dst_cfs_rq = tg_cfs_rq(task_group(p), dest_cpu);
10386#else10386#else
10387 dst_cfs_rq = &cpu_rq(dest_cpu)->cfs;10387 dst_cfs_rq = &cpu_rq(dest_cpu)->cfs;
10388#endif10388#endif
@@ -14812,7 +14812,7 @@ static int task_is_throttled_fair(struct task_struct *p, int cpu) · 4423 unmodified lines
14812 struct cfs_rq *cfs_rq;14812 struct cfs_rq *cfs_rq;
14813 14813
14814#ifdef CONFIG_FAIR_GROUP_SCHED14814#ifdef CONFIG_FAIR_GROUP_SCHED
14815 cfs_rq = task_group(p)->cfs_rq[cpu];14815 cfs_rq = tg_cfs_rq(task_group(p), cpu);
14816#else14816#else
14817 cfs_rq = &cpu_rq(cpu)->cfs;14817 cfs_rq = &cpu_rq(cpu)->cfs;
14818#endif14818#endif
@@ -15076,39 +15076,31 @@ static void task_change_group_fair(struct task_struct *p) · 257 unmodified lines
15076 15076
15077void free_fair_sched_group(struct task_group *tg)15077void free_fair_sched_group(struct task_group *tg)
15078{15078{
15079 int i;15079 free_percpu(tg->cfs_rq);
15080
15081 for_each_possible_cpu(i) {
15082 if (tg->cfs_rq)
15083 kfree(tg->cfs_rq[i]);
15084 }
15085
15086 kfree(tg->cfs_rq);
15087}15080}
15088 15081
15089int alloc_fair_sched_group(struct task_group *tg, struct task_group *parent)15082int alloc_fair_sched_group(struct task_group *tg, struct task_group *parent)
15090{15083{
15091 struct cfs_tg_state *state;15084 struct cfs_tg_state __percpu *state;
15092 struct sched_entity *se;15085 struct sched_entity *se;
15093 struct cfs_rq *cfs_rq;15086 struct cfs_rq *cfs_rq;
15094 int i;15087 int i;
15095 15088
15096 tg->cfs_rq = kzalloc_objs(cfs_rq, nr_cpu_ids);15089 state = alloc_percpu_gfp(struct cfs_tg_state, GFP_KERNEL);
15097 if (!tg->cfs_rq)15090 if (!state)
15098 goto err;15091 goto err;
15099 15092
15093 tg->cfs_rq = &state->cfs_rq;
15100 tg->shares = NICE_0_LOAD;15094 tg->shares = NICE_0_LOAD;
15101 15095
15102 init_cfs_bandwidth(tg_cfs_bandwidth(tg), tg_cfs_bandwidth(parent));15096 init_cfs_bandwidth(tg_cfs_bandwidth(tg), tg_cfs_bandwidth(parent));
15103 15097
15104 for_each_possible_cpu(i) {15098 for_each_possible_cpu(i) {
15105 state = kzalloc_node(sizeof(*state),15099 cfs_rq = tg_cfs_rq(tg, i);
15106 GFP_KERNEL, cpu_to_node(i));15100 if (!cfs_rq)
15107 if (!state)
15108 goto err;15101 goto err;
15109 15102
15110 cfs_rq = &state->cfs_rq;15103 se = tg_se(tg, i);
15111 se = &state->se;
15112 init_cfs_rq(cfs_rq);15104 init_cfs_rq(cfs_rq);
15113 init_tg_cfs_entry(tg, cfs_rq, se, i, tg_se(parent, i));15105 init_tg_cfs_entry(tg, cfs_rq, se, i, tg_se(parent, i));
15114 init_entity_runnable_average(se);15106 init_entity_runnable_average(se);
@@ -15145,7 +15137,7 @@ void unregister_fair_sched_group(struct task_group *tg) · 30 unmodified lines
15145 destroy_cfs_bandwidth(tg_cfs_bandwidth(tg));15137 destroy_cfs_bandwidth(tg_cfs_bandwidth(tg));
15146 15138
15147 for_each_possible_cpu(cpu) {15139 for_each_possible_cpu(cpu) {
15148 struct cfs_rq *cfs_rq = tg->cfs_rq[cpu];15140 struct cfs_rq *cfs_rq = tg_cfs_rq(tg, cpu);
15149 struct sched_entity *se = tg_se(tg, cpu);15141 struct sched_entity *se = tg_se(tg, cpu);
15150 struct rq *rq = cpu_rq(cpu);15142 struct rq *rq = cpu_rq(cpu);
15151 15143
@@ -15182,8 +15174,6 @@ void init_tg_cfs_entry(struct task_group *tg, struct cfs_rq *cfs_rq, · 30 unmodified lines
15182 cfs_rq->rq = rq;15174 cfs_rq->rq = rq;
15183 init_cfs_rq_runtime(cfs_rq);15175 init_cfs_rq_runtime(cfs_rq);
15184 15176
15185 tg->cfs_rq[cpu] = cfs_rq;
15186
15187 /* se could be NULL for root_task_group */15177 /* se could be NULL for root_task_group */
15188 if (!se)15178 if (!se)
15189 return;15179 return;
@@ -15276,7 +15266,7 @@ int sched_group_set_idle(struct task_group *tg, long idle) · 86 unmodified lines
15276 for_each_possible_cpu(i) {15266 for_each_possible_cpu(i) {
15277 struct rq *rq = cpu_rq(i);15267 struct rq *rq = cpu_rq(i);
15278 struct sched_entity *se = tg_se(tg, i);15268 struct sched_entity *se = tg_se(tg, i);
15279 struct cfs_rq *grp_cfs_rq = tg->cfs_rq[i];15269 struct cfs_rq *grp_cfs_rq = tg_cfs_rq(tg, i);
15280 bool was_idle = cfs_rq_is_idle(grp_cfs_rq);15270 bool was_idle = cfs_rq_is_idle(grp_cfs_rq);
15281 long idle_task_delta;15271 long idle_task_delta;
15282 struct rq_flags rf;15272 struct rq_flags rf;
kernel/sched/sched.h+10−4
@@ -485,7 +485,7 @@ struct task_group { · 484 unmodified lines
485 485
486#ifdef CONFIG_FAIR_GROUP_SCHED486#ifdef CONFIG_FAIR_GROUP_SCHED
487 /* runqueue "owned" by this group on each CPU */487 /* runqueue "owned" by this group on each CPU */
488 struct cfs_rq **cfs_rq;488 struct cfs_rq __percpu *cfs_rq;
489 unsigned long shares;489 unsigned long shares;
490 /*490 /*
491 * load_avg can be heavily contended at clock tick time, so put491 * load_avg can be heavily contended at clock tick time, so put
@@ -2304,6 +2304,12 @@ struct cfs_tg_state { · 1812 unmodified lines
2304 struct sched_statistics stats;2304 struct sched_statistics stats;
2305} __no_randomize_layout;2305} __no_randomize_layout;
2306 2306
2307/* Access a specific CPU's cfs_rq from a task group */
2308static inline struct cfs_rq *tg_cfs_rq(struct task_group *tg, int cpu)
2309{
2310 return per_cpu_ptr(tg->cfs_rq, cpu);
2311}
2312
2307static inline struct sched_entity *tg_se(struct task_group *tg, int cpu)2313static inline struct sched_entity *tg_se(struct task_group *tg, int cpu)
2308{2314{
2309 struct cfs_tg_state *state;2315 struct cfs_tg_state *state;
@@ -2311,7 +2317,7 @@ static inline struct sched_entity *tg_se(struct task_group *tg, int cpu) · 1 unmodified line
2311 if (is_root_task_group(tg))2317 if (is_root_task_group(tg))
2312 return NULL;2318 return NULL;
2313 2319
2314 state = container_of(tg->cfs_rq[cpu], struct cfs_tg_state, cfs_rq);2320 state = container_of(tg_cfs_rq(tg, cpu), struct cfs_tg_state, cfs_rq);
2315 return &state->se;2321 return &state->se;
2316}2322}
2317 2323
@@ -2335,8 +2341,8 @@ static inline void set_task_rq(struct task_struct *p, unsigned int cpu) · 17 unmodified lines
2335#endif2341#endif
2336 2342
2337#ifdef CONFIG_FAIR_GROUP_SCHED2343#ifdef CONFIG_FAIR_GROUP_SCHED
2338 set_task_rq_fair(&p->se, p->se.cfs_rq, tg->cfs_rq[cpu]);2344 set_task_rq_fair(&p->se, p->se.cfs_rq, tg_cfs_rq(tg, cpu));
2339 p->se.cfs_rq = tg->cfs_rq[cpu];2345 p->se.cfs_rq = tg_cfs_rq(tg, cpu);
2340 p->se.parent = tg_se(tg, cpu);2346 p->se.parent = tg_se(tg, cpu);
2341 p->se.depth = p->se.parent ? p->se.parent->depth + 1 : 0;2347 p->se.depth = p->se.parent ? p->se.parent->depth + 1 : 0;
2342#endif2348#endif