From f5a0c8abf5e8ee35ad4cbe92414e3d25a55d30f8 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Thu, 13 Oct 2022 16:14:11 +0200 Subject: [PATCH] MEDIUM: quic: respect the threads assigned to a bind line Right now the QUIC thread mapping derives the thread ID from the CID by dividing by global.nbthread. This is a problem because this makes QUIC work on all threads and ignores the "thread" directive on the bind lines. In addition, only 8 bits are used, which is no more compatible with the up to 4096 threads we may have in a configuration. Let's modify it this way: - the CID now dedicates 12 bits to the thread ID - on output we continue to place the TID directly there. - on input, the value is extracted. If it corresponds to a valid thread number of the bind_conf, it's used as-is. - otherwise it's used as a rank within the current bind_conf's thread mask so that in the end we still get a valid thread ID for this bind_conf. The extraction function now requires a bind_conf in order to get the group and thread mask. It was better to use bind_confs now as the goal is to make them support multiple listeners sooner or later. --- include/haproxy/quic_conn.h | 45 ++++++++++++++++++++++++++++++------- src/quic_conn.c | 3 ++- src/quic_sock.c | 3 ++- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/include/haproxy/quic_conn.h b/include/haproxy/quic_conn.h index 2a962f4a47..a2292d008d 100644 --- a/include/haproxy/quic_conn.h +++ b/include/haproxy/quic_conn.h @@ -213,19 +213,48 @@ static inline void quic_connection_id_to_frm_cpy(struct quic_frame *dst, to->stateless_reset_token = src->stateless_reset_token; } -/* Retrieve the associated thread ID for . */ -static inline unsigned long quic_get_cid_tid(const unsigned char *cid) +/* extract a TID from a CID for bind_conf , from 0 to global.nbthread-1 and + * in any case no more than 4095. It takes into account the bind_conf's thread + * group and the bind_conf's thread mask. The algorithm is the following: most + * packets contain a valid thread ID for the bind_conf, which means that the + * retrieved ID directly maps to a bound thread ID. If that's not the case, + * then we have to remap it. The resulting thread ID will then differ but will + * be correctly encoded and decoded. + */ +static inline uint quic_get_cid_tid(const unsigned char *cid, const struct bind_conf *bc) { - return *cid % global.nbthread; + uint id, grp; + uint base, count; + + id = read_n16(cid) & 4095; + grp = bc->bind_tgroup; + base = ha_tgroup_info[grp - 1].base; + count = ha_tgroup_info[grp - 1].count; + + if (base <= id && id < base + count && + bc->bind_thread & ha_thread_info[id].ltid_bit) + return id; // part of the group and bound: valid + + /* The thread number isn't valid, it doesn't map to a thread bound on + * this receiver. Let's reduce it to one of the thread(s) valid for + * that receiver. + */ + count = my_popcountl(bc->bind_thread); + id = count - 1 - id % count; + id = mask_find_rank_bit(id, bc->bind_thread); + id += base; + return id; } -/* Modify to have a CID linked to the thread ID . This is - * based on quic_get_cid_tid. +/* Modify to have a CID linked to the thread ID that + * quic_get_cid_tid() will be able to extract return. */ -static inline void quic_pin_cid_to_tid(unsigned char *cid, int target_tid) +static inline void quic_pin_cid_to_tid(unsigned char *cid, uint target_tid) { - cid[0] = MIN(cid[0], 255 - target_tid); - cid[0] = cid[0] - (cid[0] % global.nbthread) + target_tid; + uint16_t prev_id; + + prev_id = read_n16(cid); + write_n16(cid, (prev_id & ~4095) | target_tid); } /* Return a 32-bits integer in from QUIC packet with as address. diff --git a/src/quic_conn.c b/src/quic_conn.c index b0ee04f4c0..88993dc16e 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -4863,7 +4863,8 @@ static struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4, /* Set tasklet tid based on the SCID selected by us for this * connection. The upper layer will also be binded on the same thread. */ - qc->tid = qc->wait_event.tasklet->tid = quic_get_cid_tid(qc->scid.data); + qc->tid = quic_get_cid_tid(qc->scid.data, l->bind_conf); + qc->wait_event.tasklet->tid = qc->tid; if (qc_conn_alloc_ssl_ctx(qc) || !quic_conn_init_timer(qc) || diff --git a/src/quic_sock.c b/src/quic_sock.c index 0bb6673175..8617489203 100644 --- a/src/quic_sock.c +++ b/src/quic_sock.c @@ -246,6 +246,7 @@ static int quic_lstnr_dgram_dispatch(unsigned char *buf, size_t len, void *owner struct quic_dgram *new_dgram, struct list *dgrams) { struct quic_dgram *dgram; + const struct listener *l = owner; unsigned char *dcid; size_t dcid_len; int cid_tid; @@ -257,7 +258,7 @@ static int quic_lstnr_dgram_dispatch(unsigned char *buf, size_t len, void *owner if (!dgram) goto err; - cid_tid = quic_get_cid_tid(dcid); + cid_tid = quic_get_cid_tid(dcid, l->bind_conf); /* All the members must be initialized! */ dgram->owner = owner; -- 2.39.5