]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: threads/stick-tables: handle multithreads on stick tables
authorEmeric Brun <ebrun@haproxy.com>
Tue, 13 Jun 2017 17:37:32 +0000 (19:37 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 31 Oct 2017 12:58:31 +0000 (13:58 +0100)
The stick table API was slightly reworked:

A global spin lock on stick table was added to perform lookup and
insert in a thread safe way. The handling of refcount on entries
is now handled directly by stick tables functions under protection
of this lock and was removed from the code of callers.

The "stktable_store" function is no more externalized and users should
now use "stktable_set_entry" in any case of insertion. This last one performs
a lookup followed by a store if not found. So the code using "stktable_store"
was re-worked.

Lookup, and set_entry functions automatically increase the refcount
of the returned/stored entry.

The function "sticktable_touch" was renamed "sticktable_touch_local"
and is now able to decrease the refcount if last arg is set to true. It
is allowing to release the entry without taking the lock twice.

A new function "sticktable_touch_remote" is now used to insert
entries coming from remote peers at the right place in the update tree.
The code of peer update was re-worked to use this new function.
This function is also able to decrease the refcount if wanted.

The function "stksess_kill" also handle a parameter to decrease
the refcount on the entry.

A read/write lock is added on each entry to protect the data content
updates of the entry.

include/common/hathreads.h
include/proto/session.h
include/proto/stick_table.h
include/proto/stream.h
include/types/stick_table.h
src/peers.c
src/proto_http.c
src/stick_table.c
src/stream.c

index 9946d51163ab6eb954a9c83feec5b5fc1fdf0255..e997ea3a9e5a4ea0dac86b01f0abb99a11ff38a6 100644 (file)
@@ -152,6 +152,8 @@ enum lock_label {
        UPDATED_SERVERS_LOCK,
        LBPRM_LOCK,
        SIGNALS_LOCK,
+       STK_TABLE_LOCK,
+       STK_SESS_LOCK,
        LOCK_LABELS
 };
 struct lock_stat {
@@ -237,7 +239,7 @@ static inline void show_lock_stats()
        const char *labels[LOCK_LABELS] = {"THREAD_SYNC", "FDTAB", "FDCACHE", "FD", "POLL",
                                           "TASK_RQ", "TASK_WQ", "POOL",
                                           "LISTENER", "LISTENER_QUEUE", "PROXY", "SERVER",
-                                          "UPDATED_SERVERS", "LBPRM", "SIGNALS" };
+                                          "UPDATED_SERVERS", "LBPRM", "SIGNALS", "STK_TABLE", "STK_SESS" };
        int lbl;
 
        for (lbl = 0; lbl < LOCK_LABELS; lbl++) {
index cb4deeceb5db6e88ede9f167963c4d6bb160195f..3dead44488029aec47c5c76b44bdde009e7b034c 100644 (file)
@@ -46,19 +46,26 @@ static inline void session_store_counters(struct session *sess)
 {
        void *ptr;
        int i;
+       struct stksess *ts;
 
        for (i = 0; i < MAX_SESS_STKCTR; i++) {
                struct stkctr *stkctr = &sess->stkctr[i];
 
-               if (!stkctr_entry(stkctr))
+               ts = stkctr_entry(stkctr);
+               if (!ts)
                        continue;
 
-               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_CONN_CUR);
-               if (ptr)
+               ptr = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_CONN_CUR);
+               if (ptr) {
+                       RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
+
                        stktable_data_cast(ptr, conn_cur)--;
-               stkctr_entry(stkctr)->ref_cnt--;
-               stksess_kill_if_expired(stkctr->table, stkctr_entry(stkctr));
+
+                       RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+               }
+
                stkctr_set_entry(stkctr, NULL);
+               stksess_kill_if_expired(stkctr->table, ts, 1);
        }
 }
 
index f48b9eb496dbc4d6fac333af17da00eed007848d..8c9f834526664d72d3ca33fffa86c1e6061b8478 100644 (file)
 struct stksess *stksess_new(struct stktable *t, struct stktable_key *key);
 void stksess_setkey(struct stktable *t, struct stksess *ts, struct stktable_key *key);
 void stksess_free(struct stktable *t, struct stksess *ts);
-void stksess_kill(struct stktable *t, struct stksess *ts);
+int stksess_kill(struct stktable *t, struct stksess *ts, int decrefcount);
 
 int stktable_init(struct stktable *t);
 int stktable_parse_type(char **args, int *idx, unsigned long *type, size_t *key_size);
 struct stksess *stktable_get_entry(struct stktable *table, struct stktable_key *key);
-struct stksess *stktable_store(struct stktable *t, struct stksess *ts, int local);
-struct stksess *stktable_store_with_exp(struct stktable *t, struct stksess *ts,
-                                        int local, int expire);
-struct stksess *stktable_touch_with_exp(struct stktable *t, struct stksess *ts,
-                                        int local, int expire);
-struct stksess *stktable_touch(struct stktable *t, struct stksess *ts, int local);
+struct stksess *stktable_set_entry(struct stktable *table, struct stksess *nts);
+void stktable_touch_with_exp(struct stktable *t, struct stksess *ts, int decrefcount, int expire);
+void stktable_touch_remote(struct stktable *t, struct stksess *ts, int decrefcnt);
+void stktable_touch_local(struct stktable *t, struct stksess *ts, int decrefccount);
 struct stksess *stktable_lookup(struct stktable *t, struct stksess *ts);
 struct stksess *stktable_lookup_key(struct stktable *t, struct stktable_key *key);
 struct stksess *stktable_update_key(struct stktable *table, struct stktable_key *key);
@@ -52,12 +50,13 @@ struct stktable_key *smp_to_stkey(struct sample *smp, struct stktable *t);
 struct stktable_key *stktable_fetch_key(struct stktable *t, struct proxy *px, struct session *sess,
                                         struct stream *strm, unsigned int opt,
                                         struct sample_expr *expr, struct sample *smp);
-struct stkctr *smp_fetch_sc_stkctr(struct session *sess, struct stream *strm, const struct arg *args, const char *kw);
-struct stkctr *smp_create_src_stkctr(struct session *sess, struct stream *strm, const struct arg *args, const char *kw);
+struct stkctr *smp_fetch_sc_stkctr(struct session *sess, struct stream *strm, const struct arg *args, const char *kw, struct stkctr *stkctr);
+struct stkctr *smp_create_src_stkctr(struct session *sess, struct stream *strm, const struct arg *args, const char *kw, struct stkctr *stkctr);
 int stktable_compatible_sample(struct sample_expr *expr, unsigned long table_type);
 int stktable_register_data_store(int idx, const char *name, int std_type, int arg_type);
 int stktable_get_data_type(char *name);
 int stktable_trash_oldest(struct stktable *t, int to_batch);
+int __stksess_kill(struct stktable *t, struct stksess *ts);
 
 /* return allocation size for standard data type <type> */
 static inline int stktable_type_size(int type)
@@ -132,10 +131,29 @@ static inline void *stktable_data_ptr(struct stktable *t, struct stksess *ts, in
 }
 
 /* kill an entry if it's expired and its ref_cnt is zero */
-static inline void stksess_kill_if_expired(struct stktable *t, struct stksess *ts)
+static inline int __stksess_kill_if_expired(struct stktable *t, struct stksess *ts)
 {
        if (t->expire != TICK_ETERNITY && tick_is_expired(ts->expire, now_ms))
-               stksess_kill(t, ts);
+               return __stksess_kill(t, ts);
+
+       return 0;
+}
+
+static inline int stksess_kill_if_expired(struct stktable *t, struct stksess *ts, int decrefcnt)
+{
+       int ret;
+
+       SPIN_LOCK(STK_TABLE_LOCK, &t->lock);
+
+       if (decrefcnt)
+               ts->ref_cnt--;
+
+       if (t->expire != TICK_ETERNITY && tick_is_expired(ts->expire, now_ms))
+               ret = __stksess_kill_if_expired(t, ts);
+
+       SPIN_UNLOCK(STK_TABLE_LOCK, &t->lock);
+
+       return ret;
 }
 
 /* sets the stick counter's entry pointer */
index aae7d345dc1270f3f3007f45c7c86d56311d77c7..3efb42bacf44a0585d0a70365eaafec5e6ee80ba 100644 (file)
@@ -90,20 +90,26 @@ static inline void stream_store_counters(struct stream *s)
 {
        void *ptr;
        int i;
+       struct stksess *ts;
 
        for (i = 0; i < MAX_SESS_STKCTR; i++) {
-               if (!stkctr_entry(&s->stkctr[i]))
+               ts = stkctr_entry(&s->stkctr[i]);
+               if (!ts)
                        continue;
 
                if (stkctr_entry(&s->sess->stkctr[i]))
                        continue;
 
-               ptr = stktable_data_ptr(s->stkctr[i].table, stkctr_entry(&s->stkctr[i]), STKTABLE_DT_CONN_CUR);
-               if (ptr)
+               ptr = stktable_data_ptr(s->stkctr[i].table, ts, STKTABLE_DT_CONN_CUR);
+               if (ptr) {
+                       RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
+
                        stktable_data_cast(ptr, conn_cur)--;
-               stkctr_entry(&s->stkctr[i])->ref_cnt--;
-               stksess_kill_if_expired(s->stkctr[i].table, stkctr_entry(&s->stkctr[i]));
+
+                       RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+               }
                stkctr_set_entry(&s->stkctr[i], NULL);
+               stksess_kill_if_expired(s->stkctr[i].table, ts, 1);
        }
 }
 
@@ -114,11 +120,13 @@ static inline void stream_store_counters(struct stream *s)
  */
 static inline void stream_stop_content_counters(struct stream *s)
 {
+       struct stksess *ts;
        void *ptr;
        int i;
 
        for (i = 0; i < MAX_SESS_STKCTR; i++) {
-               if (!stkctr_entry(&s->stkctr[i]))
+               ts = stkctr_entry(&s->stkctr[i]);
+               if (!ts)
                        continue;
 
                if (stkctr_entry(&s->sess->stkctr[i]))
@@ -127,12 +135,16 @@ static inline void stream_stop_content_counters(struct stream *s)
                if (!(stkctr_flags(&s->stkctr[i]) & STKCTR_TRACK_CONTENT))
                        continue;
 
-               ptr = stktable_data_ptr(s->stkctr[i].table, stkctr_entry(&s->stkctr[i]), STKTABLE_DT_CONN_CUR);
-               if (ptr)
+               ptr = stktable_data_ptr(s->stkctr[i].table, ts, STKTABLE_DT_CONN_CUR);
+               if (ptr) {
+                       RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
+
                        stktable_data_cast(ptr, conn_cur)--;
-               stkctr_entry(&s->stkctr[i])->ref_cnt--;
-               stksess_kill_if_expired(s->stkctr[i].table, stkctr_entry(&s->stkctr[i]));
+
+                       RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+               }
                stkctr_set_entry(&s->stkctr[i], NULL);
+               stksess_kill_if_expired(s->stkctr[i].table, ts, 1);
        }
 }
 
@@ -144,6 +156,8 @@ static inline void stream_start_counters(struct stktable *t, struct stksess *ts)
 {
        void *ptr;
 
+       RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
+
        ptr = stktable_data_ptr(t, ts, STKTABLE_DT_CONN_CUR);
        if (ptr)
                stktable_data_cast(ptr, conn_cur)++;
@@ -158,6 +172,8 @@ static inline void stream_start_counters(struct stktable *t, struct stksess *ts)
                                       t->data_arg[STKTABLE_DT_CONN_RATE].u, 1);
        if (tick_isset(t->expire))
                ts->expire = tick_add(now_ms, MS_TO_TICKS(t->expire));
