diff options
| author | Tejun Heo <tj@kernel.org> | 2026-07-16 09:40:16 -1000 |
|---|---|---|
| committer | Tejun Heo <tj@kernel.org> | 2026-07-16 09:40:16 -1000 |
| commit | 6486f6d8f5455bc7fa898d5ee432f8c0f7f9ec20 (patch) | |
| tree | 4fa60a3ebe2e58c400a02e8132c8831646101bd6 /kernel/cgroup | |
| parent | 5fe830287eb12af7212e03f6b8ba9f7eca51f06e (diff) | |
| parent | 98149f5425306cf0a1ed472b216da985b25350ff (diff) | |
| download | linux-next-6486f6d8f5455bc7fa898d5ee432f8c0f7f9ec20.tar.gz linux-next-6486f6d8f5455bc7fa898d5ee432f8c0f7f9ec20.zip | |
Merge branch 'for-7.3' into for-next
Diffstat (limited to 'kernel/cgroup')
| -rw-r--r-- | kernel/cgroup/cpuset.c | 131 |
1 files changed, 78 insertions, 53 deletions
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 0d380ad94631..4549a226f6c9 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -366,10 +366,12 @@ static struct { bool cpus_updated; bool mems_updated; bool task_work_queued; + bool many_dest_cs; /* Have many destination cpusets */ struct cpuset *old_cs; /* Source cpuset */ nodemask_t nodemask_to; } attach_ctx; static LLIST_HEAD(src_cs_head); +static LLIST_HEAD(dst_cs_head); /* * Wait if task attach is in progress until it is done and then acquire @@ -3052,6 +3054,9 @@ static int cpuset_can_attach_check(struct cpuset *cs, struct cpuset *oldcs, if (!llist_on_list(&oldcs->attach_node)) llist_add(&oldcs->attach_node, &src_cs_head); + if (!llist_on_list(&cs->attach_node)) + llist_add(&cs->attach_node, &dst_cs_head); + cpus_updated = !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus); mems_updated = !nodes_equal(cs->effective_mems, oldcs->effective_mems); @@ -3080,35 +3085,31 @@ static int cpuset_can_attach_check(struct cpuset *cs, struct cpuset *oldcs, return 0; } -static int cpuset_reserve_dl_bw(struct cpuset *cs) +static int cpuset_reserve_dl_bw(void) { + struct cpuset *cs; int cpu, ret; - if (!cs->sum_migrate_dl_bw) - return 0; + llist_for_each_entry(cs, dst_cs_head.first, attach_node) { + if (!cs->sum_migrate_dl_bw) + continue; - cpu = cpumask_any_and(cpu_active_mask, cs->effective_cpus); - if (unlikely(cpu >= nr_cpu_ids)) - return -EINVAL; + cpu = cpumask_any_and(cpu_active_mask, cs->effective_cpus); + if (unlikely(cpu >= nr_cpu_ids)) + return -EINVAL; - ret = dl_bw_alloc(cpu, cs->sum_migrate_dl_bw); - if (ret) - return ret; + ret = dl_bw_alloc(cpu, cs->sum_migrate_dl_bw); + if (ret) + return ret; - cs->dl_bw_cpu = cpu; + cs->dl_bw_cpu = cpu; + } return 0; } -static void reset_migrate_dl_data(struct cpuset *cs) -{ - cs->nr_migrate_dl_tasks = 0; - cs->sum_migrate_dl_bw = 0; - cs->dl_bw_cpu = -1; -} - /* * Clear and optionally apply (@cancel is false) the attach related data in the - * source cpusets. + * source or destination cpuset. */ static void clear_attach_data(struct llist_head *head, bool cancel) { @@ -3120,7 +3121,11 @@ static void clear_attach_data(struct llist_head *head, bool cancel) if (cs->nr_migrate_dl_tasks) { if (!cancel) atomic_add(cs->nr_migrate_dl_tasks, &cs->nr_deadline_tasks); + else if (cs->dl_bw_cpu >= 0) /* && cancel */ + dl_bw_free(cs->dl_bw_cpu, cs->sum_migrate_dl_bw); cs->nr_migrate_dl_tasks = 0; + cs->sum_migrate_dl_bw = 0; + cs->dl_bw_cpu = -1; } } } @@ -3134,19 +3139,12 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) bool setsched_check; int ret; - /* used later by cpuset_attach() */ - attach_ctx.old_cs = task_cs(cgroup_taskset_first(tset, &css)); - oldcs = attach_ctx.old_cs; - cs = css_cs(css); - + cs = oldcs = NULL; mutex_lock(&cpuset_mutex); + attach_ctx.old_cs = NULL; /* Used later in cpuset_attach_task() */ attach_ctx.cpus_updated = false; attach_ctx.mems_updated = false; - - /* Check to see if task is allowed in the cpuset */ - ret = cpuset_can_attach_check(cs, oldcs, &setsched_check); - if (ret) - goto out_unlock; + attach_ctx.many_dest_cs = false; /* * The attach_ctx.old_cs is used mainly by cpuset_migrate_mm() to get @@ -3164,17 +3162,33 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) * of child cpusets must always be a subset of the parent. So no real * page migration will be necessary no matter which child cpuset is * selected as attach_ctx.old_cs. + * + * For a v2 threaded subtree where cpuset isn't enabled in some of the + * cgroups, it is possible that oldcs == cs for some of the tasks. + * In this case, we can skip checking on those tasks as there is no + * actual migration wrt cpuset. */ cgroup_taskset_for_each(task, css, tset) { + struct cpuset *new_cs = css_cs(css); struct cpuset *new_oldcs = task_cs(task); - if (new_oldcs != oldcs) { + if ((new_oldcs != oldcs) || (new_cs != cs)) { + if (cs && (new_cs != cs)) + attach_ctx.many_dest_cs = true; + cs = new_cs; oldcs = new_oldcs; + if (oldcs == cs) + continue; + if (!attach_ctx.old_cs) + attach_ctx.old_cs = oldcs; ret = cpuset_can_attach_check(cs, oldcs, &setsched_check); if (ret) goto out_unlock; } + if (oldcs == cs) + continue; + ret = task_can_attach(task); if (ret) goto out_unlock; @@ -3202,12 +3216,28 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) } } - ret = cpuset_reserve_dl_bw(cs); + /* + * The only case where there are multiple destination cpusets for + * task migration is when enabling a v2 cpuset controllers where + * tasks will be migrated to multiple child cpusets from a parent + * cpuset with the same effective CPUs and memory nodes. IOW, + * both attach_cpus_updated and attach_mems_updated should be false. + * If not, it is a condition that the current code cannot handle. + * Print a warning and abort the attach operation as further code + * change may be needed. + */ + if (WARN_ON_ONCE(attach_ctx.many_dest_cs && (!cpuset_v2() || + attach_ctx.cpus_updated || attach_ctx.mems_updated))) { + ret = -EINVAL; + goto out_unlock; + } + + ret = cpuset_reserve_dl_bw(); out_unlock: if (ret) { - reset_migrate_dl_data(cs); /* Destination cpuset only */ clear_attach_data(&src_cs_head, true); + clear_attach_data(&dst_cs_head, true); } else { attach_ctx.in_progress++; } @@ -3218,22 +3248,10 @@ out_unlock: static void cpuset_cancel_attach(struct cgroup_taskset *tset) { - struct cgroup_subsys_state *css; - struct cpuset *cs; - - cgroup_taskset_first(tset, &css); - cs = css_cs(css); - mutex_lock(&cpuset_mutex); dec_attach_in_progress_locked(); clear_attach_data(&src_cs_head, true); - - if (cs->dl_bw_cpu >= 0) - dl_bw_free(cs->dl_bw_cpu, cs->sum_migrate_dl_bw); - - if (cs->nr_migrate_dl_tasks) - reset_migrate_dl_data(cs); - + clear_attach_data(&dst_cs_head, true); mutex_unlock(&cpuset_mutex); } @@ -3313,28 +3331,35 @@ static void cpuset_attach(struct cgroup_taskset *tset) guarantee_online_mems(cs, &attach_ctx.nodemask_to); /* + * attach_ctx.old_cs can only be NULL if no task is actually migrating. + * This is highly unlikely. If it happens at all, we can skip task + * iteration and setting old_mems_allowed. + */ + if (unlikely(!attach_ctx.old_cs)) + goto out; + + /* * In the default hierarchy, enabling cpuset in the child cgroups * will trigger a cpuset_attach() call with no change in effective cpus * and mems. In that case, we can optimize out by skipping the task - * iteration and update. + * iteration and the destination cpuset list is iterated to set + * old_mems_allowed. */ - if (cpuset_v2() && !attach_ctx.cpus_updated && !attach_ctx.mems_updated) + if (cpuset_v2() && !attach_ctx.cpus_updated && !attach_ctx.mems_updated) { + llist_for_each_entry(cs, dst_cs_head.first, attach_node) + cs->old_mems_allowed = attach_ctx.nodemask_to; goto out; + } cgroup_taskset_for_each(task, css, tset) cpuset_attach_task(cs, task); -out: if (attach_ctx.task_work_queued) schedule_flush_migrate_mm(); cs->old_mems_allowed = attach_ctx.nodemask_to; - - if (cs->nr_migrate_dl_tasks) { - atomic_add(cs->nr_migrate_dl_tasks, &cs->nr_deadline_tasks); - reset_migrate_dl_data(cs); - } - +out: clear_attach_data(&src_cs_head, false); + clear_attach_data(&dst_cs_head, false); dec_attach_in_progress_locked(); mutex_unlock(&cpuset_mutex); |
