]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: stats: Make stat_l variable used to dump a stat line thread local
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 6 Jan 2021 06:41:56 +0000 (07:41 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Wed, 6 Jan 2021 09:34:12 +0000 (10:34 +0100)
Since ee63d4bd6 ("MEDIUM: stats: integrate static proxies stats in new
stats"), all dumped stats for a given domain, the default ones and the
modules ones, are merged in a signle array to dump them in a generic way.
For this purpose, the stat_l global variable is allocated at startup to
store a line of stats before the dump, i.e. all stats of an entity
(frontend, backend, listener, server or dns nameserver). But this variable
is not thread safe.  If stats are retrieved concurrently by several clients
on different threads, the same variable is used. This leads to corrupted
stats output.

To fix the bug, the stat_l variable is now thread local.

This patch should probably solve issues #972 and #992. It must be backported
to 2.3.

src/stats.c

index e2d37779bfac5643645d22891043c45c6d2847f3..36e5c077a6d4460e85d5447d04b20d41c393b088 100644 (file)
@@ -261,7 +261,7 @@ static struct name_desc *stat_f[STATS_DOMAIN_COUNT];
 static size_t stat_count[STATS_DOMAIN_COUNT];
 
 /* one line for stats */
-static struct field *stat_l[STATS_DOMAIN_COUNT];
+static THREAD_LOCAL struct field *stat_l[STATS_DOMAIN_COUNT];
 
 /* list of all registered stats module */
 static struct list stats_module_list[STATS_DOMAIN_COUNT] = {
@@ -4500,12 +4500,7 @@ static int allocate_stats_px_postcheck(void)
                }
        }
 
-       stat_l[STATS_DOMAIN_PROXY] = malloc(stat_count[STATS_DOMAIN_PROXY] * sizeof(struct field));
-       if (!stat_l[STATS_DOMAIN_PROXY]) {
-               ha_alert("stats: cannot allocate a line for proxy statistics\n");
-               err_code |= ERR_ALERT | ERR_FATAL;
-               return err_code;
-       }
+       /* wait per-thread alloc to perform corresponding stat_l allocation */
 
        return err_code;
 }
@@ -4538,18 +4533,29 @@ static int allocate_stats_dns_postcheck(void)
                return err_code;
        }
 
-       stat_l[STATS_DOMAIN_DNS] = malloc(stat_count[STATS_DOMAIN_DNS] * sizeof(struct field));
-       if (!stat_l[STATS_DOMAIN_DNS]) {
-               ha_alert("stats: cannot allocate a line for dns statistics\n");
-               err_code |= ERR_ALERT | ERR_FATAL;
-               return err_code;
-       }
+       /* wait per-thread alloc to perform corresponding stat_l allocation */
 
        return err_code;
 }
 
 REGISTER_CONFIG_POSTPARSER("allocate-stats-dns", allocate_stats_dns_postcheck);
 
+static int allocate_stat_lines_per_thread(void)
+{
+       int domains[] = { STATS_DOMAIN_PROXY, STATS_DOMAIN_DNS }, i;
+
+       for (i = 0; i < STATS_DOMAIN_COUNT; ++i) {
+               const int domain = domains[i];
+
+               stat_l[domain] = malloc(stat_count[domain] * sizeof(struct field));
+               if (!stat_l[domain])
+                       return 0;
+       }
+       return 1;
+}
+
+REGISTER_PER_THREAD_ALLOC(allocate_stat_lines_per_thread);
+
 static int allocate_trash_counters(void)
 {
        struct stats_module *mod;
@@ -4578,15 +4584,27 @@ static int allocate_trash_counters(void)
 
 REGISTER_PER_THREAD_ALLOC(allocate_trash_counters);
 
-static void deinit_stats(void)
+static void deinit_stat_lines_per_thread(void)
 {
        int domains[] = { STATS_DOMAIN_PROXY, STATS_DOMAIN_DNS }, i;
 
        for (i = 0; i < STATS_DOMAIN_COUNT; ++i) {
                const int domain = domains[i];
 
-               if (stat_l[domain])
-                       free(stat_l[domain]);
+               free(stat_l[domain]);
+               stat_l[domain] = NULL;
+       }
+}
+
+
+REGISTER_PER_THREAD_FREE(deinit_stat_lines_per_thread);
+
+static void deinit_stats(void)
+{
+       int domains[] = { STATS_DOMAIN_PROXY, STATS_DOMAIN_DNS }, i;
+
+       for (i = 0; i < STATS_DOMAIN_COUNT; ++i) {
+               const int domain = domains[i];
 
                if (stat_f[domain])
                        free(stat_f[domain]);