]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: threads: detect incomplete CPU bindings
authorWilly Tarreau <w@1wt.eu>
Mon, 4 Sep 2023 14:53:20 +0000 (16:53 +0200)
committerWilly Tarreau <w@1wt.eu>
Mon, 4 Sep 2023 17:39:17 +0000 (19:39 +0200)
It's very easy to mess up with some cpu-map directives and to leave
some thread unbound. Let's add a test that checks that either all
threads are bound or none are bound, but that we do not face the
intermediary situation where some are pinned and others are left
wandering around, possibly on the same CPUs as bound ones.

Note that this should not be backported, or maybe turned into a
notice only, as it appears that it will easily catch invalid
configs and that may break updates for some users.

include/haproxy/thread.h
src/haproxy.c
src/thread.c

index 471f59e3b23e376edc11c1e905613a532da61390..97bb4f4faf3334e9588862487f599971832104ec 100644 (file)
@@ -43,6 +43,7 @@ int parse_nbthread(const char *arg, char **err);
 void ha_tkill(unsigned int thr, int sig);
 void ha_tkillall(int sig);
 void ha_thread_relax(void);
+int thread_detect_binding_discrepancies(void);
 int thread_map_to_groups();
 int thread_resolve_group_mask(struct thread_set *ts, int defgrp, char **err);
 int parse_thread_set(const char *arg, struct thread_set *ts, char **err);
index fd32c0c142545d6eea6772d847b44a20919973e5..7d5ddba60275d53f196e06077a714624d4267c74 100644 (file)
@@ -2310,6 +2310,8 @@ static void init(int argc, char **argv)
         }
 #endif
 
+       thread_detect_binding_discrepancies();
+
        /* Apply server states */
        apply_server_state();
 
index 44cbc2e89e63db7716410512ad8202fa48214b0c..203150af05750ea0ecd42b4635c72652ac212585 100644 (file)
@@ -1132,6 +1132,45 @@ REGISTER_BUILD_OPTS("Built without multi-threading support (USE_THREAD not set).
 #endif // USE_THREAD
 
 
+/* Returns non-zero on anomaly (bound vs unbound), and emits a warning in this
+ * case.
+ */
+int thread_detect_binding_discrepancies(void)
+{
+#if defined(USE_CPU_AFFINITY)
+       uint th, tg, id;
+       uint tot_b = 0, tot_u = 0;
+       int first_b = -1;
+       int first_u = -1;
+
+       for (th = 0; th < global.nbthread; th++) {
+               tg = ha_thread_info[th].tgid;
+               id = ha_thread_info[th].ltid;
+
+               if (ha_cpuset_count(&cpu_map[tg - 1].thread[id]) == 0) {
+                       tot_u++;
+                       if (first_u < 0)
+                               first_u = th;
+               } else {
+                       tot_b++;
+                       if (first_b < 0)
+                               first_b = th;
+               }
+       }
+
+       if (tot_u > 0 && tot_b > 0) {
+               ha_warning("Found %u thread(s) mapped to a CPU and %u thread(s) not mapped to any CPU. "
+                          "This will result in some threads being randomly assigned to the same CPU, "
+                          "which will occasionally cause severe performance degradation. First thread "
+                          "bound is %d and first thread not bound is %d. Please either bind all threads "
+                          "or none (maybe some cpu-map directives are missing?).\n",
+                          tot_b, tot_u, first_b, first_u);
+               return 1;
+       }
+#endif
+       return 0;
+}
+
 /* scans the configured thread mapping and establishes the final one. Returns <0
  * on failure, >=0 on success.
  */