+
+       RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
 }
 
 /* Enable tracking of stream counters as <stkctr> on stksess <ts>. The caller is
@@ -166,10 +182,10 @@ static inline void stream_start_counters(struct stktable *t, struct stksess *ts)
  */
 static inline void stream_track_stkctr(struct stkctr *ctr, struct stktable *t, struct stksess *ts)
 {
+       /* Why this test ???? */
        if (stkctr_entry(ctr))
                return;
 
-       ts->ref_cnt++;
        ctr->table = t;
        stkctr_set_entry(ctr, ts);
        stream_start_counters(t, ts);
@@ -178,26 +194,33 @@ static inline void stream_track_stkctr(struct stkctr *ctr, struct stktable *t, s
 /* Increase the number of cumulated HTTP requests in the tracked counters */
 static void inline stream_inc_http_req_ctr(struct stream *s)
 {
+       struct stksess *ts;
        void *ptr;
        int i;
 
        for (i = 0; i < MAX_SESS_STKCTR; i++) {
                struct stkctr *stkctr = &s->stkctr[i];
 
-               if (!stkctr_entry(stkctr)) {
+               ts = stkctr_entry(stkctr);
+               if (!ts) {
                        stkctr = &s->sess->stkctr[i];
-                       if (!stkctr_entry(stkctr))
+                       ts = stkctr_entry(stkctr);
+                       if (!ts)
                                continue;
                }
 
-               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_HTTP_REQ_CNT);
+               RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
+
+               ptr = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_HTTP_REQ_CNT);
                if (ptr)
                        stktable_data_cast(ptr, http_req_cnt)++;
 
-               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_HTTP_REQ_RATE);
+               ptr = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_HTTP_REQ_RATE);
                if (ptr)
                        update_freq_ctr_period(&stktable_data_cast(ptr, http_req_rate),
                                               stkctr->table->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
+
+               RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
        }
 }
 
@@ -206,26 +229,32 @@ static void inline stream_inc_http_req_ctr(struct stream *s)
  */
 static void inline stream_inc_be_http_req_ctr(struct stream *s)
 {
+       struct stksess *ts;
        void *ptr;
        int i;
 
        for (i = 0; i < MAX_SESS_STKCTR; i++) {
                struct stkctr *stkctr = &s->stkctr[i];
 
-               if (!stkctr_entry(stkctr))
+               ts = stkctr_entry(stkctr);
+               if (!ts)
                        continue;
 
                if (!(stkctr_flags(&s->stkctr[i]) & STKCTR_TRACK_BACKEND))
                        continue;
 
-               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_HTTP_REQ_CNT);
+               RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
+
+               ptr = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_HTTP_REQ_CNT);
                if (ptr)
                        stktable_data_cast(ptr, http_req_cnt)++;
 
-               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_HTTP_REQ_RATE);
+               ptr = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_HTTP_REQ_RATE);
                if (ptr)
                        update_freq_ctr_period(&stktable_data_cast(ptr, http_req_rate),
                                               stkctr->table->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
+
+               RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
        }
 }
 
@@ -237,26 +266,33 @@ static void inline stream_inc_be_http_req_ctr(struct stream *s)
  */
 static void inline stream_inc_http_err_ctr(struct stream *s)
 {
+       struct stksess *ts;
        void *ptr;
        int i;
 
        for (i = 0; i < MAX_SESS_STKCTR; i++) {
                struct stkctr *stkctr = &s->stkctr[i];
 
-               if (!stkctr_entry(stkctr)) {
+               ts = stkctr_entry(stkctr);
+               if (!ts) {
                        stkctr = &s->sess->stkctr[i];
-                       if (!stkctr_entry(stkctr))
+                       ts = stkctr_entry(stkctr);
+                       if (!ts)
                                continue;
                }
 
-               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_HTTP_ERR_CNT);
+               RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
+
+               ptr = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_HTTP_ERR_CNT);
                if (ptr)
                        stktable_data_cast(ptr, http_err_cnt)++;
 
-               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_HTTP_ERR_RATE);
+               ptr = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_HTTP_ERR_RATE);
                if (ptr)
                        update_freq_ctr_period(&stktable_data_cast(ptr, http_err_rate),
                                               stkctr->table->data_arg[STKTABLE_DT_HTTP_ERR_RATE].u, 1);
+
+               RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
        }
 }
 
