]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: stick-table: set the track-sc limit at boottime via tune.stick-counters
authorWilly Tarreau <w@1wt.eu>
Fri, 6 Jan 2023 15:09:58 +0000 (16:09 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 6 Jan 2023 17:08:49 +0000 (18:08 +0100)
The number of stick-counter entries usable by track-sc rules is currently
set at build time. There is no good value for this since the vast majority
of users don't need any, most need only a few and rare users need more.
Adding more counters for everyone increases memory and CPU usages for no
reason.

This patch moves the per-session and per-stream arrays to a pool of a size
defined at boot time. This way it becomes possible to set the number of
entries at boot time via a new global setting "tune.stick-counters" that
sets the limit for the whole process. When not set, the MAX_SESS_STR_CTR
value still applies, or 3 if not set, as before.

It is also possible to lower the value to 0 to save a bit of memory if
not used at all.

Note that a few low-level sample-fetch functions had to be protected due
to the ability to use sample-fetches in the global section to set some
variables.

13 files changed:
doc/configuration.txt
include/haproxy/global-t.h
include/haproxy/session-t.h
include/haproxy/session.h
include/haproxy/stick_table.h
include/haproxy/stream-t.h
include/haproxy/stream.h
src/cfgparse.c
src/haproxy.c
src/session.c
src/stick_table.c
src/stream.c
src/tcp_rules.c

index c5e6d2a0338999c1822218c8ee99858a6365d66b..6d1c2d8aa7b2f77cdd0d13c3d33908a1756efb7e 100644 (file)
@@ -1158,6 +1158,7 @@ The following keywords are supported in the "global" section :
    - tune.sched.low-latency
    - tune.sndbuf.client
    - tune.sndbuf.server
+   - tune.stick-counters
    - tune.ssl.cachesize
    - tune.ssl.capture-buffer-size
    - tune.ssl.capture-cipherlist-size (deprecated)
@@ -3305,6 +3306,21 @@ tune.ssl.ssl-ctx-cache-size <number>
   dynamically is expensive, they are cached. The default cache size is set to
   1000 entries.
 
+tune.stick-counters <number>
+  Sets the number of stick-counters that may be tracked at the same time by a
+  connection or a request via "track-sc*" actions in "tcp-request" or
+  "http-request" rules. The defaut value is set at build time by the macro
+  MAX_SESS_STK_CTR, and defaults to 3. With this setting it is possible to
+  change the value and ignore the one passed at build time. Increasing this
+  value may be needed when porting complex configurations to haproxy, but users
+  are warned against the costs: each entry takes 16 bytes per connection and
+  16 bytes per request, all of which need to be allocated and zeroed for all
+  requests even when not used. As such a value of 10 will inflate the memory
+  consumption per request by 320 bytes and will cause this memory to be erased
+  for each request, which does have measurable CPU impacts. Conversely, when
+  no "track-sc" rules are used, the value may be lowered (0 being valid to
+  entirely disable stick-counters).
+
 tune.vars.global-max-size <size>
 tune.vars.proc-max-size <size>
 tune.vars.reqres-max-size <size>
@@ -7617,9 +7633,10 @@ http-request track-sc2 <key> [table <table>] [ { if | unless } <condition> ]
 
   This enables tracking of sticky counters from current request. These rules do
   not stop evaluation and do not change default action. The number of counters
-  that may be simultaneously tracked by the same connection is set in
-  MAX_SESS_STKCTR at build time (reported in haproxy -vv) which defaults to 3,
-  so the track-sc number is between 0 and (MAX_SESS_STKCTR-1). The first
+  that may be simultaneously tracked by the same connection is set by the
+  global "tune.stick-counters" setting, which defaults to MAX_SESS_STKCTR if
+  set at build time (it is reported in haproxy -vv) and which defaults to 3,
+  so the track-sc number is between 0 and (tune.stick-counters-1). The first
   "track-sc0" rule executed enables tracking of the counters of the specified
   table as the first set. The first "track-sc1" rule executed enables tracking
   of the counters of the specified table as the second set. The first
@@ -18924,12 +18941,12 @@ TCP/IP addresses and ports, as well as elements from stick-tables related to
 the incoming connection. For retrieving a value from a sticky counters, the
 counter number can be explicitly set as 0, 1, or 2 using the pre-defined
 "sc0_", "sc1_", or "sc2_" prefix. These three pre-defined prefixes can only be
-used if MAX_SESS_STKCTR value does not exceed 3, otherwise the counter number
-can be specified as the first integer argument when using the "sc_" prefix.
-Starting from "sc_0" to "sc_N" where N is (MAX_SESS_STKCTR-1). An optional
-table may be specified with the "sc*" form, in which case the currently
-tracked key will be looked up into this alternate table instead of the table
-currently being tracked.
+used if the global "tune.stick-counters" value does not exceed 3, otherwise the
+counter number can be specified as the first integer argument when using the
+"sc_" prefix starting from "sc_0" to "sc_N" where N is (tune.stick-counters-1).
+An optional table may be specified with the "sc*" form, in which case the
+currently tracked key will be looked up into this alternate table instead of
+the table currently being tracked.
 
 bc_dst : ip
   This is the destination ip address of the connection on the server side,
index 11f4b2c0aabbeecaeb8e02e34e916dcf23fa74e7..92827c6fda0081bc64e73924fbaa063be7454de8 100644 (file)
@@ -162,6 +162,7 @@ struct global {
                int pool_high_count;  /* max number of opened fd before we start killing idle connections when creating new connections */
                size_t pool_cache_size;    /* per-thread cache size per pool (defaults to CONFIG_HAP_POOL_CACHE_SIZE) */
                unsigned short idle_timer; /* how long before an empty buffer is considered idle (ms) */
+               int nb_stk_ctr;       /* number of stick counters, defaults to MAX_SESS_STKCTR */
 #ifdef USE_QUIC
                unsigned int quic_backend_max_idle_timeout;
                unsigned int quic_frontend_max_idle_timeout;
index e7a989962186d8c327436b4d79e6f1f46df9f8cc..46023f0239512ddf1999ec28411865548e38c9ba 100644 (file)
@@ -50,7 +50,7 @@ struct session {
        enum obj_type *origin;          /* the connection / applet which initiated this session */
        struct timeval accept_date;     /* date of the session's accept() in user date */
        struct timeval tv_accept;       /* date of the session's accept() in internal date (monotonic) */
-       struct stkctr stkctr[MAX_SESS_STKCTR];  /* stick counters for tcp-connection */
+       struct stkctr *stkctr;          /* stick counters for tcp-connection */
        struct vars vars;               /* list of variables for the session scope. */
        struct task *task;              /* handshake timeout processing */
        long t_handshake;               /* handshake duration, -1 = not completed */
index 13152cf1ba321fd4b128658a403591994c584491..38335e4a51608b6f43d67da6c15908b8e6b2c2be 100644 (file)
@@ -50,7 +50,10 @@ static inline void session_store_counters(struct session *sess)
        int i;
        struct stksess *ts;
 
-       for (i = 0; i < MAX_SESS_STKCTR; i++) {
+       if (unlikely(!sess->stkctr)) // pool not allocated yet
+               return;
+
+       for (i = 0; i < global.tune.nb_stk_ctr; i++) {
                struct stkctr *stkctr = &sess->stkctr[i];
 
                ts = stkctr_entry(stkctr);
@@ -80,7 +83,10 @@ static inline void session_inc_http_req_ctr(struct session *sess)
 {
        int i;
 
-       for (i = 0; i < MAX_SESS_STKCTR; i++)
+       if (unlikely(!sess->stkctr)) // pool not allocated yet
+               return;
+
+       for (i = 0; i < global.tune.nb_stk_ctr; i++)
                stkctr_inc_http_req_ctr(&sess->stkctr[i]);
 }
 
@@ -94,7 +100,10 @@ static inline void session_inc_http_err_ctr(struct session *sess)
 {
        int i;
 
-       for (i = 0; i < MAX_SESS_STKCTR; i++)
+       if (unlikely(!sess->stkctr)) // pool not allocated yet
+               return;
+
+       for (i = 0; i < global.tune.nb_stk_ctr; i++)
                stkctr_inc_http_err_ctr(&sess->stkctr[i]);
 }
 
@@ -107,7 +116,10 @@ static inline void session_inc_http_fail_ctr(struct session *sess)
 {
        int i;
 
-       for (i = 0; i < MAX_SESS_STKCTR; i++)
+       if (unlikely(!sess->stkctr)) // pool not allocated yet
+               return;
+
+       for (i = 0; i < global.tune.nb_stk_ctr; i++)
                stkctr_inc_http_fail_ctr(&sess->stkctr[i]);
 }
 
index 196aabb35ea39bf3947579f74984bd9bd70731e2..cbec1379541d0a6c2a75c528637eec819154b08b 100644 (file)
@@ -32,6 +32,7 @@
 #include <haproxy/ticks.h>
 
 extern struct stktable *stktables_list;
+extern struct pool_head *pool_head_stk_ctr;
 extern struct stktable_type stktable_types[];
 
 #define stktable_data_size(type) (sizeof(((union stktable_data*)0)->type))
index 1ec40f513ee092865410889e927450e659a850fd..fe85217c0a988e82417ea0aaf3183eb685fb1dad 100644 (file)
@@ -245,7 +245,7 @@ struct stream {
                struct stktable *table;
        } store[8];                     /* tracked stickiness values to store */
 
-       struct stkctr stkctr[MAX_SESS_STKCTR];  /* content-aware stick counters */
+       struct stkctr *stkctr;                  /* content-aware stick counters */
 
        struct strm_flt strm_flt;               /* current state of filters active on this stream */
 
index 06716aeeb191d177e4cfbff857b76c22ef2f0ced..ac2158d1c1b4142d1b3c8f81bb9b26081912833a 100644 (file)
@@ -116,7 +116,10 @@ static inline void stream_store_counters(struct stream *s)
        int i;
        struct stksess *ts;
 
-       for (i = 0; i < MAX_SESS_STKCTR; i++) {
+       if (unlikely(!s->stkctr)) // pool not allocated yet
+               return;
+
+       for (i = 0; i < global.tune.nb_stk_ctr; i++) {
                ts = stkctr_entry(&s->stkctr[i]);
                if (!ts)
                        continue;
@@ -152,7 +155,10 @@ static inline void stream_stop_content_counters(struct stream *s)
        void *ptr;
        int i;
 
-       for (i = 0; i < MAX_SESS_STKCTR; i++) {
+       if (unlikely(!s->stkctr)) // pool not allocated yet
+               return;
+
+       for (i = 0; i < global.tune.nb_stk_ctr; i++) {
                ts = stkctr_entry(&s->stkctr[i]);
                if (!ts)
                        continue;
@@ -231,7 +237,10 @@ static inline void stream_inc_http_req_ctr(struct stream *s)
 {
        int i;
 
-       for (i = 0; i < MAX_SESS_STKCTR; i++) {
+       if (unlikely(!s->stkctr)) // pool not allocated yet
+               return;
+
+       for (i = 0; i < global.tune.nb_stk_ctr; i++) {
                if (!stkctr_inc_http_req_ctr(&s->stkctr[i]))
                        stkctr_inc_http_req_ctr(&s->sess->stkctr[i]);
        }
@@ -244,7 +253,10 @@ static inline void stream_inc_be_http_req_ctr(struct stream *s)
 {
        int i;
 
-       for (i = 0; i < MAX_SESS_STKCTR; i++) {
+       if (unlikely(!s->stkctr)) // pool not allocated yet
+               return;
+
+       for (i = 0; i < global.tune.nb_stk_ctr; i++) {
                if (!stkctr_entry(&s->stkctr[i]) || !(stkctr_flags(&s->stkctr[i]) & STKCTR_TRACK_BACKEND))
                        continue;
 
@@ -262,7 +274,10 @@ static inline void stream_inc_http_err_ctr(struct stream *s)
 {
        int i;
 
-       for (i = 0; i < MAX_SESS_STKCTR; i++) {
+       if (unlikely(!s->stkctr)) // pool not allocated yet
+               return;
+
+       for (i = 0; i < global.tune.nb_stk_ctr; i++) {
                if (!stkctr_inc_http_err_ctr(&s->stkctr[i]))
                        stkctr_inc_http_err_ctr(&s->sess->stkctr[i]);
        }
@@ -277,7 +292,10 @@ static inline void stream_inc_http_fail_ctr(struct stream *s)
 {
        int i;
 
-       for (i = 0; i < MAX_SESS_STKCTR; i++) {
+       if (unlikely(!s->stkctr)) // pool not allocated yet
+               return;
+
+       for (i = 0; i < global.tune.nb_stk_ctr; i++) {
                if (!stkctr_inc_http_fail_ctr(&s->stkctr[i]))
                        stkctr_inc_http_fail_ctr(&s->sess->stkctr[i]);
        }
index ac98a9567beb097e3c79a5d9d913701e2870829c..6f2d93a055fdb0657ea14e2d6e34e4d0cb748faf 100644 (file)
@@ -1564,9 +1564,13 @@ cfg_parse_track_sc_num(unsigned int *track_sc_num,
                return -1;
        }
 
-       if (num >= MAX_SESS_STKCTR) {
-               memprintf(errmsg, "%u track-sc number exceeding "
-                         "%d (MAX_SESS_STKCTR-1) value", num, MAX_SESS_STKCTR - 1);
+       if (num >= global.tune.nb_stk_ctr) {
+               if (!global.tune.nb_stk_ctr)
+                       memprintf(errmsg, "%u track-sc number not usable, stick-counters "
+                                 "are disabled by tune.stick-counters", num);
+               else
+                       memprintf(errmsg, "%u track-sc number exceeding "
+                                 "%d (tune.stick-counters-1) value", num, global.tune.nb_stk_ctr - 1);
                return -1;
        }
 
index c454c803dc6a5e93997fcad6f83c5c1c4fd53613..5f345f99d73cde5046b6c489e83d58d782ed864b 100644 (file)
@@ -204,6 +204,7 @@ struct global global = {
 #else
                .idle_timer = 1000, /* 1 second */
 #endif
+               .nb_stk_ctr = MAX_SESS_STKCTR,
 #ifdef USE_QUIC
                .quic_backend_max_idle_timeout = QUIC_TP_DFLT_BACK_MAX_IDLE_TIMEOUT,
                .quic_frontend_max_idle_timeout = QUIC_TP_DFLT_FRONT_MAX_IDLE_TIMEOUT,
index 3cbacc7ae850933ce60b25f74b7af18f4a2acba5..5c868ae0981af9a1b4b8b4573c8bc407db4b39e0 100644 (file)
@@ -46,7 +46,13 @@ struct session *session_new(struct proxy *fe, struct listener *li, enum obj_type
                sess->origin = origin;
                sess->accept_date = date; /* user-visible date for logging */
                sess->tv_accept   = now;  /* corrected date for internal use */
-               memset(sess->stkctr, 0, sizeof(sess->stkctr));
+               sess->stkctr = NULL;
+               if (pool_head_stk_ctr) {
+                       sess->stkctr = pool_alloc(pool_head_stk_ctr);
+                       if (!sess->stkctr)
+                               goto out_fail_alloc;
+                       memset(sess->stkctr, 0, sizeof(sess->stkctr[0]) * global.tune.nb_stk_ctr);
+               }
                vars_init_head(&sess->vars, SCOPE_SESS);
                sess->task = NULL;
                sess->t_handshake = -1; /* handshake not done yet */
@@ -60,6 +66,9 @@ struct session *session_new(struct proxy *fe, struct listener *li, enum obj_type
                sess->dst = NULL;
        }
        return sess;
+ out_fail_alloc:
+       pool_free(pool_head_session, sess);
+       return NULL;
 }
 
 void session_free(struct session *sess)
@@ -70,6 +79,7 @@ void session_free(struct session *sess)
        if (sess->listener)
                listener_release(sess->listener);
        session_store_counters(sess);
+       pool_free(pool_head_stk_ctr, sess->stkctr);
        vars_prune_per_sess(&sess->vars);
        conn = objt_conn(sess->origin);
        if (conn != NULL && conn->mux)
@@ -116,7 +126,7 @@ static void session_count_new(struct session *sess)
 
        proxy_inc_fe_sess_ctr(sess->listener, sess->fe);
 
-       for (i = 0; i < MAX_SESS_STKCTR; i++) {
+       for (i = 0; i < global.tune.nb_stk_ctr; i++) {
                stkctr = &sess->stkctr[i];
                if (!stkctr_entry(stkctr))
                        continue;
index 3533ec09e4da59c1f5d6f51c0a6c05ed564ddd84..6a861110a25ffb2f60a0e3b9e80bb90e05905a1a 100644 (file)
@@ -50,7 +50,7 @@
 /* structure used to return a table key built from a sample */
 static THREAD_LOCAL struct stktable_key static_table_key;
 static int (*smp_fetch_src)(const struct arg *, struct sample *, const char *, void *);
-
+struct pool_head *pool_head_stk_ctr __read_mostly = NULL;
 struct stktable *stktables_list;
 struct eb_root stktable_by_name = EB_ROOT;
 
@@ -2456,14 +2456,16 @@ static enum act_return action_inc_gpc1(struct act_rule *rule, struct proxy *px,
                                        struct session *sess, struct stream *s, int flags)
 {
        struct stksess *ts;
-       struct stkctr *stkctr;
+       struct stkctr *stkctr = NULL;
        unsigned int period = 0;
 
        /* Extract the stksess, return OK if no stksess available. */
-       if (s)
+       if (s && s->stkctr)
                stkctr = &s->stkctr[rule->arg.gpc.sc];
-       else
+       else if (sess->stkctr)
                stkctr = &sess->stkctr[rule->arg.gpc.sc];
+       else
+               return ACT_RET_CONT;
 
        ts = stkctr_entry(stkctr);
        if (ts) {
@@ -2522,6 +2524,11 @@ static enum act_parse_ret parse_inc_gpc(const char **args, int *arg, struct prox
        const char *cmd_name = args[*arg-1];
        char *error;
 
+       if (!global.tune.nb_stk_ctr) {
+               memprintf(err, "Cannot use '%s', stick-counters are disabled via tune.stick-counters", args[*arg-1]);
+               return ACT_RET_PRS_ERR;
+       }
+
        cmd_name += strlen("sc-inc-gpc");
        if (*cmd_name == '(') {
                cmd_name++; /* skip the '(' */
@@ -2538,9 +2545,9 @@ static enum act_parse_ret parse_inc_gpc(const char **args, int *arg, struct prox
                                return ACT_RET_PRS_ERR;
                        }
 
-                       if (rule->arg.gpc.sc >= MAX_SESS_STKCTR) {
-                               memprintf(err, "invalid stick table track ID '%s'. The max allowed ID is %d",
-                                         args[*arg-1], MAX_SESS_STKCTR-1);
+                       if (rule->arg.gpc.sc >= global.tune.nb_stk_ctr) {
+                               memprintf(err, "invalid stick table track ID '%s'. The max allowed ID is %d (tune.stick-counters)",
+                                         args[*arg-1], global.tune.nb_stk_ctr-1);
                                return ACT_RET_PRS_ERR;
                        }
                }
@@ -2566,9 +2573,9 @@ static enum act_parse_ret parse_inc_gpc(const char **args, int *arg, struct prox
                                return ACT_RET_PRS_ERR;
                        }
 
-                       if (rule->arg.gpc.sc >= MAX_SESS_STKCTR) {
-                               memprintf(err, "invalid stick table track ID. The max allowed ID is %d",
-                                         MAX_SESS_STKCTR-1);
+                       if (rule->arg.gpc.sc >= global.tune.nb_stk_ctr) {
+                               memprintf(err, "invalid stick table track ID. The max allowed ID is %d (tune.stick-counters)",
+                                         global.tune.nb_stk_ctr-1);
                                return ACT_RET_PRS_ERR;
                        }
                }
@@ -2599,16 +2606,18 @@ static enum act_return action_set_gpt(struct act_rule *rule, struct proxy *px,
 {
        void *ptr;
        struct stksess *ts;
-       struct stkctr *stkctr;
+       struct stkctr *stkctr = NULL;
        unsigned int value = 0;
        struct sample *smp;
        int smp_opt_dir;
 
        /* Extract the stksess, return OK if no stksess available. */
-       if (s)
+       if (s && s->stkctr)
                stkctr = &s->stkctr[rule->arg.gpt.sc];
-       else
+       else if (sess->stkctr)
                stkctr = &sess->stkctr[rule->arg.gpt.sc];
+       else
+               return ACT_RET_CONT;
 
        ts = stkctr_entry(stkctr);
        if (!ts)
@@ -2663,16 +2672,18 @@ static enum act_return action_set_gpt0(struct act_rule *rule, struct proxy *px,
 {
        void *ptr;
        struct stksess *ts;
-       struct stkctr *stkctr;
+       struct stkctr *stkctr = NULL;
        unsigned int value = 0;
        struct sample *smp;
        int smp_opt_dir;
 
        /* Extract the stksess, return OK if no stksess available. */
-       if (s)
+       if (s && s->stkctr)
                stkctr = &s->stkctr[rule->arg.gpt.sc];
-       else
+       else if (sess->stkctr)
                stkctr = &sess->stkctr[rule->arg.gpt.sc];
+       else
+               return ACT_RET_CONT;
 
        ts = stkctr_entry(stkctr);
        if (!ts)
@@ -2741,6 +2752,11 @@ static enum act_parse_ret parse_set_gpt(const char **args, int *arg, struct prox
        char *error;
        int smp_val;
 
+       if (!global.tune.nb_stk_ctr) {
+               memprintf(err, "Cannot use '%s', stick-counters are disabled via tune.stick-counters", args[*arg-1]);
+               return ACT_RET_PRS_ERR;
+       }
+
        cmd_name += strlen("sc-set-gpt");
        if (*cmd_name == '(') {
                cmd_name++; /* skip the '(' */
@@ -2757,9 +2773,9 @@ static enum act_parse_ret parse_set_gpt(const char **args, int *arg, struct prox
                                return ACT_RET_PRS_ERR;
                        }
 
-                       if (rule->arg.gpt.sc >= MAX_SESS_STKCTR) {
+                       if (rule->arg.gpt.sc >= global.tune.nb_stk_ctr) {
                                memprintf(err, "invalid stick table track ID '%s'. The max allowed ID is %d",
-                                         args[*arg-1], MAX_SESS_STKCTR-1);
+                                         args[*arg-1], global.tune.nb_stk_ctr-1);
                                return ACT_RET_PRS_ERR;
                        }
                }
@@ -2783,9 +2799,9 @@ static enum act_parse_ret parse_set_gpt(const char **args, int *arg, struct prox
                                return ACT_RET_PRS_ERR;
                        }
 
-                       if (rule->arg.gpt.sc >= MAX_SESS_STKCTR) {
+                       if (rule->arg.gpt.sc >= global.tune.nb_stk_ctr) {
                                memprintf(err, "invalid stick table track ID '%s'. The max allowed ID is %d",
-                                         args[*arg-1], MAX_SESS_STKCTR-1);
+                                         args[*arg-1], global.tune.nb_stk_ctr-1);
                                return ACT_RET_PRS_ERR;
                        }
                }
@@ -2914,15 +2930,19 @@ smp_fetch_sc_stkctr(struct session *sess, struct stream *strm, const struct arg
         * the sc[0-9]_ form, or even higher using sc_(num) if needed.
         * args[arg] is the first optional argument. We first lookup the
         * ctr form the stream, then from the session if it was not there.
-        * But we must be sure the counter does not exceed MAX_SESS_STKCTR.
+        * But we must be sure the counter does not exceed global.tune.nb_stk_ctr.
         */
-       if (num >= MAX_SESS_STKCTR)
+       if (num >= global.tune.nb_stk_ctr)
                return NULL;
 
-       if (strm)
+       stkptr = NULL;
+       if (strm && strm->stkctr)
                stkptr = &strm->stkctr[num];
-       if (!strm || !stkctr_entry(stkptr)) {
-               stkptr = &sess->stkctr[num];
+       if (!strm || !stkptr || !stkctr_entry(stkptr)) {
+               if (sess->stkctr)
+                       stkptr = &sess->stkctr[num];
+               else
+                       return NULL;
                if (!stkctr_entry(stkptr))
                        return NULL;
        }
@@ -4990,6 +5010,45 @@ static void cli_release_show_table(struct appctx *appctx)
        }
 }
 
+static int stk_parse_stick_counters(char **args, int section_type, struct proxy *curpx,
+                                const struct proxy *defpx, const char *file, int line,
+                                char **err)
+{
+       char *error;
+       int counters;
+
+       counters = strtol(args[1], &error, 10);
+       if (*error != 0) {
+               memprintf(err, "%s: '%s' is an invalid number", args[0], args[1]);
+               return -1;
+       }
+
+       if (counters < 0) {
+               memprintf(err, "%s: the number of stick-counters may not be negative (was %d)", args[0], counters);
+               return -1;
+       }
+
+       global.tune.nb_stk_ctr = counters;
+       return 0;
+}
+
+/* This function creates the stk_ctr pools after the configuration parsing. It
+ * returns 0 on success otherwise ERR_*. If nb_stk_ctr is 0, the pool remains
+ * NULL.
+ */
+static int stkt_create_stk_ctr_pool(void)
+{
+       if (!global.tune.nb_stk_ctr)
+               return 0;
+
+       pool_head_stk_ctr = create_pool("stk_ctr", sizeof(*((struct session*)0)->stkctr) * global.tune.nb_stk_ctr, MEM_F_SHARED);
+       if (!pool_head_stk_ctr) {
+               ha_alert("out of memory while creating the stick-counters pool.\n");
+               return ERR_ABORT;
+       }
+       return 0;
+}
+
 static void stkt_late_init(void)
 {
        struct sample_fetch *f;
@@ -4997,6 +5056,7 @@ static void stkt_late_init(void)
        f = find_sample_fetch("src", strlen("src"));
        if (f)
                smp_fetch_src = f->process;
+       hap_register_post_check(stkt_create_stk_ctr_pool);
 }
 
 INITCALL0(STG_INIT, stkt_late_init);
@@ -5273,3 +5333,10 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, {
 }};
 
 INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws);
+
+static struct cfg_kw_list cfg_kws = {{ },{
+       { CFG_GLOBAL, "tune.stick-counters", stk_parse_stick_counters },
+       { /* END */ }
+}};
+
+INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
index 006c22964d109d55d0b0eb1a81928d685bb3edff..04b408136fa9d6293d7ff1e866dd087f227f536b 100644 (file)
@@ -388,13 +388,20 @@ struct stream *stream_new(struct session *sess, struct stconn *sc, struct buffer
        s->last_rule_file = NULL;
        s->last_rule_line = 0;
 
-       /* Copy SC counters for the stream. We don't touch refcounts because
-        * any reference we have is inherited from the session. Since the stream
-        * doesn't exist without the session, the session's existence guarantees
-        * we don't lose the entry. During the store operation, the stream won't
-        * touch these ones.
-        */
-       memcpy(s->stkctr, sess->stkctr, sizeof(s->stkctr));
+       s->stkctr = NULL;
+       if (pool_head_stk_ctr) {
+               s->stkctr = pool_alloc(pool_head_stk_ctr);
+               if (!s->stkctr)
+                       goto out_fail_alloc;
+
+               /* Copy SC counters for the stream. We don't touch refcounts because
+                * any reference we have is inherited from the session. Since the stream
+                * doesn't exist without the session, the session's existence guarantees
+                * we don't lose the entry. During the store operation, the stream won't
+                * touch these ones.
+                */
+               memcpy(s->stkctr, sess->stkctr, sizeof(s->stkctr[0]) * global.tune.nb_stk_ctr);
+       }
 
        s->sess = sess;
 
@@ -582,6 +589,8 @@ struct stream *stream_new(struct session *sess, struct stconn *sc, struct buffer
  out_fail_attach_scf:
        task_destroy(t);
  out_fail_alloc:
+       if (s)
+               pool_free(pool_head_stk_ctr, s->stkctr);
        pool_free(pool_head_stream, s);
        DBG_TRACE_DEVEL("leaving on error", STRM_EV_STRM_NEW|STRM_EV_STRM_ERR);
        return NULL;
@@ -701,6 +710,7 @@ void stream_free(struct stream *s)
                vars_prune(&s->vars_reqres, s->sess, s);
 
        stream_store_counters(s);
+       pool_free(pool_head_stk_ctr, s->stkctr);
 
        list_for_each_entry_safe(bref, back, &s->back_refs, users) {
                /* we have to unlink all watchers. We must not relink them if
@@ -797,7 +807,7 @@ void stream_process_counters(struct stream *s)
                if (sess->listener && sess->listener->counters)
                        _HA_ATOMIC_ADD(&sess->listener->counters->bytes_in, bytes);
 
-               for (i = 0; i < MAX_SESS_STKCTR; i++) {
+               for (i = 0; i < global.tune.nb_stk_ctr; i++) {
                        if (!stkctr_inc_bytes_in_ctr(&s->stkctr[i], bytes))
                                stkctr_inc_bytes_in_ctr(&sess->stkctr[i], bytes);
                }
@@ -815,7 +825,7 @@ void stream_process_counters(struct stream *s)
                if (sess->listener && sess->listener->counters)
                        _HA_ATOMIC_ADD(&sess->listener->counters->bytes_out, bytes);
 
-               for (i = 0; i < MAX_SESS_STKCTR; i++) {
+               for (i = 0; i < global.tune.nb_stk_ctr; i++) {
                        if (!stkctr_inc_bytes_out_ctr(&s->stkctr[i], bytes))
                                stkctr_inc_bytes_out_ctr(&sess->stkctr[i], bytes);
                }
index e649794951f2304ec164b65b867af3e2710f8c2b..6465e6ed17a874a2539ad17b2b380c818eb526dd 100644 (file)
@@ -1058,7 +1058,7 @@ static int tcp_parse_request_rule(char **args, int arg, int section_type,
                        memprintf(err,
                                  "'%s %s' expects 'accept', 'reject', 'capture', 'expect-proxy', 'expect-netscaler-cip', 'track-sc0' ... 'track-sc%d', %s "
                                  "in %s '%s' (got '%s').%s%s%s\n",
-                                 args[0], args[1], MAX_SESS_STKCTR-1,
+                                 args[0], args[1], global.tune.nb_stk_ctr-1,
                                  trash.area, proxy_type_str(curpx),
                                  curpx->id, args[arg],
                                  best ? " Did you mean '" : "",