diff --git a/include/haproxy/thread.h b/include/haproxy/thread.h index 97bb4f4fa..df3c17bc3 100644 --- a/include/haproxy/thread.h +++ b/include/haproxy/thread.h @@ -44,6 +44,7 @@ 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_detect_more_than_cpus(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 7d5ddba60..06b988dad 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -2311,6 +2311,7 @@ static void init(int argc, char **argv) #endif thread_detect_binding_discrepancies(); + thread_detect_more_than_cpus(); /* Apply server states */ apply_server_state(); diff --git a/src/thread.c b/src/thread.c index 203150af0..6c3eddd36 100644 --- a/src/thread.c +++ b/src/thread.c @@ -1171,6 +1171,62 @@ int thread_detect_binding_discrepancies(void) return 0; } +/* Returns non-zero on anomaly (more threads than CPUs), and emits a warning in + * this case. It checks against configured cpu-map if any, otherwise against + * the number of CPUs at boot if known. It's better to run it only after + * thread_detect_binding_discrepancies() so that mixed cases can be eliminated. + */ +int thread_detect_more_than_cpus(void) +{ +#if defined(USE_CPU_AFFINITY) + struct hap_cpuset cpuset_map, cpuset_boot, cpuset_all; + uint th, tg, id; + int bound; + int tot_map, tot_all; + + ha_cpuset_zero(&cpuset_boot); + ha_cpuset_zero(&cpuset_map); + ha_cpuset_zero(&cpuset_all); + bound = 0; + 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])) { + ha_cpuset_or(&cpuset_map, &cpu_map[tg - 1].thread[id]); + bound++; + } + } + + ha_cpuset_assign(&cpuset_all, &cpuset_map); + if (bound != global.nbthread) { + if (ha_cpuset_detect_bound(&cpuset_boot)) + ha_cpuset_or(&cpuset_all, &cpuset_boot); + } + + tot_map = ha_cpuset_count(&cpuset_map); + tot_all = ha_cpuset_count(&cpuset_all); + + if (tot_map && bound > tot_map) { + ha_warning("This configuration binds %d threads to a total of %d CPUs via cpu-map " + "directives. This means that some threads will compete for the same CPU, " + "which will cause severe performance degradation. Please fix either the " + "'cpu-map' directives or set the global 'nbthread' value accordingly.\n", + bound, tot_map); + return 1; + } + else if (tot_all && global.nbthread > tot_all) { + ha_warning("This configuration enables %d threads running on a total of %d CPUs. " + "This means that some threads will compete for the same CPU, which will cause " + "severe performance degradation. Please either the 'cpu-map' directives to " + "adjust the CPUs to use, or fix the global 'nbthread' value.\n", + global.nbthread, tot_all); + return 1; + } +#endif + return 0; +} + + /* scans the configured thread mapping and establishes the final one. Returns <0 * on failure, >=0 on success. */