diff --git a/include/common/hathreads.h b/include/common/hathreads.h index 9946d5116..e997ea3a9 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 cb4deeceb..3dead4448 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 f48b9eb49..8c9f83452 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 aae7d345d..3efb42bac 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 77eeccdc2..4f5de9986 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 579e096d7..25f1ba32b 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 @@ static void peer_io_handler(struct appctx *appctx) 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 @@ static void peer_io_handler(struct appctx *appctx) } /* 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 @@ static void peer_io_handler(struct appctx *appctx) 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 @@ static void peer_io_handler(struct appctx *appctx) 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 @@ static void peer_io_handler(struct appctx *appctx) 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 @@ static void peer_io_handler(struct appctx *appctx) 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 @@ static void peer_io_handler(struct appctx *appctx) } } } + + 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 @@ static void peer_io_handler(struct appctx *appctx) /* 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 @@ static void peer_io_handler(struct appctx *appctx) } 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 @@ static void peer_io_handler(struct appctx *appctx) 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 @@ static void peer_io_handler(struct appctx *appctx) /* 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 @@ static void peer_io_handler(struct appctx *appctx) } 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 @@ static void peer_io_handler(struct appctx *appctx) 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 @@ static void peer_io_handler(struct appctx *appctx) /* 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 @@ static void peer_io_handler(struct appctx *appctx) } 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 @@ static void peer_io_handler(struct appctx *appctx) 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 f60f8ed1c..d32b7c5e8 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -2721,7 +2721,7 @@ http_req_get_intercept_rule(struct proxy *px, struct list *rules, struct stream 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 @@ http_req_get_intercept_rule(struct proxy *px, struct list *rules, struct stream 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 @@ http_res_get_intercept_rule(struct proxy *px, struct list *rules, struct stream 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 @@ http_res_get_intercept_rule(struct proxy *px, struct list *rules, struct stream 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 @@ http_res_get_intercept_rule(struct proxy *px, struct list *rules, struct stream 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 5e8211623..55a1fff8d 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 7bfe7864f..8975638ac 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 */