index 77eeccdc2de089496062724abfb569fc9514ad32..4f5de998659fffcfbcbbf0739bc2e550a29e5ef4 100644 (file)
@@ -129,6 +129,9 @@ extern struct stktable_type stktable_types[];
 struct stksess {
        unsigned int expire;      /* session expiration date */
        unsigned int ref_cnt;     /* reference count, can only purge when zero */
+#ifdef USE_THREAD
+       HA_RWLOCK_T lock;         /* lock related to the table entry */
+#endif
        struct eb32_node exp;     /* ebtree node used to hold the session in expiration tree */
        struct eb32_node upd;     /* ebtree node used to hold the update sequence tree */
        struct ebmb_node key;     /* ebtree node used to hold the session in table */
@@ -143,6 +146,9 @@ struct stktable {
        struct eb_root exps;      /* head of sticky session expiration tree */
        struct eb_root updates;   /* head of sticky updates sequence tree */
        struct pool_head *pool;   /* pool used to allocate sticky sessions */
+#ifdef USE_THREAD
+       HA_SPINLOCK_T lock;       /* spin lock related to the table */
+#endif
        struct task *exp_task;    /* expiration task */
        struct task *sync_task;   /* sync task */
        unsigned int update;
index 579e096d71ebea62bc911c77ca34c1493b97551e..25f1ba32b7c864353276c2d6158a9f671f8353ec 100644 (file)
@@ -270,7 +270,7 @@ static inline void peer_set_update_msg_type(char *msg_type, int use_identifier,
  * If function returns 0, the caller should consider we were unable to encode this message (TODO:
  * check size)
  */
-static int peer_prepare_updatemsg(struct stksess *ts, struct shared_table *st, char *msg, size_t size, int use_identifier, int use_timed)
+static int peer_prepare_updatemsg(struct stksess *ts, struct shared_table *st, unsigned int updateid, char *msg, size_t size, int use_identifier, int use_timed)
 {
        uint32_t netinteger;
        unsigned short datalen;
@@ -283,13 +283,13 @@ static int peer_prepare_updatemsg(struct stksess *ts, struct shared_table *st, c
        /* construct message */
 
        /* check if we need to send the update identifer */
-       if (!st->last_pushed || ts->upd.key < st->last_pushed || ((ts->upd.key - st->last_pushed) != 1)) {
+       if (!st->last_pushed || updateid < st->last_pushed || ((updateid - st->last_pushed) != 1)) {
                use_identifier = 1;
        }
 
        /* encode update identifier if needed */
        if (use_identifier)  {
-               netinteger = htonl(ts->upd.key);
+               netinteger = htonl(updateid);
                memcpy(cursor, &netinteger, sizeof(netinteger));
                cursor += sizeof(netinteger);
        }
@@ -318,6 +318,7 @@ static int peer_prepare_updatemsg(struct stksess *ts, struct shared_table *st, c
                cursor += st->table->key_size;
        }
 
+       RWLOCK_RDLOCK(STK_SESS_LOCK, &ts->lock);
        /* encode values */
        for (data_type = 0 ; data_type < STKTABLE_DATA_TYPES ; data_type++) {
 
@@ -357,6 +358,7 @@ static int peer_prepare_updatemsg(struct stksess *ts, struct shared_table *st, c
                        }
                }
        }
+       RWLOCK_RDUNLOCK(STK_SESS_LOCK, &ts->lock);
 
        /* Compute datalen */
        datalen = (cursor - datamsg);
@@ -1152,7 +1154,9 @@ switchstate:
                                                newts = stksess_new(st->table, NULL);
                                                if (!newts)
                                                        goto ignore_msg;
-
+                                               /* Force expiratiion to remote date
+                                                  in case of first insert */
+                                               newts->expire = tick_add(now_ms, expire);
                                                if (st->table->type == SMP_T_STR) {
                                                        unsigned int to_read, to_store;
 
@@ -1201,27 +1205,13 @@ switchstate:
                                                }
 
                                                /* lookup for existing entry */
-                                               ts = stktable_lookup(st->table, newts);
-                                               if (ts) {
-                                                       /* the entry already exist, we can free ours */
-                                                       stktable_touch_with_exp(st->table, ts, 0, tick_add(now_ms, expire));
+                                               ts = stktable_set_entry(st->table, newts);
+                                               if (ts != newts) {
                                                        stksess_free(st->table, newts);
                                                        newts = NULL;
                                                }
-                                               else {
-                                                       struct eb32_node *eb;
-
-                                                       /* create new entry */
-                                                       ts = stktable_store_with_exp(st->table, newts, 0, tick_add(now_ms, expire));
-                                                       newts = NULL; /* don't reuse it */
 
-                                                       ts->upd.key= (++st->table->update)+(2147483648U);
-                                                       eb = eb32_insert(&st->table->updates, &ts->upd);
-                                                       if (eb != &ts->upd) {
-                                                               eb32_delete(eb);
-                                                               eb32_insert(&st->table->updates, &ts->upd);
-                                                       }
-                                               }
+                                               RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
 
                                                for (data_type = 0 ; data_type < STKTABLE_DATA_TYPES ; data_type++) {
 
@@ -1233,6 +1223,8 @@ switchstate:
                                                                                data = intdecode(&msg_cur, msg_end);
                                                                                if (!msg_cur) {
                                                                                        /* malformed message */
+                                                                                       RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+                                                                                       stktable_touch_remote(st->table, ts, 1);
                                                                                        appctx->st0 = PEER_SESS_ST_ERRPROTO;
                                                                                        goto switchstate;
                                                                                }
@@ -1248,6 +1240,8 @@ switchstate:
                                                                                data = intdecode(&msg_cur, msg_end);
                                                                                if (!msg_cur) {
                                                                                        /* malformed message */
+                                                                                       RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+                                                                                       stktable_touch_remote(st->table, ts, 1);
                                                                                        appctx->st0 = PEER_SESS_ST_ERRPROTO;
                                                                                        goto switchstate;
                                                                                }
@@ -1263,6 +1257,8 @@ switchstate:
                                                                                data = intdecode(&msg_cur, msg_end);
                                                                                if (!msg_cur) {
                                                                                        /* malformed message */
+                                                                                       RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+                                                                                       stktable_touch_remote(st->table, ts, 1);
                                                                                        appctx->st0 = PEER_SESS_ST_ERRPROTO;
                                                                                        goto switchstate;
                                                                                }
@@ -1278,18 +1274,24 @@ switchstate:
                                                                                data.curr_tick = tick_add(now_ms, -intdecode(&msg_cur, msg_end));
                                                                                if (!msg_cur) {
                                                                                        /* malformed message */
+                                                                                       RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+                                                                                       stktable_touch_remote(st->table, ts, 1);
                                                                                        appctx->st0 = PEER_SESS_ST_ERRPROTO;
                                                                                        goto switchstate;
                                                                                }
                                                                                data.curr_ctr = intdecode(&msg_cur, msg_end);
                                                                                if (!msg_cur) {
                                                                                        /* malformed message */
+                                                                                       RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+                                                                                       stktable_touch_remote(st->table, ts, 1);
                                                                                        appctx->st0 = PEER_SESS_ST_ERRPROTO;
                                                                                        goto switchstate;
                                                                                }
                                                                                data.prev_ctr = intdecode(&msg_cur, msg_end);
                                                                                if (!msg_cur) {
                                                                                        /* malformed message */
+                                                                                       RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+                                                                                       stktable_touch_remote(st->table, ts, 1);
                                                                                        appctx->st0 = PEER_SESS_ST_ERRPROTO;
                                                                                        goto switchstate;
                                                                                }
@@ -1302,6 +1304,10 @@ switchstate:
                                                                }
                                                        }
                                                }
+
+                                               RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+                                               stktable_touch_remote(st->table, ts, 1);
+
                                        }
                                        else if (msg_head[1] == PEER_MSG_STKT_ACK) {
                                                /* ack message */
@@ -1441,12 +1447,14 @@ incomplete:
 
                                                                /* We force new pushed to 1 to force identifier in update message */
                                                                new_pushed = 1;
-                                                               eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
+                                                               SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
                                                                while (1) {
                                                                        uint32_t msglen;
                                                                        struct stksess *ts;
+                                                                       unsigned updateid;
 
                                                                        /* push local updates */
+                                                                       eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
                                                                        if (!eb) {
                                                                                eb = eb32_first(&st->table->updates);
                                                                                if (!eb || ((int)(eb->key - st->last_pushed) <= 0)) {
@@ -1461,9 +1469,16 @@ incomplete:
                                                                        }
 
                                                                        ts = eb32_entry(eb, struct stksess, upd);
-                                                                       msglen = peer_prepare_updatemsg(ts, st, trash.str, trash.size, new_pushed, 0);
+                                                                       updateid = ts->upd.key;
+                                                                       ts->ref_cnt++;
+                                                                       SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
+
+                                                                       msglen = peer_prepare_updatemsg(ts, st, updateid, trash.str, trash.size, new_pushed, 0);
                                                                        if (!msglen) {
                                                                                /* internal error: message does not fit in trash */
+                                                                               SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
+                                                                               ts->ref_cnt--;
+                                                                               SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
                                                                                appctx->st0 = PEER_SESS_ST_END;
                                                                                goto switchstate;
                                                                        }
@@ -1472,20 +1487,25 @@ incomplete:
                                                                        repl = ci_putblk(si_ic(si), trash.str, msglen);
                                                                        if (repl <= 0) {
                                                                                /* no more write possible */
+                                                                               SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
+                                                                               ts->ref_cnt--;
+                                                                               SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
                                                                                if (repl == -1) {
                                                                                        goto full;
                                                                                }
                                                                                appctx->st0 = PEER_SESS_ST_END;
                                                                                goto switchstate;
                                                                        }
-                                                                       st->last_pushed = ts->upd.key;
+
+                                                                       SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
+                                                                       ts->ref_cnt--;
+                                                                       st->last_pushed = updateid;
                                                                        if ((int)(st->last_pushed - st->table->commitupdate) > 0)
                                                                                        st->table->commitupdate = st->last_pushed;
                                                                        /* identifier may not needed in next update message */
                                                                        new_pushed = 0;
-
-                                                                       eb = eb32_next(eb);
                                                                }
+                                                               SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
                                                        }
                                                }
                                                else {
@@ -1518,13 +1538,15 @@ incomplete:
 
                                                                /* We force new pushed to 1 to force identifier in update message */
                                                                new_pushed = 1;
-                                                               eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
+                                                               SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
                                                                while (1) {
                                                                        uint32_t msglen;
                                                                        struct stksess *ts;
                                                                        int use_timed;
+                                                                       unsigned updateid;
 
                                                                        /* push local updates */
+                                                                       eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
                                                                        if (!eb) {
                                                                                st->flags |= SHTABLE_F_TEACH_STAGE1;
                                                                                eb = eb32_first(&st->table->updates);
@@ -1534,10 +1556,17 @@ incomplete:
                                                                        }
 
                                                                        ts = eb32_entry(eb, struct stksess, upd);
+                                                                       updateid = ts->upd.key;
+                                                                       ts->ref_cnt++;
+                                                                       SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
+
                                                                        use_timed = !(curpeer->flags & PEER_F_DWNGRD);
-                                                                       msglen = peer_prepare_updatemsg(ts, st, trash.str, trash.size, new_pushed, use_timed);
+                                                                       msglen = peer_prepare_updatemsg(ts, st, updateid, trash.str, trash.size, new_pushed, use_timed);
                                                                        if (!msglen) {
                                                                                /* internal error: message does not fit in trash */
+                                                                               SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
+                                                                               ts->ref_cnt--;
+                                                                               SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
                                                                                appctx->st0 = PEER_SESS_ST_END;
                                                                                goto switchstate;
                                                                        }
@@ -1546,18 +1575,22 @@ incomplete:
                                                                        repl = ci_putblk(si_ic(si), trash.str, msglen);
                                                                        if (repl <= 0) {
                                                                                /* no more write possible */
+                                                                               SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
+                                                                               ts->ref_cnt--;
+                                                                               SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
                                                                                if (repl == -1) {
                                                                                        goto full;
                                                                                }
                                                                                appctx->st0 = PEER_SESS_ST_END;
                                                                                goto switchstate;
                                                                        }
-                                                                       st->last_pushed = ts->upd.key;
+                                                                       SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
+                                                                       ts->ref_cnt--;
+                                                                       st->last_pushed = updateid;
                                                                        /* identifier may not needed in next update message */
                                                                        new_pushed = 0;
-
-                                                                       eb = eb32_next(eb);
                                                                }
+                                                               SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
                                                        }
 
                                                        if (!(st->flags & SHTABLE_F_TEACH_STAGE2)) {
@@ -1589,11 +1622,15 @@ incomplete:
 
                                                                /* We force new pushed to 1 to force identifier in update message */
                                                                new_pushed = 1;
-                                                               eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
+                                                               SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
                                                                while (1) {
                                                                        uint32_t msglen;
                                                                        struct stksess *ts;
                                                                        int use_timed;
+                                                                       unsigned updateid;
+
+                                                                       /* push local updates */
+                                                                       eb = eb32_lookup_ge(&st->table->updates, st->last_pushed+1);
 
                                                                        /* push local updates */
                                                                        if (!eb || eb->key > st->teaching_origin) {
@@ -1602,10 +1639,17 @@ incomplete:
                                                                        }
 
                                                                        ts = eb32_entry(eb, struct stksess, upd);
+                                                                       updateid = ts->upd.key;
+                                                                       ts->ref_cnt++;
+                                                                       SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
+
                                                                        use_timed = !(curpeer->flags & PEER_F_DWNGRD);
-                                                                       msglen = peer_prepare_updatemsg(ts, st, trash.str, trash.size, new_pushed, use_timed);
+                                                                       msglen = peer_prepare_updatemsg(ts, st, updateid, trash.str, trash.size, new_pushed, use_timed);
                                                                        if (!msglen) {
                                                                                /* internal error: message does not fit in trash */
+                                                                               SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
+                                                                               ts->ref_cnt--;
+                                                                               SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
                                                                                appctx->st0 = PEER_SESS_ST_END;
                                                                                goto switchstate;
                                                                        }
@@ -1614,18 +1658,23 @@ incomplete:
                                                                        repl = ci_putblk(si_ic(si), trash.str, msglen);
                                                                        if (repl <= 0) {
                                                                                /* no more write possible */
+                                                                               SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
+                                                                               ts->ref_cnt--;
+                                                                               SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
                                                                                if (repl == -1) {
                                                                                        goto full;
                                                                                }
                                                                                appctx->st0 = PEER_SESS_ST_END;
                                                                                goto switchstate;
                                                                        }
-                                                                       st->last_pushed = ts->upd.key;
+
+                                                                       SPIN_LOCK(STK_TABLE_LOCK, &st->table->lock);
+                                                                       ts->ref_cnt--;
+                                                                       st->last_pushed = updateid;
                                                                        /* identifier may not needed in next update message */
                                                                        new_pushed = 0;
-
-                                                                       eb = eb32_next(eb);
                                                                }
+                                                               SPIN_UNLOCK(STK_TABLE_LOCK, &st->table->lock);
                                                        }
                                                }
 
index f60f8ed1c7b19a3dce2b4771c5c178be594fa29f..d32b7c5e8086d287e6b8889eabf52c02e2c4040f 100644 (file)
@@ -2721,7 +2721,7 @@ resume_execution:
                                struct stktable *t;
                                struct stksess *ts;
                                struct stktable_key *key;
-                               void *ptr;
+                               void *ptr1, *ptr2;
 
                                t = rule->arg.trk_ctr.table.t;
                                key = stktable_fetch_key(t, s->be, sess, s, SMP_OPT_DIR_REQ | SMP_OPT_FINAL, rule->arg.trk_ctr.expr, NULL);
@@ -2730,14 +2730,20 @@ resume_execution:
                                        stream_track_stkctr(&s->stkctr[trk_idx(rule->action)], t, ts);
 
                                        /* let's count a new HTTP request as it's the first time we do it */
-                                       ptr = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_CNT);
-                                       if (ptr)
-                                               stktable_data_cast(ptr, http_req_cnt)++;
+                                       ptr1 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_CNT);
+                                       ptr2 = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_RATE);
+                                       if (ptr1 || ptr2) {
+                                               RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
 
-                                       ptr = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_RATE);
-                                       if (ptr)
-                                               update_freq_ctr_period(&stktable_data_cast(ptr, http_req_rate),
-                                                                      t->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
+                                               if (ptr1)
+                                                       stktable_data_cast(ptr1, http_req_cnt)++;
+
+                                               if (ptr2)
+                                                       update_freq_ctr_period(&stktable_data_cast(ptr2, http_req_rate),
+                                                                              t->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
+
+                                               RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+                                       }
 
                                        stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_CONTENT);
                                        if (sess->fe != s->be)
@@ -3002,6 +3008,8 @@ resume_execution:
                                if (key && (ts = stktable_get_entry(t, key))) {
                                        stream_track_stkctr(&s->stkctr[trk_idx(rule->action)], t, ts);
 
+                                       RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
+
                                        /* let's count a new HTTP request as it's the first time we do it */
                                        ptr = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_CNT);
                                        if (ptr)
@@ -3012,10 +3020,6 @@ resume_execution:
                                                update_freq_ctr_period(&stktable_data_cast(ptr, http_req_rate),
                                                                                           t->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
 
-                                       stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_CONTENT);
-                                       if (sess->fe != s->be)
-                                               stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_BACKEND);
-
                                        /* When the client triggers a 4xx from the server, it's most often due
                                         * to a missing object or permission. These events should be tracked
                                         * because if they happen often, it may indicate a brute force or a
@@ -3033,6 +3037,13 @@ resume_execution:
                                                        update_freq_ctr_period(&stktable_data_cast(ptr, http_err_rate),
                                                                               t->data_arg[STKTABLE_DT_HTTP_ERR_RATE].u, 1);
                                        }
+
+                                       RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+
+                                       stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_CONTENT);
+                                       if (sess->fe != s->be)
+                                               stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_BACKEND);
+
                                }
                        }
                        break;
index 5e8211623cda3f9a0a5857b3e9141c2bef76a829..55a1fff8da8ed76fca60b88814516333a907d135 100644 (file)
 #include <proto/tcp_rules.h>
 
 /* structure used to return a table key built from a sample */
-static struct stktable_key static_table_key;
+static THREAD_LOCAL struct stktable_key static_table_key;
 
 /*
  * Free an allocated sticky session <ts>, and decrease sticky sessions counter
  * in table <t>.
  */
-void stksess_free(struct stktable *t, struct stksess *ts)
+void __stksess_free(struct stktable *t, struct stksess *ts)
 {
        t->current--;
        pool_free2(t->pool, (void *)ts - t->data_size);
 }
 
+/*
+ * Free an allocated sticky session <ts>, and decrease sticky sessions counter
+ * in table <t>.
+ * This function locks the table
+ */
+void stksess_free(struct stktable *t, struct stksess *ts)
+{
+       SPIN_LOCK(STK_TABLE_LOCK, &t->lock);
+       __stksess_free(t, ts);
+       SPIN_UNLOCK(STK_TABLE_LOCK, &t->lock);
+}
+
 /*
  * Kill an stksess (only if its ref_cnt is zero).
  */
-void stksess_kill(struct stktable *t, struct stksess *ts)
+int __stksess_kill(struct stktable *t, struct stksess *ts)
 {
        if (ts->ref_cnt)
-               return;
+               return 0;
 
        eb32_delete(&ts->exp);
        eb32_delete(&ts->upd);
        ebmb_delete(&ts->key);
-       stksess_free(t, ts);
+       __stksess_free(t, ts);
+       return 1;
+}
+
+/*
+ * Decrease the refcount if decrefcnt is not 0.
+ * and try to kill the stksess
+ * This function locks the table
+ */
+int stksess_kill(struct stktable *t, struct stksess *ts, int decrefcnt)
+{
+       int ret;
+
+       SPIN_LOCK(STK_TABLE_LOCK, &t->lock);
+       if (decrefcnt)
+               ts->ref_cnt--;
+       ret = __stksess_kill(t, ts);
+       SPIN_UNLOCK(STK_TABLE_LOCK, &t->lock);
+
+       return ret;
 }
 
 /*
@@ -87,13 +118,15 @@ void stksess_setkey(struct stktable *t, struct stksess *ts, struct stktable_key
  * Init sticky session <ts> of table <t>. The data parts are cleared and <ts>
  * is returned.
  */
