From 819fc6f5636b08e252b46c5de4fec406c13b7505 Mon Sep 17 00:00:00 2001 From: Emeric Brun Date: Tue, 13 Jun 2017 19:37:32 +0200 Subject: [PATCH] MEDIUM: threads/stick-tables: handle multithreads on stick tables 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 | 4 +- include/proto/session.h | 17 +- include/proto/stick_table.h | 40 +- include/proto/stream.h | 80 +++- include/types/stick_table.h | 6 + src/peers.c | 121 +++-- src/proto_http.c | 35 +- src/stick_table.c | 864 ++++++++++++++++++++++++++++-------- src/stream.c | 45 +- 9 files changed, 912 insertions(+), 300 deletions(-) diff --git a/include/common/hathreads.h b/include/common/hathreads.h index 9946d51163..e997ea3a9e 100644 --- a/include/common/hathreads.h +++ b/include/common/hathreads.h @@ -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++) { diff --git a/include/proto/session.h b/include/proto/session.h index cb4deeceb5..3dead44488 100644 --- a/include/proto/session.h +++ b/include/proto/session.h @@ -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); } } diff --git a/include/proto/stick_table.h b/include/proto/stick_table.h index f48b9eb496..8c9f834526 100644 --- a/include/proto/stick_table.h +++ b/include/proto/stick_table.h @@ -34,17 +34,15 @@ 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 */ 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 */ diff --git a/include/proto/stream.h b/include/proto/stream.h index aae7d345dc..3efb42bacf 100644 --- a/include/proto/stream.h +++ b/include/proto/stream.h @@ -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 on stksess . 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); } } diff --git a/include/types/stick_table.h b/include/types/stick_table.h index 77eeccdc2d..4f5de99865 100644 --- a/include/types/stick_table.h +++ b/include/types/stick_table.h @@ -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; diff --git a/src/peers.c b/src/peers.c index 579e096d71..25f1ba32b7 100644 --- a/src/peers.c +++ b/src/peers.c @@ -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); } } diff --git a/src/proto_http.c b/src/proto_http.c index f60f8ed1c7..d32b7c5e80 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -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; diff --git a/src/stick_table.c b/src/stick_table.c index 5e8211623c..55a1fff8da 100644 --- a/src/stick_table.c +++ b/src/stick_table.c @@ -42,30 +42,61 @@ #include /* 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 , and decrease sticky sessions counter * in table . */ -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 , and decrease sticky sessions counter + * in table . + * 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 of table . The data parts are cleared and * 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 sticky sessions from table * 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 sticky sessions from table + * 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 's sticky session counter is increased. If * 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 's sticky session counter is increased. If + * 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 for a sticky session matching 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 in , 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 for a sticky session matching 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 for a sticky session with same key as . * 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 for a sticky session with same key as . + * 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 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 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 * 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 but do not touch its expiration node. + * The table's expiration timer is updated using the date of expiration coming from + * 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 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. 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 as supplementary argument - * to set the date of expiration of 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 . 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, 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.\" 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); } } diff --git a/src/stream.c b/src/stream.c index 7bfe7864f1..8975638ac5 100644 --- a/src/stream.c +++ b/src/stream.c @@ -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 */ -- 2.39.5