]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: cpu-topo: add a CPU policy setting to the global section
authorWilly Tarreau <w@1wt.eu>
Mon, 3 Mar 2025 12:44:11 +0000 (13:44 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 14 Mar 2025 17:33:16 +0000 (18:33 +0100)
We'll need to let the user decide what's best for their workload, and in
order to do this we'll have to provide tunable options. For that, we're
introducing struct ha_cpu_policy which contains a name, a description
and a function pointer. The purpose will be to use that function pointer
to choose the best CPUs to use and now to set the number of threads and
thread-groups, that will be called during the thread setup phase. The
only supported policy for now is "none" which doesn't set/touch anything
(i.e. all available CPUs are used).

include/haproxy/cpu_topo-t.h
include/haproxy/cpu_topo.h
src/cpu_topo.c
src/thread.c

index cb6e4f0058a023985581857ccab08d5974b9a296..4d85945eb4bd4dfe9d1aeb130c1d55f9b10cc0e4 100644 (file)
@@ -49,4 +49,19 @@ struct ha_cpu_cluster {
        uint nb_cpu;      /* total CPUs */
 };
 
+/* Description of a CPU selection policy. For now it only associates an option
+ * name with a callback function that is supposed to adjust the global.nbthread
+ * and global.nbtgroups based on the policy, the topology, and the constraints
+ * on the number of threads which must be between tmin and tmax included, and
+ * the number of thread groups which must be between gmin and gmax included.
+ * The callback also takes the policy number (cpu_policy) and a pointer to a
+ * string to write an error to in case of failure (in which case ret must be
+ * < 0 and the caller will fre the location). More settings might come later.
+ */
+struct ha_cpu_policy {
+       const char *name;                    /* option name in the configuration */
+       const char *desc;                    /* short description for help messages */
+       int (*fct)(int policy, int tmin, int tmax, int gmin, int gmax, char **err);
+};
+
 #endif /* _HAPROXY_CPU_TOPO_T_H */
index 97a8a783d7a8ac88540088f569369398b2fdc329..959d89b9955ff222d8f6cfda24a99d3a5eb77afd 100644 (file)
@@ -37,6 +37,11 @@ void cpu_compose_clusters(void);
 /* apply remaining topology-based cpu set restrictions */
 void cpu_refine_cpusets(void);
 
+/* apply the chosen CPU policy. Returns < 0 on failure with a message in *err
+ * that must be freed by the caller if non-null.
+ */
+int cpu_apply_policy(int tmin, int tmax, int gmin, int gmax, char **err);
+
 /* Detects CPUs that are bound to the current process. Returns the number of
  * CPUs detected or 0 if the detection failed.
  */
index 15d664f6ed83d843ae3470db1603c513679ef7f9..1aef23198c30cf7ff2af84beb24c7ea162068c03 100644 (file)
@@ -46,6 +46,15 @@ struct cpu_set_cfg {
        struct hap_cpuset drop_threads;
 } cpu_set_cfg;
 
+/* CPU policy choice */
+static int cpu_policy = 0;
+
+/* list of CPU policies for "cpu-policy". The default one is the first one. */
+static struct ha_cpu_policy ha_cpu_policy[] = {
+       { .name = "none",               .desc = "use all available CPUs",                           .fct = NULL   },
+       { 0 } /* end */
+};
+
 /* Detects CPUs that are online on the system. It may rely on FS access (e.g.
  * /sys on Linux). Returns the number of CPUs detected or 0 if the detection
  * failed.
@@ -872,6 +881,29 @@ void cpu_refine_cpusets(void)
        }
 }
 
+/* apply the chosen CPU policy if no cpu-map was forced. Returns < 0 on failure
+ * with a message in *err that must be freed by the caller if non-null.
+ */
+int cpu_apply_policy(int tmin, int tmax, int gmin, int gmax, char **err)
+{
+       *err = NULL;
+
+       if (cpu_map_configured()) {
+               /* nothing to do */
+               return 0;
+       }
+
+       if (!ha_cpu_policy[cpu_policy].fct) {
+               /* nothing to do */
+               return 0;
+       }
+
+       if (ha_cpu_policy[cpu_policy].fct(cpu_policy, tmin, tmax, gmin, gmax, err) < 0)
+               return -1;
+
+       return 0;
+}
+
 /* CPU topology detection below, OS-specific */
 
 #if defined(__linux__)
@@ -1330,6 +1362,36 @@ static int cfg_parse_cpu_set(char **args, int section_type, struct proxy *curpx,
        return -1;
 }
 
+/* Parse the "cpu-policy" global directive, which takes the name of one of the
+ * ha_cpu_policy[] names, and sets the associated index in cpu_policy.
+ */
+static int cfg_parse_cpu_policy(char **args, int section_type, struct proxy *curpx,
+                               const struct proxy *defpx, const char *file, int line,
+                               char **err)
+{
+       int i;
+
+       if (too_many_args(1, args, err, NULL))
+               return -1;
+
+       for (i = 0; ha_cpu_policy[i].name; i++) {
+               if (strcmp(args[1], ha_cpu_policy[i].name) == 0) {
+                       cpu_policy = i;
+                       return 0;
+               }
+       }
+
+       memprintf(err, "'%s' passed an unknown CPU policy '%s'. Supported values are:", args[0], args[1]);
+       for (i = 0; ha_cpu_policy[i].name; i++) {
+               memprintf(err, "%s%s '%s' (%s)%s", *err,
+                         (i > 0 && ha_cpu_policy[i+1].name) ? "" : " and",
+                         ha_cpu_policy[i].name,
+                         ha_cpu_policy[i].desc,
+                         (ha_cpu_policy[i+1].name) ? "," : ".\n");
+       }
+       return -1;
+}
+
 /* Allocates everything needed to store CPU topology at boot.
  * Returns non-zero on success, zero on failure.
  */
@@ -1403,6 +1465,7 @@ REGISTER_POST_DEINIT(cpu_topo_deinit);
 
 /* config keyword parsers */
 static struct cfg_kw_list cfg_kws = {ILH, {
+       { CFG_GLOBAL, "cpu-policy",  cfg_parse_cpu_policy, 0 },
        { CFG_GLOBAL, "cpu-set",  cfg_parse_cpu_set, 0 },
        { 0, NULL, NULL }
 }};
index 7a720d827ed0a7191a95258710576abed666d1bc..57925b989c998f4e8e7bb0a2dd9647ee53818509 100644 (file)
@@ -1549,6 +1549,7 @@ void thread_detect_count(void)
        int grp_max __maybe_unused;
        int cpus_avail __maybe_unused;
        int cpu __maybe_unused;
+       char *err __maybe_unused;
 
        thr_min = 1; thr_max = MAX_THREADS;
        grp_min = 1; grp_max = MAX_TGROUPS;
@@ -1614,6 +1615,13 @@ void thread_detect_count(void)
         * on the same cluster _capacity_ up to thr_max.
         */
 
+       if (cpu_apply_policy(thr_min, thr_max, grp_min, grp_max, &err) < 0) {
+               if (err)
+                       ha_warning("cpu-policy: %s\n", err);
+               ha_free(&err);
+               return;
+       }
+
        /* Let's implement here the automatic binding to the first available
         * NUMA node when thread count is not set, taskset is not used and
         * no cpu-map directive is present.