-static struct stksess *stksess_init(struct stktable *t, struct stksess * ts)
+static struct stksess *__stksess_init(struct stktable *t, struct stksess * ts)
 {
        memset((void *)ts - t->data_size, 0, t->data_size);
        ts->ref_cnt = 0;
        ts->key.node.leaf_p = NULL;
        ts->exp.node.leaf_p = NULL;
        ts->upd.node.leaf_p = NULL;
+       ts->expire = tick_add(now_ms, MS_TO_TICKS(t->expire));
+       RWLOCK_INIT(&ts->lock);
        return ts;
 }
 
@@ -101,7 +134,7 @@ static struct stksess *stksess_init(struct stktable *t, struct stksess * ts)
  * Trash oldest <to_batch> sticky sessions from table <t>
  * Returns number of trashed sticky sessions.
  */
-int stktable_trash_oldest(struct stktable *t, int to_batch)
+int __stktable_trash_oldest(struct stktable *t, int to_batch)
 {
        struct stksess *ts;
        struct eb32_node *eb;
@@ -152,13 +185,28 @@ int stktable_trash_oldest(struct stktable *t, int to_batch)
                /* session expired, trash it */
                ebmb_delete(&ts->key);
                eb32_delete(&ts->upd);
-               stksess_free(t, ts);
+               __stksess_free(t, ts);
                batched++;
        }
 
        return batched;
 }
 
+/*
+ * Trash oldest <to_batch> sticky sessions from table <t>
+ * Returns number of trashed sticky sessions.
+ * This function locks the table
+ */
+int stktable_trash_oldest(struct stktable *t, int to_batch)
+{
+       int ret;
+
+       SPIN_LOCK(STK_TABLE_LOCK, &t->lock);
+       ret = __stktable_trash_oldest(t, to_batch);
+       SPIN_UNLOCK(STK_TABLE_LOCK, &t->lock);
+
+       return ret;
+}
 /*
  * Allocate and initialise a new sticky session.
  * The new sticky session is returned or NULL in case of lack of memory.
@@ -166,7 +214,7 @@ int stktable_trash_oldest(struct stktable *t, int to_batch)
  * stksess_free(). Table <t>'s sticky session counter is increased. If <key>
  * is not NULL, it is assigned to the new session.
  */
-struct stksess *stksess_new(struct stktable *t, struct stktable_key *key)
+struct stksess *__stksess_new(struct stktable *t, struct stktable_key *key)
 {
        struct stksess *ts;
 
@@ -174,7 +222,7 @@ struct stksess *stksess_new(struct stktable *t, struct stktable_key *key)
                if ( t->nopurge )
                        return NULL;
 
-               if (!stktable_trash_oldest(t, (t->size >> 8) + 1))
+               if (!__stktable_trash_oldest(t, (t->size >> 8) + 1))
                        return NULL;
        }
 
@@ -182,19 +230,37 @@ struct stksess *stksess_new(struct stktable *t, struct stktable_key *key)
        if (ts) {
                t->current++;
                ts = (void *)ts + t->data_size;
-               stksess_init(t, ts);
+               __stksess_init(t, ts);
                if (key)
                        stksess_setkey(t, ts, key);
        }
 
        return ts;
 }
+/*
+ * Allocate and initialise a new sticky session.
+ * The new sticky session is returned or NULL in case of lack of memory.
+ * Sticky sessions should only be allocated this way, and must be freed using
+ * stksess_free(). Table <t>'s sticky session counter is increased. If <key>
+ * is not NULL, it is assigned to the new session.
+ * This function locks the table
+ */
+struct stksess *stksess_new(struct stktable *t, struct stktable_key *key)
+{
+       struct stksess *ts;
+
+       SPIN_LOCK(STK_TABLE_LOCK, &t->lock);
+       ts = __stksess_new(t, key);
+       SPIN_UNLOCK(STK_TABLE_LOCK, &t->lock);
+
+       return ts;
+}
 
 /*
  * Looks in table <t> for a sticky session matching key <key>.
  * Returns pointer on requested sticky session or NULL if none was found.
  */
-struct stksess *stktable_lookup_key(struct stktable *t, struct stktable_key *key)
+struct stksess *__stktable_lookup_key(struct stktable *t, struct stktable_key *key)
 {
        struct ebmb_node *eb;
 
@@ -211,23 +277,22 @@ struct stksess *stktable_lookup_key(struct stktable *t, struct stktable_key *key
        return ebmb_entry(eb, struct stksess, key);
 }
 
-/* Lookup and touch <key> in <table>, or create the entry if it does not exist.
- * This is mainly used for situations where we want to refresh a key's usage so
- * that it does not expire, and we want to have it created if it was not there.
- * The stksess is returned, or NULL if it could not be created.
+/*
+ * Looks in table <t> for a sticky session matching key <key>.
+ * Returns pointer on requested sticky session or NULL if none was found.
+ * The refcount of the found entry is increased and this function
+ * is protected using the table lock
  */
-struct stksess *stktable_update_key(struct stktable *table, struct stktable_key *key)
+struct stksess *stktable_lookup_key(struct stktable *t, struct stktable_key *key)
 {
        struct stksess *ts;
 
-       ts = stktable_lookup_key(table, key);
-       if (likely(ts))
-               return stktable_touch(table, ts, 1);
+       SPIN_LOCK(STK_TABLE_LOCK, &t->lock);
+       ts = __stktable_lookup_key(t, key);
+       if (ts)
+               ts->ref_cnt++;
+       SPIN_UNLOCK(STK_TABLE_LOCK, &t->lock);
 
-       /* entry does not exist, initialize a new one */
-       ts = stksess_new(table, key);
-       if (likely(ts))
-               stktable_store(table, ts, 1);
        return ts;
 }
 
@@ -235,7 +300,7 @@ struct stksess *stktable_update_key(struct stktable *table, struct stktable_key
  * Looks in table <t> for a sticky session with same key as <ts>.
  * Returns pointer on requested sticky session or NULL if none was found.
  */
-struct stksess *stktable_lookup(struct stktable *t, struct stksess *ts)
+struct stksess *__stktable_lookup(struct stktable *t, struct stksess *ts)
 {
        struct ebmb_node *eb;
 
@@ -250,11 +315,31 @@ struct stksess *stktable_lookup(struct stktable *t, struct stksess *ts)
        return ebmb_entry(eb, struct stksess, key);
 }
 
+/*
+ * Looks in table <t> for a sticky session with same key as <ts>.
+ * Returns pointer on requested sticky session or NULL if none was found.
+ * The refcount of the found entry is increased and this function
+ * is protected using the table lock
+ */
+struct stksess *stktable_lookup(struct stktable *t, struct stksess *ts)
+{
+       struct stksess *lts;
+
+       SPIN_LOCK(STK_TABLE_LOCK, &t->lock);
+       lts = __stktable_lookup(t, ts);
+       if (lts)
+               lts->ref_cnt++;
+       SPIN_UNLOCK(STK_TABLE_LOCK, &t->lock);
+
+       return lts;
+}
+
 /* Update the expiration timer for <ts> but do not touch its expiration node.
  * The table's expiration timer is updated if set.
+ * The node will be also inserted into the update tree if needed, at a position
+ * depending if the update is a local or coming from a remote node
  */
-struct stksess *stktable_touch_with_exp(struct stktable *t, struct stksess *ts,
-                                        int local, int expire)
+void __stktable_touch_with_exp(struct stktable *t, struct stksess *ts, int local, int expire)
 {
        struct eb32_node * eb;
        ts->expire = expire;
@@ -263,89 +348,163 @@ struct stksess *stktable_touch_with_exp(struct stktable *t, struct stksess *ts,
                task_queue(t->exp_task);
        }
 
-       /* If sync is enabled and update is local */
-       if (t->sync_task && local) {
-               /* If this entry is not in the tree
-                  or not scheduled for at least one peer */
-               if (!ts->upd.node.leaf_p
-                   || (int)(t->commitupdate - ts->upd.key) >= 0
-                   || (int)(ts->upd.key - t->localupdate) >= 0) {
-                       ts->upd.key = ++t->update;
-                       t->localupdate = t->update;
-                       eb32_delete(&ts->upd);
-                       eb = eb32_insert(&t->updates, &ts->upd);
-                       if (eb != &ts->upd)  {
-                               eb32_delete(eb);
-                               eb32_insert(&t->updates, &ts->upd);
+       /* If sync is enabled */
+       if (t->sync_task) {
+               if (local) {
+                       /* If this entry is not in the tree
+                          or not scheduled for at least one peer */
+                       if (!ts->upd.node.leaf_p
+                           || (int)(t->commitupdate - ts->upd.key) >= 0
+                           || (int)(ts->upd.key - t->localupdate) >= 0) {
+                               ts->upd.key = ++t->update;
+                               t->localupdate = t->update;
+                               eb32_delete(&ts->upd);
+                               eb = eb32_insert(&t->updates, &ts->upd);
+                               if (eb != &ts->upd)  {
+                                       eb32_delete(eb);
+                                       eb32_insert(&t->updates, &ts->upd);
+                               }
+                       }
+                       task_wakeup(t->sync_task, TASK_WOKEN_MSG);
+               }
+               else {
+                       /* If this entry is not in the tree */
+                       if (!ts->upd.node.leaf_p) {
+                               ts->upd.key= (++t->update)+(2147483648U);
+                               eb = eb32_insert(&t->updates, &ts->upd);
+                               if (eb != &ts->upd) {
+                                       eb32_delete(eb);
+                                       eb32_insert(&t->updates, &ts->upd);
+                               }
                        }
                }
-               task_wakeup(t->sync_task, TASK_WOKEN_MSG);
        }
-       return ts;
 }
 
 /* Update the expiration timer for <ts> but do not touch its expiration node.
- * The table's expiration timer is updated if set. The date of expiration coming from
+ * The table's expiration timer is updated using the date of expiration coming from
  * <t> stick-table configuration.
+ * The node will be also inserted into the update tree if needed, at a position
+ * considering the update is coming from a remote node
  */
-struct stksess *stktable_touch(struct stktable *t, struct stksess *ts, int local)
+void stktable_touch_remote(struct stktable *t, struct stksess *ts, int decrefcnt)
+{
+       SPIN_LOCK(STK_TABLE_LOCK, &t->lock);
+       __stktable_touch_with_exp(t, ts, 0, ts->expire);
+       if (decrefcnt)
+               ts->ref_cnt--;
+       SPIN_UNLOCK(STK_TABLE_LOCK, &t->lock);
+}
+
+/* Update the expiration timer for <ts> but do not touch its expiration node.
+ * The table's expiration timer is updated using the date of expiration coming from
+ * <t> stick-table configuration.
+ * The node will be also inserted into the update tree if needed, at a position
+ * considering the update was made locally
+ */
+void stktable_touch_local(struct stktable *t, struct stksess *ts, int decrefcnt)
 {
        int expire = tick_add(now_ms, MS_TO_TICKS(t->expire));
 
-       return stktable_touch_with_exp(t, ts, local, expire);
+       SPIN_LOCK(STK_TABLE_LOCK, &t->lock);
+       __stktable_touch_with_exp(t, ts, 1, expire);
+       if (decrefcnt)
+               ts->ref_cnt--;
+       SPIN_UNLOCK(STK_TABLE_LOCK, &t->lock);
+}
+/* Just decrease the ref_cnt of the current session */
+void stktable_release(struct stktable *t, struct stksess *ts)
+{
+       SPIN_LOCK(STK_TABLE_LOCK, &t->lock);
+       ts->ref_cnt--;
+       SPIN_UNLOCK(STK_TABLE_LOCK, &t->lock);
 }
 
 /* Insert new sticky session <ts> in the table. It is assumed that it does not
  * yet exist (the caller must check this). The table's timeout is updated if it
  * is set. <ts> is returned.
  */
-struct stksess *stktable_store(struct stktable *t, struct stksess *ts, int local)
+void __stktable_store(struct stktable *t, struct stksess *ts)
 {
-       ebmb_insert(&t->keys, &ts->key, t->key_size);
-       stktable_touch(t, ts, local);
-       ts->exp.key = ts->expire;
-       eb32_insert(&t->exps, &ts->exp);
-       return ts;
-}
 
-/* Same function as stktable_store(), but with <expire> as supplementary argument
- * to set the date of expiration of <ts> new sticky session thanks to
- * stktable_touch_with_exp().
- */
-struct stksess *stktable_store_with_exp(struct stktable *t, struct stksess *ts,
-                                        int local, int expire)
-{
        ebmb_insert(&t->keys, &ts->key, t->key_size);
-       stktable_touch_with_exp(t, ts, local, expire);
        ts->exp.key = ts->expire;
        eb32_insert(&t->exps, &ts->exp);
-       return ts;
+       if (t->expire) {
+               t->exp_task->expire = t->exp_next = tick_first(ts->expire, t->exp_next);
+               task_queue(t->exp_task);
+       }
 }
 
 /* Returns a valid or initialized stksess for the specified stktable_key in the
  * specified table, or NULL if the key was NULL, or if no entry was found nor
  * could be created. The entry's expiration is updated.
  */
-struct stksess *stktable_get_entry(struct stktable *table, struct stktable_key *key)
+struct stksess *__stktable_get_entry(struct stktable *table, struct stktable_key *key)
 {
        struct stksess *ts;
 
        if (!key)
                return NULL;
 
-       ts = stktable_lookup_key(table, key);
+       ts = __stktable_lookup_key(table, key);
        if (ts == NULL) {
                /* entry does not exist, initialize a new one */
-               ts = stksess_new(table, key);
+               ts = __stksess_new(table, key);
                if (!ts)
                        return NULL;
-               stktable_store(table, ts, 1);
+               __stktable_store(table, ts);
        }
-       else
-               stktable_touch(table, ts, 1);
        return ts;
 }
+/* Returns a valid or initialized stksess for the specified stktable_key in the
+ * specified table, or NULL if the key was NULL, or if no entry was found nor
+ * could be created. The entry's expiration is updated.
+ * This function locks the table, and the refcount of the entry is increased.
+ */
+struct stksess *stktable_get_entry(struct stktable *table, struct stktable_key *key)
+{
+       struct stksess *ts;
+
+       SPIN_LOCK(STK_TABLE_LOCK, &table->lock);
+       ts = __stktable_get_entry(table, key);
+       if (ts)
+               ts->ref_cnt++;
+       SPIN_UNLOCK(STK_TABLE_LOCK, &table->lock);
+
+       return ts;
+}
+
+/* Lookup for an entry with the same key and store the submitted
+ * stksess if not found.
+ */
+struct stksess *__stktable_set_entry(struct stktable *table, struct stksess *nts)
+{
+       struct stksess *ts;
+
+       ts = __stktable_lookup(table, nts);
+       if (ts == NULL) {
+               ts = nts;
+               __stktable_store(table, ts);
+       }
+       return ts;
+}
+
+/* Lookup for an entry with the same key and store the submitted
+ * stksess if not found.
+ * This function locks the table, and the refcount of the entry is increased.
+ */
+struct stksess *stktable_set_entry(struct stktable *table, struct stksess *nts)
+{
+       struct stksess *ts;
+
+       SPIN_LOCK(STK_TABLE_LOCK, &table->lock);
+       ts = __stktable_set_entry(table, nts);
+       ts->ref_cnt++;
+       SPIN_UNLOCK(STK_TABLE_LOCK, &table->lock);
 
+       return ts;
+}
 /*
  * Trash expired sticky sessions from table <t>. The next expiration date is
  * returned.
@@ -356,6 +515,7 @@ static int stktable_trash_expired(struct stktable *t)
        struct eb32_node *eb;
        int looped = 0;
 
+       SPIN_LOCK(STK_TABLE_LOCK, &t->lock);
        eb = eb32_lookup_ge(&t->exps, now_ms - TIMER_LOOK_BACK);
 
        while (1) {
@@ -376,6 +536,7 @@ static int stktable_trash_expired(struct stktable *t)
                if (likely(tick_is_lt(now_ms, eb->key))) {
                        /* timer not expired yet, revisit it later */
                        t->exp_next = eb->key;
+                       SPIN_UNLOCK(STK_TABLE_LOCK, &t->lock);
                        return t->exp_next;
                }
 
@@ -404,8 +565,9 @@ static int stktable_trash_expired(struct stktable *t)
                /* session expired, trash it */
                ebmb_delete(&ts->key);
                eb32_delete(&ts->upd);
-               stksess_free(t, ts);
+               __stksess_free(t, ts);
        }
+       SPIN_UNLOCK(STK_TABLE_LOCK, &t->lock);
 
        /* We have found no task to expire in any tree */
        t->exp_next = TICK_ETERNITY;
@@ -428,9 +590,10 @@ static struct task *process_table_expire(struct task *task)
 int stktable_init(struct stktable *t)
 {
        if (t->size) {
-               memset(&t->keys, 0, sizeof(t->keys));
+               t->keys = EB_ROOT_UNIQUE;
                memset(&t->exps, 0, sizeof(t->exps));
                t->updates = EB_ROOT_UNIQUE;
+               SPIN_INIT(&t->lock);
 
                t->pool = create_pool("sticktables", sizeof(struct stksess) + t->data_size + t->key_size, MEM_F_SHARED);
 
@@ -1381,17 +1544,22 @@ static enum act_return action_inc_gpc0(struct act_rule *rule, struct proxy *px,
 
                /* First, update gpc0_rate if it's tracked. Second, update its gpc0 if tracked. */
                ptr1 = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_GPC0_RATE);
-               if (ptr1)
-                       update_freq_ctr_period(&stktable_data_cast(ptr1, gpc0_rate),
+               ptr2 = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_GPC0);
+               if (ptr1 || ptr2) {
+                       RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
+
+                       if (ptr1)
+                               update_freq_ctr_period(&stktable_data_cast(ptr1, gpc0_rate),
                                               stkctr->table->data_arg[STKTABLE_DT_GPC0_RATE].u, 1);
 
-               ptr2 = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_GPC0);
-               if (ptr2)
-                       stktable_data_cast(ptr2, gpc0)++;
+                       if (ptr2)
+                               stktable_data_cast(ptr2, gpc0)++;
 
-               /* If data was modified, we need to touch to re-schedule sync */
-               if (ptr1 || ptr2)
-                       stktable_touch(stkctr->table, ts, 1);
+                       RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+
+                       /* If data was modified, we need to touch to re-schedule sync */
+                       stktable_touch_local(stkctr->table, ts, 0);
+               }
        }
        return ACT_RET_CONT;
 }
@@ -1460,8 +1628,13 @@ static enum act_return action_set_gpt0(struct act_rule *rule, struct proxy *px,
        /* Store the sample in the required sc, and ignore errors. */
        ptr = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_GPT0);
        if (ptr) {
+               RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
+
                stktable_data_cast(ptr, gpt0) = rule->arg.gpt.value;
-               stktable_touch(stkctr->table, ts, 1);
+
+               RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+
+               stktable_touch_local(stkctr->table, ts, 0);
        }
 
        return ACT_RET_CONT;
@@ -1563,9 +1736,8 @@ smp_fetch_table_avl(const struct arg *args, struct sample *smp, const char *kw,
  * the session will be consulted.
  */
 struct stkctr *
-smp_fetch_sc_stkctr(struct session *sess, struct stream *strm, const struct arg *args, const char *kw)
+smp_fetch_sc_stkctr(struct session *sess, struct stream *strm, const struct arg *args, const char *kw, struct stkctr *stkctr)
 {
-       static struct stkctr stkctr;
        struct stkctr *stkptr;
        struct stksess *stksess;
        unsigned int num = kw[2] - '0';
@@ -1597,9 +1769,9 @@ smp_fetch_sc_stkctr(struct session *sess, struct stream *strm, const struct arg
                if (!key)
                        return NULL;
 
-               stkctr.table = &args->data.prx->table;
-               stkctr_set_entry(&stkctr, stktable_lookup_key(stkctr.table, key));
-               return &stkctr;
+               stkctr->table = &args->data.prx->table;
+               stkctr_set_entry(stkctr, stktable_lookup_key(stkctr->table, key));
+               return stkctr;
        }
 
        /* Here, <num> contains the counter number from 0 to 9 for
@@ -1622,9 +1794,9 @@ smp_fetch_sc_stkctr(struct session *sess, struct stream *strm, const struct arg
 
        if (unlikely(args[arg].type == ARGT_TAB)) {
                /* an alternate table was specified, let's look up the same key there */
-               stkctr.table = &args[arg].data.prx->table;
-               stkctr_set_entry(&stkctr, stktable_lookup(stkctr.table, stksess));
-               return &stkctr;
+               stkctr->table = &args[arg].data.prx->table;
+               stkctr_set_entry(stkctr, stktable_lookup(stkctr->table, stksess));
+               return stkctr;
        }
        return stkptr;
 }
@@ -1635,9 +1807,8 @@ smp_fetch_sc_stkctr(struct session *sess, struct stream *strm, const struct arg
  * src_clr_gpc*.
  */
 struct stkctr *
-smp_create_src_stkctr(struct session *sess, struct stream *strm, const struct arg *args, const char *kw)
+smp_create_src_stkctr(struct session *sess, struct stream *strm, const struct arg *args, const char *kw, struct stkctr *stkctr)
 {
-       static struct stkctr stkctr;
        struct stktable_key *key;
        struct connection *conn = objt_conn(sess->origin);
        struct sample smp;
@@ -1660,9 +1831,9 @@ smp_create_src_stkctr(struct session *sess, struct stream *strm, const struct ar
        if (!key)
                return NULL;
 
-       stkctr.table = &args->data.prx->table;
-       stkctr_set_entry(&stkctr, stktable_update_key(stkctr.table, key));
-       return &stkctr;
+       stkctr->table = &args->data.prx->table;
+       stkctr_set_entry(stkctr, stktable_get_entry(stkctr->table, key));
+       return stkctr;
 }
 
 /* set return a boolean indicating if the requested stream counter is
@@ -1672,9 +1843,18 @@ smp_create_src_stkctr(struct session *sess, struct stream *strm, const struct ar
 static int
 smp_fetch_sc_tracked(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
+       struct stkctr *stkctr;
+
        smp->flags = SMP_F_VOL_TEST;
        smp->data.type = SMP_T_BOOL;
-       smp->data.u.sint = !!smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
+       smp->data.u.sint = !!stkctr;
+
+       /* release the ref count */
+       if ((stkctr == &tmpstkctr) &&  stkctr_entry(stkctr))
+               stktable_release(stkctr->table, stkctr_entry(stkctr));
+
        return 1;
 }
 
@@ -1686,9 +1866,10 @@ smp_fetch_sc_tracked(const struct arg *args, struct sample *smp, const char *kw,
 static int
 smp_fetch_sc_get_gpt0(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -1696,11 +1877,24 @@ smp_fetch_sc_get_gpt0(const struct arg *args, struct sample *smp, const char *kw
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
 
-       if (stkctr_entry(stkctr) != NULL) {
-               void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_GPT0);
-               if (!ptr)
+       if (stkctr_entry(stkctr)) {
+               void *ptr;
+
+               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_GPT0);
+               if (!ptr) {
+                       if (stkctr == &tmpstkctr)
+                               stktable_release(stkctr->table, stkctr_entry(stkctr));
                        return 0; /* parameter not stored */
+               }
+
+               RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                smp->data.u.sint = stktable_data_cast(ptr, gpt0);
+
+               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
+               if (stkctr == &tmpstkctr)
+                       stktable_release(stkctr->table, stkctr_entry(stkctr));
        }
        return 1;
 }
@@ -1713,9 +1907,10 @@ smp_fetch_sc_get_gpt0(const struct arg *args, struct sample *smp, const char *kw
 static int
 smp_fetch_sc_get_gpc0(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -1724,10 +1919,23 @@ smp_fetch_sc_get_gpc0(const struct arg *args, struct sample *smp, const char *kw
        smp->data.u.sint = 0;
 
        if (stkctr_entry(stkctr) != NULL) {
-               void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_GPC0);
-               if (!ptr)
+               void *ptr;
+
+               ptr  = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_GPC0);
+               if (!ptr) {
+                       if (stkctr == &tmpstkctr)
+                               stktable_release(stkctr->table, stkctr_entry(stkctr));
                        return 0; /* parameter not stored */
+               }
+
+               RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                smp->data.u.sint = stktable_data_cast(ptr, gpc0);
+
+               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
+               if (stkctr == &tmpstkctr)
+                       stktable_release(stkctr->table, stkctr_entry(stkctr));
        }
        return 1;
 }
@@ -1740,9 +1948,10 @@ smp_fetch_sc_get_gpc0(const struct arg *args, struct sample *smp, const char *kw
 static int
 smp_fetch_sc_gpc0_rate(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -1750,11 +1959,24 @@ smp_fetch_sc_gpc0_rate(const struct arg *args, struct sample *smp, const char *k
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
        if (stkctr_entry(stkctr) != NULL) {
-               void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_GPC0_RATE);
-               if (!ptr)
+               void *ptr;
+
+               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_GPC0_RATE);
+               if (!ptr) {
+                       if (stkctr == &tmpstkctr)
+                               stktable_release(stkctr->table, stkctr_entry(stkctr));
                        return 0; /* parameter not stored */
+               }
+
+               RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                smp->data.u.sint = read_freq_ctr_period(&stktable_data_cast(ptr, gpc0_rate),
                                  stkctr->table->data_arg[STKTABLE_DT_GPC0_RATE].u);
+
+               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
+               if (stkctr == &tmpstkctr)
+                       stktable_release(stkctr->table, stkctr_entry(stkctr));
        }
        return 1;
 }
@@ -1766,9 +1988,10 @@ smp_fetch_sc_gpc0_rate(const struct arg *args, struct sample *smp, const char *k
 static int
 smp_fetch_sc_inc_gpc0(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -1776,29 +1999,37 @@ smp_fetch_sc_inc_gpc0(const struct arg *args, struct sample *smp, const char *kw
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
 
-       if (stkctr_entry(stkctr) == NULL)
-               stkctr = smp_create_src_stkctr(smp->sess, smp->strm, args, kw);
+       if (!stkctr_entry(stkctr))
+               stkctr = smp_create_src_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
 
        if (stkctr && stkctr_entry(stkctr)) {
                void *ptr1,*ptr2;
 
+
                /* First, update gpc0_rate if it's tracked. Second, update its
                 * gpc0 if tracked. Returns gpc0's value otherwise the curr_ctr.
                 */
                ptr1 = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_GPC0_RATE);
-               if (ptr1) {
-                       update_freq_ctr_period(&stktable_data_cast(ptr1, gpc0_rate),
-                                              stkctr->table->data_arg[STKTABLE_DT_GPC0_RATE].u, 1);
-                       smp->data.u.sint = (&stktable_data_cast(ptr1, gpc0_rate))->curr_ctr;
-               }
-
                ptr2 = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_GPC0);
-               if (ptr2)
-                       smp->data.u.sint = ++stktable_data_cast(ptr2, gpc0);
+               if (ptr1 || ptr2) {
+                       RWLOCK_WRLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
 
-               /* If data was modified, we need to touch to re-schedule sync */
-               if (ptr1 || ptr2)
-                       stktable_touch(stkctr->table, stkctr_entry(stkctr), 1);
+                       if (ptr1) {
+                               update_freq_ctr_period(&stktable_data_cast(ptr1, gpc0_rate),
+                                                      stkctr->table->data_arg[STKTABLE_DT_GPC0_RATE].u, 1);
+                               smp->data.u.sint = (&stktable_data_cast(ptr1, gpc0_rate))->curr_ctr;
+                       }
+
+                       if (ptr2)
+                               smp->data.u.sint = ++stktable_data_cast(ptr2, gpc0);
+
+                       RWLOCK_WRUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
+                       /* If data was modified, we need to touch to re-schedule sync */
+                       stktable_touch_local(stkctr->table, stkctr_entry(stkctr), (stkctr == &tmpstkctr) ? 1 : 0);
+               }
+               else if (stkctr == &tmpstkctr)
+                       stktable_release(stkctr->table, stkctr_entry(stkctr));
        }
        return 1;
 }
@@ -1810,9 +2041,10 @@ smp_fetch_sc_inc_gpc0(const struct arg *args, struct sample *smp, const char *kw
 static int
 smp_fetch_sc_clr_gpc0(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -1820,17 +2052,28 @@ smp_fetch_sc_clr_gpc0(const struct arg *args, struct sample *smp, const char *kw
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
 
-       if (stkctr_entry(stkctr) == NULL)
-               stkctr = smp_create_src_stkctr(smp->sess, smp->strm, args, kw);
+       if (!stkctr_entry(stkctr))
+               stkctr = smp_create_src_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
 
-       if (stkctr_entry(stkctr) != NULL) {
-               void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_GPC0);
-               if (!ptr)
+       if (stkctr && stkctr_entry(stkctr)) {
+               void *ptr;
+
+               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_GPC0);
+               if (!ptr) {
+                       if (stkctr == &tmpstkctr)
+                               stktable_release(stkctr->table, stkctr_entry(stkctr));
                        return 0; /* parameter not stored */
+               }
+
+               RWLOCK_WRLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                smp->data.u.sint = stktable_data_cast(ptr, gpc0);
                stktable_data_cast(ptr, gpc0) = 0;
+
+               RWLOCK_WRUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                /* If data was modified, we need to touch to re-schedule sync */
-               stktable_touch(stkctr->table, stkctr_entry(stkctr), 1);
+               stktable_touch_local(stkctr->table, stkctr_entry(stkctr), (stkctr == &tmpstkctr) ? 1 : 0);
        }
        return 1;
 }
@@ -1842,9 +2085,10 @@ smp_fetch_sc_clr_gpc0(const struct arg *args, struct sample *smp, const char *kw
 static int
 smp_fetch_sc_conn_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -1852,10 +2096,25 @@ smp_fetch_sc_conn_cnt(const struct arg *args, struct sample *smp, const char *kw
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
        if (stkctr_entry(stkctr) != NULL) {
-               void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_CONN_CNT);
-               if (!ptr)
+               void *ptr;
+
+               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_CONN_CNT);
+               if (!ptr) {
+                       if (stkctr == &tmpstkctr)
+                               stktable_release(stkctr->table, stkctr_entry(stkctr));
                        return 0; /* parameter not stored */
+               }
+
+               RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                smp->data.u.sint = stktable_data_cast(ptr, conn_cnt);
+
+               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
+               if (stkctr == &tmpstkctr)
+                       stktable_release(stkctr->table, stkctr_entry(stkctr));
+
+
        }
        return 1;
 }
@@ -1867,9 +2126,10 @@ smp_fetch_sc_conn_cnt(const struct arg *args, struct sample *smp, const char *kw
 static int
 smp_fetch_sc_conn_rate(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -1877,11 +2137,24 @@ smp_fetch_sc_conn_rate(const struct arg *args, struct sample *smp, const char *k
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
        if (stkctr_entry(stkctr) != NULL) {
-               void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_CONN_RATE);
-               if (!ptr)
+               void *ptr;
+
+               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_CONN_RATE);
+               if (!ptr) {
+                       if (stkctr == &tmpstkctr)
+                               stktable_release(stkctr->table, stkctr_entry(stkctr));
                        return 0; /* parameter not stored */
+               }
+
+               RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                smp->data.u.sint = read_freq_ctr_period(&stktable_data_cast(ptr, conn_rate),
                                               stkctr->table->data_arg[STKTABLE_DT_CONN_RATE].u);
+
+               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
+               if (stkctr == &tmpstkctr)
+                       stktable_release(stkctr->table, stkctr_entry(stkctr));
        }
        return 1;
 }
@@ -1913,18 +2186,28 @@ smp_fetch_src_updt_conn_cnt(const struct arg *args, struct sample *smp, const ch
 
        px = args->data.prx;
 
-       if ((ts = stktable_update_key(&px->table, key)) == NULL)
+       if ((ts = stktable_get_entry(&px->table, key)) == NULL)
                /* entry does not exist and could not be created */
                return 0;
 
        ptr = stktable_data_ptr(&px->table, ts, STKTABLE_DT_CONN_CNT);
-       if (!ptr)
+       if (!ptr) {
                return 0; /* parameter not stored in this table */
+       }
 
        smp->data.type = SMP_T_SINT;
+
+       RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
+
        smp->data.u.sint = ++stktable_data_cast(ptr, conn_cnt);
-       /* Touch was previously performed by stktable_update_key */
+
+       RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+
        smp->flags = SMP_F_VOL_TEST;
+
+       stktable_touch_local(&px->table, ts, 1);
+
+       /* Touch was previously performed by stktable_update_key */
        return 1;
 }
 
@@ -1935,9 +2218,10 @@ smp_fetch_src_updt_conn_cnt(const struct arg *args, struct sample *smp, const ch
 static int
 smp_fetch_sc_conn_cur(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -1945,10 +2229,23 @@ smp_fetch_sc_conn_cur(const struct arg *args, struct sample *smp, const char *kw
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
        if (stkctr_entry(stkctr) != NULL) {
-               void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_CONN_CUR);
-               if (!ptr)
+               void *ptr;
+
+               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_CONN_CUR);
+               if (!ptr) {
+                       if (stkctr == &tmpstkctr)
+                               stktable_release(stkctr->table, stkctr_entry(stkctr));
                        return 0; /* parameter not stored */
+               }
+
+               RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                smp->data.u.sint = stktable_data_cast(ptr, conn_cur);
+
+               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
+               if (stkctr == &tmpstkctr)
+                       stktable_release(stkctr->table, stkctr_entry(stkctr));
        }
        return 1;
 }
@@ -1960,9 +2257,10 @@ smp_fetch_sc_conn_cur(const struct arg *args, struct sample *smp, const char *kw
 static int
 smp_fetch_sc_sess_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -1970,10 +2268,23 @@ smp_fetch_sc_sess_cnt(const struct arg *args, struct sample *smp, const char *kw
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
        if (stkctr_entry(stkctr) != NULL) {
-               void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_SESS_CNT);
-               if (!ptr)
+               void *ptr;
+
+               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_SESS_CNT);
+               if (!ptr) {
+                       if (stkctr == &tmpstkctr)
+                               stktable_release(stkctr->table, stkctr_entry(stkctr));
                        return 0; /* parameter not stored */
+               }
+
+               RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                smp->data.u.sint = stktable_data_cast(ptr, sess_cnt);
+
+               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
+               if (stkctr == &tmpstkctr)
+                       stktable_release(stkctr->table, stkctr_entry(stkctr));
        }
        return 1;
 }
@@ -1984,9 +2295,10 @@ smp_fetch_sc_sess_cnt(const struct arg *args, struct sample *smp, const char *kw
 static int
 smp_fetch_sc_sess_rate(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -1994,11 +2306,24 @@ smp_fetch_sc_sess_rate(const struct arg *args, struct sample *smp, const char *k
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
        if (stkctr_entry(stkctr) != NULL) {
-               void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_SESS_RATE);
-               if (!ptr)
+               void *ptr;
+
+               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_SESS_RATE);
+               if (!ptr) {
+                       if (stkctr == &tmpstkctr)
+                               stktable_release(stkctr->table, stkctr_entry(stkctr));
                        return 0; /* parameter not stored */
+               }
+
+               RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                smp->data.u.sint = read_freq_ctr_period(&stktable_data_cast(ptr, sess_rate),
                                               stkctr->table->data_arg[STKTABLE_DT_SESS_RATE].u);
+
+               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
+               if (stkctr == &tmpstkctr)
+                       stktable_release(stkctr->table, stkctr_entry(stkctr));
        }
        return 1;
 }
@@ -2010,9 +2335,10 @@ smp_fetch_sc_sess_rate(const struct arg *args, struct sample *smp, const char *k
 static int
 smp_fetch_sc_http_req_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -2020,10 +2346,23 @@ smp_fetch_sc_http_req_cnt(const struct arg *args, struct sample *smp, const char
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
        if (stkctr_entry(stkctr) != NULL) {
-               void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_HTTP_REQ_CNT);
-               if (!ptr)
+               void *ptr;
+
+               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_HTTP_REQ_CNT);
+               if (!ptr) {
+                       if (stkctr == &tmpstkctr)
+                               stktable_release(stkctr->table, stkctr_entry(stkctr));
                        return 0; /* parameter not stored */
+               }
+
+               RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                smp->data.u.sint = stktable_data_cast(ptr, http_req_cnt);
+
+               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
+               if (stkctr == &tmpstkctr)
+                       stktable_release(stkctr->table, stkctr_entry(stkctr));
        }
        return 1;
 }
@@ -2035,9 +2374,10 @@ smp_fetch_sc_http_req_cnt(const struct arg *args, struct sample *smp, const char
 static int
 smp_fetch_sc_http_req_rate(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -2045,11 +2385,24 @@ smp_fetch_sc_http_req_rate(const struct arg *args, struct sample *smp, const cha
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
        if (stkctr_entry(stkctr) != NULL) {
-               void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_HTTP_REQ_RATE);
-               if (!ptr)
+               void *ptr;
+
+               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_HTTP_REQ_RATE);
+               if (!ptr) {
+                       if (stkctr == &tmpstkctr)
+                               stktable_release(stkctr->table, stkctr_entry(stkctr));
                        return 0; /* parameter not stored */
+               }
+
+               RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                smp->data.u.sint = read_freq_ctr_period(&stktable_data_cast(ptr, http_req_rate),
                                               stkctr->table->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u);
+
+               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
+               if (stkctr == &tmpstkctr)
+                       stktable_release(stkctr->table, stkctr_entry(stkctr));
        }
        return 1;
 }
@@ -2061,9 +2414,10 @@ smp_fetch_sc_http_req_rate(const struct arg *args, struct sample *smp, const cha
 static int
 smp_fetch_sc_http_err_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -2071,10 +2425,23 @@ smp_fetch_sc_http_err_cnt(const struct arg *args, struct sample *smp, const char
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
        if (stkctr_entry(stkctr) != NULL) {
-               void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_HTTP_ERR_CNT);
-               if (!ptr)
+               void *ptr;
+
+               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_HTTP_ERR_CNT);
+               if (!ptr) {
+                       if (stkctr == &tmpstkctr)
+                               stktable_release(stkctr->table, stkctr_entry(stkctr));
                        return 0; /* parameter not stored */
+               }
+
+               RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                smp->data.u.sint = stktable_data_cast(ptr, http_err_cnt);
+
+               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
+               if (stkctr == &tmpstkctr)
+                       stktable_release(stkctr->table, stkctr_entry(stkctr));
        }
        return 1;
 }
@@ -2086,9 +2453,10 @@ smp_fetch_sc_http_err_cnt(const struct arg *args, struct sample *smp, const char
 static int
 smp_fetch_sc_http_err_rate(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -2096,11 +2464,24 @@ smp_fetch_sc_http_err_rate(const struct arg *args, struct sample *smp, const cha
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
        if (stkctr_entry(stkctr) != NULL) {
-               void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_HTTP_ERR_RATE);
-               if (!ptr)
+               void *ptr;
+
+               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_HTTP_ERR_RATE);
+               if (!ptr) {
+                       if (stkctr == &tmpstkctr)
+                               stktable_release(stkctr->table, stkctr_entry(stkctr));
                        return 0; /* parameter not stored */
+               }
+
+               RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                smp->data.u.sint = read_freq_ctr_period(&stktable_data_cast(ptr, http_err_rate),
                                               stkctr->table->data_arg[STKTABLE_DT_HTTP_ERR_RATE].u);
+
+               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
+               if (stkctr == &tmpstkctr)
+                       stktable_release(stkctr->table, stkctr_entry(stkctr));
        }
        return 1;
 }
@@ -2112,9 +2493,10 @@ smp_fetch_sc_http_err_rate(const struct arg *args, struct sample *smp, const cha
 static int
 smp_fetch_sc_kbytes_in(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -2122,10 +2504,23 @@ smp_fetch_sc_kbytes_in(const struct arg *args, struct sample *smp, const char *k
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
        if (stkctr_entry(stkctr) != NULL) {
-               void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_BYTES_IN_CNT);
-               if (!ptr)
+               void *ptr;
+
+               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_BYTES_IN_CNT);
+               if (!ptr) {
+                       if (stkctr == &tmpstkctr)
+                               stktable_release(stkctr->table, stkctr_entry(stkctr));
                        return 0; /* parameter not stored */
+               }
+
+               RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                smp->data.u.sint = stktable_data_cast(ptr, bytes_in_cnt) >> 10;
+
+               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
+               if (stkctr == &tmpstkctr)
+                       stktable_release(stkctr->table, stkctr_entry(stkctr));
        }
        return 1;
 }
@@ -2137,9 +2532,10 @@ smp_fetch_sc_kbytes_in(const struct arg *args, struct sample *smp, const char *k
 static int
 smp_fetch_sc_bytes_in_rate(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -2147,11 +2543,24 @@ smp_fetch_sc_bytes_in_rate(const struct arg *args, struct sample *smp, const cha
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
        if (stkctr_entry(stkctr) != NULL) {
-               void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_BYTES_IN_RATE);
-               if (!ptr)
+               void *ptr;
+
+               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_BYTES_IN_RATE);
+               if (!ptr) {
+                       if (stkctr == &tmpstkctr)
+                               stktable_release(stkctr->table, stkctr_entry(stkctr));
                        return 0; /* parameter not stored */
+               }
+
+               RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                smp->data.u.sint = read_freq_ctr_period(&stktable_data_cast(ptr, bytes_in_rate),
                                               stkctr->table->data_arg[STKTABLE_DT_BYTES_IN_RATE].u);
+
+               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
+               if (stkctr == &tmpstkctr)
+                       stktable_release(stkctr->table, stkctr_entry(stkctr));
        }
        return 1;
 }
@@ -2163,9 +2572,10 @@ smp_fetch_sc_bytes_in_rate(const struct arg *args, struct sample *smp, const cha
 static int
 smp_fetch_sc_kbytes_out(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -2173,10 +2583,23 @@ smp_fetch_sc_kbytes_out(const struct arg *args, struct sample *smp, const char *
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
        if (stkctr_entry(stkctr) != NULL) {
-               void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_BYTES_OUT_CNT);
-               if (!ptr)
+               void *ptr;
+
+               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_BYTES_OUT_CNT);
+               if (!ptr) {
+                       if (stkctr == &tmpstkctr)
+                               stktable_release(stkctr->table, stkctr_entry(stkctr));
                        return 0; /* parameter not stored */
+               }
+
+               RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                smp->data.u.sint = stktable_data_cast(ptr, bytes_out_cnt) >> 10;
+
+               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
+               if (stkctr == &tmpstkctr)
+                       stktable_release(stkctr->table, stkctr_entry(stkctr));
        }
        return 1;
 }
@@ -2188,9 +2611,10 @@ smp_fetch_sc_kbytes_out(const struct arg *args, struct sample *smp, const char *
 static int
 smp_fetch_sc_bytes_out_rate(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
@@ -2198,11 +2622,24 @@ smp_fetch_sc_bytes_out_rate(const struct arg *args, struct sample *smp, const ch
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
        if (stkctr_entry(stkctr) != NULL) {
-               void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_BYTES_OUT_RATE);
-               if (!ptr)
+               void *ptr;
+
+               ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_BYTES_OUT_RATE);
+               if (!ptr) {
+                       if (stkctr == &tmpstkctr)
+                               stktable_release(stkctr->table, stkctr_entry(stkctr));
                        return 0; /* parameter not stored */
+               }
+
+               RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
                smp->data.u.sint = read_freq_ctr_period(&stktable_data_cast(ptr, bytes_out_rate),
                                               stkctr->table->data_arg[STKTABLE_DT_BYTES_OUT_RATE].u);
+
+               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock);
+
+               if (stkctr == &tmpstkctr)
+                       stktable_release(stkctr->table, stkctr_entry(stkctr));
        }
        return 1;
 }
@@ -2213,15 +2650,23 @@ smp_fetch_sc_bytes_out_rate(const struct arg *args, struct sample *smp, const ch
 static int
 smp_fetch_sc_trackers(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       struct stkctr tmpstkctr;
        struct stkctr *stkctr;
 
-       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw);
+       stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw, &tmpstkctr);
        if (!stkctr)
                return 0;
 
        smp->flags = SMP_F_VOL_TEST;
        smp->data.type = SMP_T_SINT;
-       smp->data.u.sint = stkctr_entry(stkctr) ? stkctr_entry(stkctr)->ref_cnt : 0;
+       if (stkctr == &tmpstkctr) {
+               smp->data.u.sint = stkctr_entry(stkctr) ? (stkctr_entry(stkctr)->ref_cnt-1) : 0;
+               stktable_release(stkctr->table, stkctr_entry(stkctr));
+       }
+       else {
+               smp->data.u.sint = stkctr_entry(stkctr) ? stkctr_entry(stkctr)->ref_cnt : 0;
+       }
+
        return 1;
 }
 
@@ -2420,52 +2865,59 @@ static int table_process_entry_per_key(struct appctx *appctx, char **args)
        if (!cli_has_level(appctx, ACCESS_LVL_OPER))
                return 1;
 
-       ts = stktable_lookup_key(&px->table, &static_table_key);
-
        switch (appctx->ctx.table.action) {
        case STK_CLI_ACT_SHOW:
+               ts = stktable_lookup_key(&px->table, &static_table_key);
                if (!ts)
                        return 1;
                chunk_reset(&trash);
-               if (!table_dump_head_to_buffer(&trash, si, px, px))
+               if (!table_dump_head_to_buffer(&trash, si, px, px)) {
+                       stktable_release(&px->table, ts);
                        return 0;
-               if (!table_dump_entry_to_buffer(&trash, si, px, ts))
+               }
+               RWLOCK_RDLOCK(STK_SESS_LOCK, &ts->lock);
+               if (!table_dump_entry_to_buffer(&trash, si, px, ts)) {
+                       RWLOCK_RDUNLOCK(STK_SESS_LOCK, &ts->lock);
+                       stktable_release(&px->table, ts);
                        return 0;
+               }
+               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &ts->lock);
+               stktable_release(&px->table, ts);
                break;
 
        case STK_CLI_ACT_CLR:
