Commit 8a895c2e authored by Xavier's avatar Xavier Committed by Tejun Heo

cpuset: use Union-Find to optimize the merging of cpumasks

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: default avatarXavier <xavier_qy@163.com>
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
parent 93c8332c
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#include <linux/cgroup.h> #include <linux/cgroup.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/union_find.h>
DEFINE_STATIC_KEY_FALSE(cpusets_pre_enable_key); DEFINE_STATIC_KEY_FALSE(cpusets_pre_enable_key);
DEFINE_STATIC_KEY_FALSE(cpusets_enabled_key); DEFINE_STATIC_KEY_FALSE(cpusets_enabled_key);
...@@ -173,9 +174,6 @@ struct cpuset { ...@@ -173,9 +174,6 @@ struct cpuset {
*/ */
int attach_in_progress; int attach_in_progress;
/* partition number for rebuild_sched_domains() */
int pn;
/* for custom sched domain */ /* for custom sched domain */
int relax_domain_level; int relax_domain_level;
...@@ -207,6 +205,9 @@ struct cpuset { ...@@ -207,6 +205,9 @@ struct cpuset {
/* Remote partition silbling list anchored at remote_children */ /* Remote partition silbling list anchored at remote_children */
struct list_head remote_sibling; struct list_head remote_sibling;
/* Used to merge intersecting subsets for generate_sched_domains */
struct uf_node node;
}; };
/* /*
...@@ -1007,18 +1008,15 @@ static inline int nr_cpusets(void) ...@@ -1007,18 +1008,15 @@ static inline int nr_cpusets(void)
* were changed (added or removed.) * were changed (added or removed.)
* *
* Finding the best partition (set of domains): * Finding the best partition (set of domains):
* The triple nested loops below over i, j, k scan over the * The double nested loops below over i, j scan over the load
* load balanced cpusets (using the array of cpuset pointers in * balanced cpusets (using the array of cpuset pointers in csa[])
* csa[]) looking for pairs of cpusets that have overlapping * looking for pairs of cpusets that have overlapping cpus_allowed
* cpus_allowed, but which don't have the same 'pn' partition * and merging them using a union-find algorithm.
* number and gives them in the same partition number. It keeps *
* looping on the 'restart' label until it can no longer find * The union of the cpus_allowed masks from the set of all cpusets
* any such pairs. * having the same root then form the one element of the partition
* (one sched domain) to be passed to partition_sched_domains().
* *
* The union of the cpus_allowed masks from the set of
* all cpusets having the same 'pn' value then form the one
* element of the partition (one sched domain) to be passed to
* partition_sched_domains().
*/ */
static int generate_sched_domains(cpumask_var_t **domains, static int generate_sched_domains(cpumask_var_t **domains,
struct sched_domain_attr **attributes) struct sched_domain_attr **attributes)
...@@ -1026,7 +1024,7 @@ static int generate_sched_domains(cpumask_var_t **domains, ...@@ -1026,7 +1024,7 @@ static int generate_sched_domains(cpumask_var_t **domains,
struct cpuset *cp; /* top-down scan of cpusets */ struct cpuset *cp; /* top-down scan of cpusets */
struct cpuset **csa; /* array of all cpuset ptrs */ struct cpuset **csa; /* array of all cpuset ptrs */
int csn; /* how many cpuset ptrs in csa so far */ int csn; /* how many cpuset ptrs in csa so far */
int i, j, k; /* indices for partition finding loops */ int i, j; /* indices for partition finding loops */
cpumask_var_t *doms; /* resulting partition; i.e. sched domains */ cpumask_var_t *doms; /* resulting partition; i.e. sched domains */
struct sched_domain_attr *dattr; /* attributes for custom domains */ struct sched_domain_attr *dattr; /* attributes for custom domains */
int ndoms = 0; /* number of sched domains in result */ int ndoms = 0; /* number of sched domains in result */
...@@ -1034,6 +1032,7 @@ static int generate_sched_domains(cpumask_var_t **domains, ...@@ -1034,6 +1032,7 @@ static int generate_sched_domains(cpumask_var_t **domains,
struct cgroup_subsys_state *pos_css; struct cgroup_subsys_state *pos_css;
bool root_load_balance = is_sched_load_balance(&top_cpuset); bool root_load_balance = is_sched_load_balance(&top_cpuset);
bool cgrpv2 = cgroup_subsys_on_dfl(cpuset_cgrp_subsys); bool cgrpv2 = cgroup_subsys_on_dfl(cpuset_cgrp_subsys);
int nslot_update;
doms = NULL; doms = NULL;
dattr = NULL; dattr = NULL;
...@@ -1121,31 +1120,25 @@ static int generate_sched_domains(cpumask_var_t **domains, ...@@ -1121,31 +1120,25 @@ static int generate_sched_domains(cpumask_var_t **domains,
if (root_load_balance && (csn == 1)) if (root_load_balance && (csn == 1))
goto single_root_domain; goto single_root_domain;
for (i = 0; i < csn; i++) if (!cgrpv2) {
csa[i]->pn = i; for (i = 0; i < csn; i++)
ndoms = csn; uf_node_init(&csa[i]->node);
restart:
/* Find the best partition (set of sched domains) */
for (i = 0; i < csn; i++) {
struct cpuset *a = csa[i];
int apn = a->pn;
for (j = 0; j < csn; j++) {
struct cpuset *b = csa[j];
int bpn = b->pn;
if (apn != bpn && cpusets_overlap(a, b)) {
for (k = 0; k < csn; k++) {
struct cpuset *c = csa[k];
if (c->pn == bpn) /* Merge overlapping cpusets */
c->pn = apn; for (i = 0; i < csn; i++) {
} for (j = i + 1; j < csn; j++) {
ndoms--; /* one less element */ if (cpusets_overlap(csa[i], csa[j]))
goto restart; uf_union(&csa[i]->node, &csa[j]->node);
} }
} }
/* Count the total number of domains */
for (i = 0; i < csn; i++) {
if (uf_find(&csa[i]->node) == &csa[i]->node)
ndoms++;
}
} else {
ndoms = csn;
} }
/* /*
...@@ -1178,44 +1171,25 @@ static int generate_sched_domains(cpumask_var_t **domains, ...@@ -1178,44 +1171,25 @@ static int generate_sched_domains(cpumask_var_t **domains,
} }
for (nslot = 0, i = 0; i < csn; i++) { for (nslot = 0, i = 0; i < csn; i++) {
struct cpuset *a = csa[i]; nslot_update = 0;
struct cpumask *dp;
int apn = a->pn;
if (apn < 0) {
/* Skip completed partitions */
continue;
}
dp = doms[nslot];
if (nslot == ndoms) {
static int warnings = 10;
if (warnings) {
pr_warn("rebuild_sched_domains confused: nslot %d, ndoms %d, csn %d, i %d, apn %d\n",
nslot, ndoms, csn, i, apn);
warnings--;
}
continue;
}
cpumask_clear(dp);
if (dattr)
*(dattr + nslot) = SD_ATTR_INIT;
for (j = i; j < csn; j++) { for (j = i; j < csn; j++) {
struct cpuset *b = csa[j]; if (uf_find(&csa[j]->node) == &csa[i]->node) {
struct cpumask *dp = doms[nslot];
if (apn == b->pn) {
cpumask_or(dp, dp, b->effective_cpus); if (i == j) {
nslot_update = 1;
cpumask_clear(dp);
if (dattr)
*(dattr + nslot) = SD_ATTR_INIT;
}
cpumask_or(dp, dp, csa[j]->effective_cpus);
cpumask_and(dp, dp, housekeeping_cpumask(HK_TYPE_DOMAIN)); cpumask_and(dp, dp, housekeeping_cpumask(HK_TYPE_DOMAIN));
if (dattr) if (dattr)
update_domain_attr_tree(dattr + nslot, b); update_domain_attr_tree(dattr + nslot, csa[j]);
/* Done with this partition */
b->pn = -1;
} }
} }
nslot++; if (nslot_update)
nslot++;
} }
BUG_ON(nslot != ndoms); BUG_ON(nslot != ndoms);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment