From: Willy Tarreau Date: Mon, 4 Sep 2023 14:53:20 +0000 (+0200) Subject: MEDIUM: threads: detect incomplete CPU bindings X-Git-Tag: v2.9-dev5~55 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8357f950cbb33640a6b940fe1fc33120a5ab177d;p=thirdparty%2Fhaproxy.git MEDIUM: threads: detect incomplete CPU bindings 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. --- diff --git a/include/haproxy/thread.h b/include/haproxy/thread.h index 471f59e3b2..97bb4f4faf 100644 --- a/include/haproxy/thread.h +++ b/include/haproxy/thread.h @@ -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); diff --git a/src/haproxy.c b/src/haproxy.c index fd32c0c142..7d5ddba602 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -2310,6 +2310,8 @@ static void init(int argc, char **argv) } #endif + thread_detect_binding_discrepancies(); + /* Apply server states */ apply_server_state(); diff --git a/src/thread.c b/src/thread.c index 44cbc2e89e..203150af05 100644 --- a/src/thread.c +++ b/src/thread.c @@ -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. */