+               ts = stktable_lookup_key(&px->table, &static_table_key);
                if (!ts)
                        return 1;
-               if (ts->ref_cnt) {
+
+               if (!stksess_kill(&px->table, ts, 1)) {
                        /* don't delete an entry which is currently referenced */
                        appctx->ctx.cli.severity = LOG_ERR;
                        appctx->ctx.cli.msg = "Entry currently in use, cannot remove\n";
                        appctx->st0 = CLI_ST_PRINT;
                        return 1;
                }
-               stksess_kill(&px->table, ts);
+
                break;
 
        case STK_CLI_ACT_SET:
-               if (ts)
-                       stktable_touch(&px->table, ts, 1);
-               else {
-                       ts = stksess_new(&px->table, &static_table_key);
-                       if (!ts) {
-                               /* don't delete an entry which is currently referenced */
-                               appctx->ctx.cli.severity = LOG_ERR;
-                               appctx->ctx.cli.msg = "Unable to allocate a new entry\n";
-                               appctx->st0 = CLI_ST_PRINT;
-                               return 1;
-                       }
-                       stktable_store(&px->table, ts, 1);
+               ts = stktable_get_entry(&px->table, &static_table_key);
+               if (!ts) {
+                       /* don't delete an entry which is currently referenced */
+                       appctx->ctx.cli.severity = LOG_ERR;
+                       appctx->ctx.cli.msg = "Unable to allocate a new entry\n";
+                       appctx->st0 = CLI_ST_PRINT;
+                       return 1;
                }
 
+               RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
                for (cur_arg = 5; *args[cur_arg]; cur_arg += 2) {
                        if (strncmp(args[cur_arg], "data.", 5) != 0) {
                                appctx->ctx.cli.severity = LOG_ERR;
                                appctx->ctx.cli.msg = "\"data.<type>\" followed by a value expected\n";
                                appctx->st0 = CLI_ST_PRINT;
+                               RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+                               stktable_touch_local(&px->table, ts, 1);
                                return 1;
                        }
 
@@ -2474,6 +2926,8 @@ static int table_process_entry_per_key(struct appctx *appctx, char **args)
                                appctx->ctx.cli.severity = LOG_ERR;
                                appctx->ctx.cli.msg = "Unknown data type\n";
                                appctx->st0 = CLI_ST_PRINT;
+                               RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+                               stktable_touch_local(&px->table, ts, 1);
                                return 1;
                        }
 
@@ -2481,6 +2935,8 @@ static int table_process_entry_per_key(struct appctx *appctx, char **args)
                                appctx->ctx.cli.severity = LOG_ERR;
                                appctx->ctx.cli.msg = "Data type not stored in this table\n";
                                appctx->st0 = CLI_ST_PRINT;
+                               RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+                               stktable_touch_local(&px->table, ts, 1);
                                return 1;
                        }
 
@@ -2488,6 +2944,8 @@ static int table_process_entry_per_key(struct appctx *appctx, char **args)
                                appctx->ctx.cli.severity = LOG_ERR;
                                appctx->ctx.cli.msg = "Require a valid integer value to store\n";
                                appctx->st0 = CLI_ST_PRINT;
+                               RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+                               stktable_touch_local(&px->table, ts, 1);
                                return 1;
                        }
 
