Skip to content

Commit 70cbac2

Browse files
committed
cpuset: use Union-Find to optimize the merging of cpumasks
jira NONE_AUTOMATION Rebuild_History Non-Buildable kernel-5.14.0-570.17.1.el9_6 commit-author Xavier <[email protected]> commit 8a895c2 The process of constructing scheduling domains involves multiple loops and repeated evaluations, leading to numerous redundant and ineffective assessments that impact code efficiency. Here, we use union-find to optimize the merging of cpumasks. By employing path compression and union by rank, we effectively reduce the number of lookups and merge comparisons. Signed-off-by: Xavier <[email protected]> Signed-off-by: Tejun Heo <[email protected]> (cherry picked from commit 8a895c2) Signed-off-by: Jonathan Maple <[email protected]>
1 parent e218d40 commit 70cbac2

File tree

1 file changed

+44
-70
lines changed

1 file changed

+44
-70
lines changed

kernel/cgroup/cpuset.c

Lines changed: 44 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include <linux/cgroup.h>
4747
#include <linux/wait.h>
4848
#include <linux/workqueue.h>
49+
#include <linux/union_find.h>
4950

5051
DEFINE_STATIC_KEY_FALSE(cpusets_pre_enable_key);
5152
DEFINE_STATIC_KEY_FALSE(cpusets_enabled_key);
@@ -175,9 +176,6 @@ struct cpuset {
175176
*/
176177
int attach_in_progress;
177178

178-
/* partition number for rebuild_sched_domains() */
179-
int pn;
180-
181179
/* for custom sched domain */
182180
int relax_domain_level;
183181

@@ -211,6 +209,9 @@ struct cpuset {
211209

212210
/* Remote partition silbling list anchored at remote_children */
213211
struct list_head remote_sibling;
212+
213+
/* Used to merge intersecting subsets for generate_sched_domains */
214+
struct uf_node node;
214215
};
215216

216217
/*
@@ -998,33 +999,31 @@ static inline int nr_cpusets(void)
998999
* were changed (added or removed.)
9991000
*
10001001
* Finding the best partition (set of domains):
1001-
* The triple nested loops below over i, j, k scan over the
1002-
* load balanced cpusets (using the array of cpuset pointers in
1003-
* csa[]) looking for pairs of cpusets that have overlapping
1004-
* cpus_allowed, but which don't have the same 'pn' partition
1005-
* number and gives them in the same partition number. It keeps
1006-
* looping on the 'restart' label until it can no longer find
1007-
* any such pairs.
1002+
* The double nested loops below over i, j scan over the load
1003+
* balanced cpusets (using the array of cpuset pointers in csa[])
1004+
* looking for pairs of cpusets that have overlapping cpus_allowed
1005+
* and merging them using a union-find algorithm.
1006+
*
1007+
* The union of the cpus_allowed masks from the set of all cpusets
1008+
* having the same root then form the one element of the partition
1009+
* (one sched domain) to be passed to partition_sched_domains().
10081010
*
1009-
* The union of the cpus_allowed masks from the set of
1010-
* all cpusets having the same 'pn' value then form the one
1011-
* element of the partition (one sched domain) to be passed to
1012-
* partition_sched_domains().
10131011
*/
10141012
static int generate_sched_domains(cpumask_var_t **domains,
10151013
struct sched_domain_attr **attributes)
10161014
{
10171015
struct cpuset *cp; /* top-down scan of cpusets */
10181016
struct cpuset **csa; /* array of all cpuset ptrs */
10191017
int csn; /* how many cpuset ptrs in csa so far */
1020-
int i, j, k; /* indices for partition finding loops */
1018+
int i, j; /* indices for partition finding loops */
10211019
cpumask_var_t *doms; /* resulting partition; i.e. sched domains */
10221020
struct sched_domain_attr *dattr; /* attributes for custom domains */
10231021
int ndoms = 0; /* number of sched domains in result */
10241022
int nslot; /* next empty doms[] struct cpumask slot */
10251023
struct cgroup_subsys_state *pos_css;
10261024
bool root_load_balance = is_sched_load_balance(&top_cpuset);
10271025
bool cgrpv2 = cgroup_subsys_on_dfl(cpuset_cgrp_subsys);
1026+
int nslot_update;
10281027

10291028
doms = NULL;
10301029
dattr = NULL;
@@ -1112,31 +1111,25 @@ static int generate_sched_domains(cpumask_var_t **domains,
11121111
if (root_load_balance && (csn == 1))
11131112
goto single_root_domain;
11141113

1115-
for (i = 0; i < csn; i++)
1116-
csa[i]->pn = i;
1117-
ndoms = csn;
1118-
1119-
restart:
1120-
/* Find the best partition (set of sched domains) */
1121-
for (i = 0; i < csn; i++) {
1122-
struct cpuset *a = csa[i];
1123-
int apn = a->pn;
1124-
1125-
for (j = 0; j < csn; j++) {
1126-
struct cpuset *b = csa[j];
1127-
int bpn = b->pn;
1128-
1129-
if (apn != bpn && cpusets_overlap(a, b)) {
1130-
for (k = 0; k < csn; k++) {
1131-
struct cpuset *c = csa[k];
1114+
if (!cgrpv2) {
1115+
for (i = 0; i < csn; i++)
1116+
uf_node_init(&csa[i]->node);
11321117

1133-
if (c->pn == bpn)
1134-
c->pn = apn;
1135-
}
1136-
ndoms--; /* one less element */
1137-
goto restart;
1118+
/* Merge overlapping cpusets */
1119+
for (i = 0; i < csn; i++) {
1120+
for (j = i + 1; j < csn; j++) {
1121+
if (cpusets_overlap(csa[i], csa[j]))
1122+
uf_union(&csa[i]->node, &csa[j]->node);
11381123
}
11391124
}
1125+
1126+
/* Count the total number of domains */
1127+
for (i = 0; i < csn; i++) {
1128+
if (uf_find(&csa[i]->node) == &csa[i]->node)
1129+
ndoms++;
1130+
}
1131+
} else {
1132+
ndoms = csn;
11401133
}
11411134

11421135
/*
@@ -1169,44 +1162,25 @@ static int generate_sched_domains(cpumask_var_t **domains,
11691162
}
11701163

11711164
for (nslot = 0, i = 0; i < csn; i++) {
1172-
struct cpuset *a = csa[i];
1173-
struct cpumask *dp;
1174-
int apn = a->pn;
1175-
1176-
if (apn < 0) {
1177-
/* Skip completed partitions */
1178-
continue;
1179-
}
1180-
1181-
dp = doms[nslot];
1182-
1183-
if (nslot == ndoms) {
1184-
static int warnings = 10;
1185-
if (warnings) {
1186-
pr_warn("rebuild_sched_domains confused: nslot %d, ndoms %d, csn %d, i %d, apn %d\n",
1187-
nslot, ndoms, csn, i, apn);
1188-
warnings--;
1189-
}
1190-
continue;
1191-
}
1192-
1193-
cpumask_clear(dp);
1194-
if (dattr)
1195-
*(dattr + nslot) = SD_ATTR_INIT;
1165+
nslot_update = 0;
11961166
for (j = i; j < csn; j++) {
1197-
struct cpuset *b = csa[j];
1198-
1199-
if (apn == b->pn) {
1200-
cpumask_or(dp, dp, b->effective_cpus);
1167+
if (uf_find(&csa[j]->node) == &csa[i]->node) {
1168+
struct cpumask *dp = doms[nslot];
1169+
1170+
if (i == j) {
1171+
nslot_update = 1;
1172+
cpumask_clear(dp);
1173+
if (dattr)
1174+
*(dattr + nslot) = SD_ATTR_INIT;
1175+
}
1176+
cpumask_or(dp, dp, csa[j]->effective_cpus);
12011177
cpumask_and(dp, dp, housekeeping_cpumask(HK_TYPE_DOMAIN));
12021178
if (dattr)
1203-
update_domain_attr_tree(dattr + nslot, b);
1204-
1205-
/* Done with this partition */
1206-
b->pn = -1;
1179+
update_domain_attr_tree(dattr + nslot, csa[j]);
12071180
}
12081181
}
1209-
nslot++;
1182+
if (nslot_update)
1183+
nslot++;
12101184
}
12111185
BUG_ON(nslot != ndoms);
12121186

0 commit comments

Comments
 (0)