]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
cpuset: use Union-Find to optimize the merging of cpumasks
authorXavier <xavier_qy@163.com>
Thu, 4 Jul 2024 06:24:44 +0000 (14:24 +0800)
committerTejun Heo <tj@kernel.org>
Tue, 30 Jul 2024 23:04:50 +0000 (13:04 -1000)
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 <xavier_qy@163.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
kernel/cgroup/cpuset.c

index 31aefc3e1a6a1849ab2d1e15cb01e8aa1b7581ae..9066f9b4af24ead89403b6602773e3586696303f 100644 (file)
@@ -46,6 +46,7 @@
 #include <linux/cgroup.h>
 #include <linux/wait.h>
 #include <linux/workqueue.h>
+#include <linux/union_find.h>
 
 DEFINE_STATIC_KEY_FALSE(cpusets_pre_enable_key);
 DEFINE_STATIC_KEY_FALSE(cpusets_enabled_key);
@@ -173,9 +174,6 @@ struct cpuset {
         */
        int attach_in_progress;
 
-       /* partition number for rebuild_sched_domains() */
-       int pn;
-
        /* for custom sched domain */
        int relax_domain_level;
 
@@ -207,6 +205,9 @@ struct cpuset {
 
        /* Remote partition silbling list anchored at remote_children */
        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)
  *        were changed (added or removed.)
  *
  * Finding the best partition (set of domains):
- *     The triple nested loops below over i, j, k scan over the
- *     load balanced cpusets (using the array of cpuset pointers in
- *     csa[]) looking for pairs of cpusets that have overlapping
- *     cpus_allowed, but which don't have the same 'pn' partition
- *     number and gives them in the same partition number.  It keeps
- *     looping on the 'restart' label until it can no longer find
- *     any such pairs.
+ *     The double nested loops below over i, j scan over the load
+ *     balanced cpusets (using the array of cpuset pointers in csa[])
+ *     looking for pairs of cpusets that have overlapping cpus_allowed
+ *     and merging them using a union-find algorithm.
+ *
+ *     The union of the cpus_allowed masks from the set of all cpusets
+ *     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,
                        struct sched_domain_attr **attributes)
@@ -1026,7 +1024,7 @@ static int generate_sched_domains(cpumask_var_t **domains,
        struct cpuset *cp;      /* top-down scan of cpusets */
        struct cpuset **csa;    /* array of all cpuset ptrs */
        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 */
        struct sched_domain_attr *dattr;  /* attributes for custom domains */
        int ndoms = 0;          /* number of sched domains in result */
@@ -1034,6 +1032,7 @@ static int generate_sched_domains(cpumask_var_t **domains,
        struct cgroup_subsys_state *pos_css;
        bool root_load_balance = is_sched_load_balance(&top_cpuset);
        bool cgrpv2 = cgroup_subsys_on_dfl(cpuset_cgrp_subsys);
+       int nslot_update;
 
        doms = NULL;
        dattr = NULL;
@@ -1121,31 +1120,25 @@ v2:
        if (root_load_balance && (csn == 1))
                goto single_root_domain;
 
-       for (i = 0; i < csn; i++)
-               csa[i]->pn = i;
-       ndoms = csn;
-
-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 (!cgrpv2) {
+               for (i = 0; i < csn; i++)
+                       uf_node_init(&csa[i]->node);
 
-                                       if (c->pn == bpn)
-                                               c->pn = apn;
-                               }
-                               ndoms--;        /* one less element */
-                               goto restart;
+               /* Merge overlapping cpusets */
+               for (i = 0; i < csn; i++) {
+                       for (j = i + 1; j < csn; j++) {
+                               if (cpusets_overlap(csa[i], csa[j]))
+                                       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 @@ restart:
        }
 
        for (nslot = 0, i = 0; i < csn; i++) {
-               struct cpuset *a = csa[i];
-               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;
+               nslot_update = 0;
                for (j = i; j < csn; j++) {
-                       struct cpuset *b = csa[j];
-
-                       if (apn == b->pn) {
-                               cpumask_or(dp, dp, b->effective_cpus);
+                       if (uf_find(&csa[j]->node) == &csa[i]->node) {
+                               struct cpumask *dp = doms[nslot];
+
+                               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));
                                if (dattr)
-                                       update_domain_attr_tree(dattr + nslot, b);
-
-                               /* Done with this partition */
-                               b->pn = -1;
+                                       update_domain_attr_tree(dattr + nslot, csa[j]);
                        }
                }
-               nslot++;
+               if (nslot_update)
+                       nslot++;
        }
        BUG_ON(nslot != ndoms);