@@ -2516,6 +2974,8 @@ static int table_process_entry_per_key(struct appctx *appctx, char **args)
                                break;
                        }
                }
+               RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+               stktable_touch_local(&px->table, ts, 1);
                break;
 
        default:
@@ -2658,8 +3118,7 @@ static int cli_io_handler_table(struct appctx *appctx)
        if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW))) {
                /* in case of abort, remove any refcount we might have set on an entry */
                if (appctx->st2 == STAT_ST_LIST) {
-                       appctx->ctx.table.entry->ref_cnt--;
-                       stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
+                       stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry, 1);
                }
                return 1;
        }
@@ -2692,13 +3151,16 @@ static int cli_io_handler_table(struct appctx *appctx)
                                if (appctx->ctx.table.target &&
                                    (strm_li(s)->bind_conf->level & ACCESS_LVL_MASK) >= ACCESS_LVL_OPER) {
                                        /* dump entries only if table explicitly requested */
+                                       SPIN_LOCK(STK_TABLE_LOCK, &appctx->ctx.table.proxy->table.lock);
                                        eb = ebmb_first(&appctx->ctx.table.proxy->table.keys);
                                        if (eb) {
                                                appctx->ctx.table.entry = ebmb_entry(eb, struct stksess, key);
                                                appctx->ctx.table.entry->ref_cnt++;
                                                appctx->st2 = STAT_ST_LIST;
+                                               SPIN_UNLOCK(STK_TABLE_LOCK, &appctx->ctx.table.proxy->table.lock);
                                                break;
                                        }
+                                       SPIN_UNLOCK(STK_TABLE_LOCK, &appctx->ctx.table.proxy->table.lock);
                                }
                        }
                        appctx->ctx.table.proxy = appctx->ctx.table.proxy->next;
@@ -2707,11 +3169,14 @@ static int cli_io_handler_table(struct appctx *appctx)
                case STAT_ST_LIST:
                        skip_entry = 0;
 
+                       RWLOCK_RDLOCK(STK_SESS_LOCK, &appctx->ctx.table.entry->lock);
+
                        if (appctx->ctx.table.data_type >= 0) {
                                /* we're filtering on some data contents */
                                void *ptr;
                                long long data;
 
+
                                dt = appctx->ctx.table.data_type;
                                ptr = stktable_data_ptr(&appctx->ctx.table.proxy->table,
                                                        appctx->ctx.table.entry,
@@ -2751,9 +3216,14 @@ static int cli_io_handler_table(struct appctx *appctx)
                        }
 
                        if (show && !skip_entry &&
-                           !table_dump_entry_to_buffer(&trash, si, appctx->ctx.table.proxy, appctx->ctx.table.entry))
-                           return 0;
+                           !table_dump_entry_to_buffer(&trash, si, appctx->ctx.table.proxy, appctx->ctx.table.entry)) {
+                               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &appctx->ctx.table.entry->lock);
+                               return 0;
+                       }
 
+                       RWLOCK_RDUNLOCK(STK_SESS_LOCK, &appctx->ctx.table.entry->lock);
+
+                       SPIN_LOCK(STK_TABLE_LOCK, &appctx->ctx.table.proxy->table.lock);
                        appctx->ctx.table.entry->ref_cnt--;
 
                        eb = ebmb_next(&appctx->ctx.table.entry->key);
@@ -2761,18 +3231,21 @@ static int cli_io_handler_table(struct appctx *appctx)
                                struct stksess *old = appctx->ctx.table.entry;
                                appctx->ctx.table.entry = ebmb_entry(eb, struct stksess, key);
                                if (show)
-                                       stksess_kill_if_expired(&appctx->ctx.table.proxy->table, old);
+                                       __stksess_kill_if_expired(&appctx->ctx.table.proxy->table, old);
                                else if (!skip_entry && !appctx->ctx.table.entry->ref_cnt)
-                                       stksess_kill(&appctx->ctx.table.proxy->table, old);
+                                       __stksess_kill(&appctx->ctx.table.proxy->table, old);
                                appctx->ctx.table.entry->ref_cnt++;
+                               SPIN_UNLOCK(STK_TABLE_LOCK, &appctx->ctx.table.proxy->table.lock);
                                break;
                        }
 
 
                        if (show)
-                               stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
+                               __stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
                        else if (!skip_entry && !appctx->ctx.table.entry->ref_cnt)
-                               stksess_kill(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
+                               __stksess_kill(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
+
+                       SPIN_UNLOCK(STK_TABLE_LOCK, &appctx->ctx.table.proxy->table.lock);
 
                        appctx->ctx.table.proxy = appctx->ctx.table.proxy->next;
                        appctx->st2 = STAT_ST_INFO;
@@ -2789,8 +3262,7 @@ static int cli_io_handler_table(struct appctx *appctx)
 static void cli_release_show_table(struct appctx *appctx)
 {
        if (appctx->st2 == STAT_ST_LIST) {
-               appctx->ctx.table.entry->ref_cnt--;
-               stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
+               stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry, 1);
        }
 }
 
index 7bfe7864f1ecb47a27caf9926bf6e80575d7919e..8975638ac56b9e1b8c8f751ffbafc39633d1ff59 100644 (file)
@@ -465,6 +465,7 @@ void stream_process_counters(struct stream *s)
        struct session *sess = s->sess;
        unsigned long long bytes;
        void *ptr1,*ptr2;
+       struct stksess *ts;
        int i;
 
        bytes = s->req.total - s->logs.bytes_in;
@@ -482,24 +483,28 @@ void stream_process_counters(struct stream *s)
                for (i = 0; i < MAX_SESS_STKCTR; i++) {
                        struct stkctr *stkctr = &s->stkctr[i];
 
-                       if (!stkctr_entry(stkctr)) {
+                       ts = stkctr_entry(stkctr);
+                       if (!ts) {
                                stkctr = &sess->stkctr[i];
-                               if (!stkctr_entry(stkctr))
+                               ts = stkctr_entry(stkctr);
+                               if (!ts)
                                        continue;
                        }
 
-                       ptr1 = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_BYTES_IN_CNT);
+                       RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
+                       ptr1 = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_BYTES_IN_CNT);
                        if (ptr1)
                                stktable_data_cast(ptr1, bytes_in_cnt) += bytes;
 
-                       ptr2 = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_BYTES_IN_RATE);
+                       ptr2 = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_BYTES_IN_RATE);
                        if (ptr2)
                                update_freq_ctr_period(&stktable_data_cast(ptr2, bytes_in_rate),
                                                       stkctr->table->data_arg[STKTABLE_DT_BYTES_IN_RATE].u, bytes);
+                       RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
 
                        /* If data was modified, we need to touch to re-schedule sync */
                        if (ptr1 || ptr2)
-                               stktable_touch(stkctr->table, stkctr_entry(stkctr), 1);
+                               stktable_touch_local(stkctr->table, ts, 0);
                }
        }
 
@@ -518,24 +523,28 @@ void stream_process_counters(struct stream *s)
                for (i = 0; i < MAX_SESS_STKCTR; i++) {
                        struct stkctr *stkctr = &s->stkctr[i];
 
-                       if (!stkctr_entry(stkctr)) {
+                       ts = stkctr_entry(stkctr);
+                       if (!ts) {
                                stkctr = &sess->stkctr[i];
-                               if (!stkctr_entry(stkctr))
+                               ts = stkctr_entry(stkctr);
+                               if (!ts)
                                        continue;
                        }
 
-                       ptr1 = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_BYTES_OUT_CNT);
+                       RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
+                       ptr1 = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_BYTES_OUT_CNT);
                        if (ptr1)
                                stktable_data_cast(ptr1, bytes_out_cnt) += bytes;
 
-                       ptr2 = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_BYTES_OUT_RATE);
+                       ptr2 = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_BYTES_OUT_RATE);
                        if (ptr2)
                                update_freq_ctr_period(&stktable_data_cast(ptr2, bytes_out_rate),
                                                       stkctr->table->data_arg[STKTABLE_DT_BYTES_OUT_RATE].u, bytes);
+                       RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
 
                        /* If data was modified, we need to touch to re-schedule sync */
                        if (ptr1 || ptr2)
-                               stktable_touch(stkctr->table, stkctr_entry(stkctr), 1);
+                               stktable_touch_local(stkctr->table, stkctr_entry(stkctr), 0);
                }
        }
 }
@@ -1383,8 +1392,10 @@ static int process_sticking_rules(struct stream *s, struct channel *req, int an_
                                                void *ptr;
 
                                                /* srv found in table */
+                                               RWLOCK_RDLOCK(STK_SESS_LOCK, &ts->lock);
                                                ptr = stktable_data_ptr(rule->table.t, ts, STKTABLE_DT_SERVER_ID);
                                                node = eb32_lookup(&px->conf.used_server_id, stktable_data_cast(ptr, server_id));
+                                               RWLOCK_RDUNLOCK(STK_SESS_LOCK, &ts->lock);
                                                if (node) {
                                                        struct server *srv;
 
@@ -1397,7 +1408,7 @@ static int process_sticking_rules(struct stream *s, struct channel *req, int an_
                                                        }
                                                }
                                        }
-                                       stktable_touch(rule->table.t, ts, 1);
+                                       stktable_touch_local(rule->table.t, ts, 1);
                                }
                        }
                        if (rule->flags & STK_IS_STORE) {
@@ -1501,18 +1512,18 @@ static int process_store_rules(struct stream *s, struct channel *rep, int an_bit
                        continue;
                }
 
-               ts = stktable_lookup(s->store[i].table, s->store[i].ts);
-               if (ts) {
+               ts = stktable_set_entry(s->store[i].table, s->store[i].ts);
+               if (ts != s->store[i].ts) {
                        /* the entry already existed, we can free ours */
-                       stktable_touch(s->store[i].table, ts, 1);
                        stksess_free(s->store[i].table, s->store[i].ts);
                }
-               else
-                       ts = stktable_store(s->store[i].table, s->store[i].ts, 1);
-
                s->store[i].ts = NULL;
+
+               RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
                ptr = stktable_data_ptr(s->store[i].table, ts, STKTABLE_DT_SERVER_ID);
                stktable_data_cast(ptr, server_id) = objt_server(s->target)->puid;
+               RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+               stktable_touch_local(s->store[i].table, ts, 1);
        }
        s->store_count = 0; /* everything is stored */