]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: cpu-topo: skip CPU properties that we've verified do not exist
authorWilly Tarreau <w@1wt.eu>
Mon, 6 Jan 2025 19:01:52 +0000 (20:01 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 14 Mar 2025 17:30:31 +0000 (18:30 +0100)
A number of entries under /cpu/cpu%d only exist on certain kernel
versions, certain archs and/or with certain modules loaded. It's
pointless to insist on trying to read them all for all CPUs when
we've already verified they do not exist. Thus let's use stat()
the first time prior to checking some of them, and only try to
access them when they really exist. This almost completely
eliminates the large number of ENOENT that was visible in strace
during startup.

src/cpu_topo.c

index aebdcf8840dd3fbc27fe909e80e5e9e26e1d69d6..d1431a50345cac025ae8ed4f4470fde4d74fed77 100644 (file)
@@ -249,6 +249,8 @@ int cpu_detect_topology(void)
        struct ha_cpu_topo cpu_id = { }; /* all zeroes */
        struct hap_cpuset node_cpu_set;
        struct dirent *de;
+       int no_cache, no_topo, no_capa, no_clust, no_pkg;
+       int no_cppc, no_freq;
        DIR *dir;
        int cpu;
 
@@ -263,6 +265,8 @@ int cpu_detect_topology(void)
        if (!is_dir_present(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu"))
                goto skip_cpu;
 
+       /* detect the presence of some kernel-specific fields */
+       no_cache = no_topo = no_capa = no_clust = no_pkg = no_freq = no_cppc = -1;
        for (cpu = 0; cpu <= cpu_topo_lastcpu; cpu++) {
                struct hap_cpuset cpus_list;
                int next_level = 1; // assume L1 if unknown
@@ -285,6 +289,12 @@ int cpu_detect_topology(void)
                 * are "Data", "Instruction", "Unified". We just ignore inst if
                 * found.
                 */
+               if (no_cache < 0)
+                       no_cache = !is_dir_present(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu/cpu%d/cache", cpu);
+
+               if (no_cache)
+                       goto skip_cache;
+
                for (idx = 0; idx < 10; idx++) {
                        if (!is_dir_present(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu/cpu%d/cache/index%d", cpu, idx))
                                break;
@@ -321,6 +331,13 @@ int cpu_detect_topology(void)
                        }
                }
 
+       skip_cache:
+               if (no_topo < 0)
+                       no_topo = !is_dir_present(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu/cpu%d/topology", cpu);
+
+               if (no_topo)
+                       goto skip_topo;
+
                /* Now let's try to get more info about how the cores are
                 * arranged in packages, clusters, cores, threads etc. It
                 * overlaps a bit with the cache above, but as not all systems
@@ -359,7 +376,12 @@ int cpu_detect_topology(void)
                 * only consider these values if there's more than one CPU per
                 * cluster (some kernels such as 6.1 report one cluster per CPU).
                 */
-               if (ha_cpu_topo[cpu].cl_gid < 0 &&
+               if (no_clust < 0) {
+                       no_clust = !is_file_present(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu/cpu%d/topology/cluster_cpus_list", cpu) &&
+                                  !is_file_present(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu/cpu%d/topology/core_siblings_list", cpu);
+               }
+
+               if (!no_clust && ha_cpu_topo[cpu].cl_gid < 0 &&
                    (read_line_to_trash(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu/cpu%d/topology/cluster_cpus_list", cpu) >= 0 ||
                     read_line_to_trash(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu/cpu%d/topology/core_siblings_list", cpu) >= 0)) {
                        parse_cpu_set_args[0] = trash.area;
@@ -384,7 +406,12 @@ int cpu_detect_topology(void)
                 * number starts at 0. The first one is preferred because it
                 * provides a list in a single read().
                 */
-               if (ha_cpu_topo[cpu].pk_id < 0 &&
+               if (no_pkg < 0) {
+                       no_pkg = !is_file_present(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu/cpu%d/topology/package_cpus_list", cpu) &&
+                                !is_file_present(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu/cpu%d/topology/core_siblings_list", cpu);
+               }
+
+               if (!no_pkg && ha_cpu_topo[cpu].pk_id < 0 &&
                    (read_line_to_trash(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu/cpu%d/topology/package_cpus_list", cpu) >= 0 ||
                     read_line_to_trash(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu/cpu%d/topology/core_siblings_list", cpu) >= 0)) {
                        parse_cpu_set_args[0] = trash.area;
@@ -404,11 +431,15 @@ int cpu_detect_topology(void)
                                ha_cpu_topo[cpu].pk_id = str2uic(trash.area);
                }
 
+       skip_topo:
+               if (no_capa < 0)
+                       no_capa = !is_file_present(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu/cpu%d/cpu_capacity", cpu);
+
                /* CPU capacity is a relative notion to compare little and big
                 * cores. Usually the values encountered in field set the big
                 * CPU's nominal capacity to 1024 and the other ones below.
                 */
-               if (ha_cpu_topo[cpu].capa < 0 &&
+               if (!no_capa && ha_cpu_topo[cpu].capa < 0 &&
                    read_line_to_trash(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu/cpu%d/cpu_capacity", cpu) >= 0) {
                        if (trash.data)
                                ha_cpu_topo[cpu].capa = str2uic(trash.area);
@@ -432,7 +463,10 @@ int cpu_detect_topology(void)
                 * an efficient core, and we major performant cores' capacity
                 * by 50% (shown to be roughly correct on modern CPUs).
                 */
-               if (ha_cpu_topo[cpu].capa < 0 &&
+               if (no_freq < 0)
+                       no_freq = !is_dir_present(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu/cpu%d/cpufreq", cpu);
+
+               if (!no_freq && ha_cpu_topo[cpu].capa < 0 &&
                    read_line_to_trash(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu/cpu%d/cpufreq/scaling_max_freq", cpu) >= 0) {
                        /* This is in kHz, turn it to MHz to stay below 32k */
                        if (trash.data) {
@@ -442,7 +476,10 @@ int cpu_detect_topology(void)
                        }
                }
 
-               if (ha_cpu_topo[cpu].capa < 0 &&
+               if (no_cppc < 0)
+                       no_cppc = !is_dir_present(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu/cpu%d/acpi_cppc", cpu);
+
+               if (!no_cppc && ha_cpu_topo[cpu].capa < 0 &&
                    read_line_to_trash(NUMA_DETECT_SYSTEM_SYSFS_PATH "/cpu/cpu%d/acpi_cppc/nominal_perf", cpu) >= 0) {
                        if (trash.data)
                                ha_cpu_topo[cpu].capa = str2uic(trash.area);