From: Amaury Denoyelle Date: Wed, 19 Jan 2022 15:01:05 +0000 (+0100) Subject: MAJOR: quic: implement accept queue X-Git-Tag: v2.6-dev1~69 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=cfa2d5648fd668d1bcb09530757d28b5e95b5758;p=thirdparty%2Fhaproxy.git MAJOR: quic: implement accept queue Do not proceed to direct accept when creating a new quic_conn. Wait for the QUIC handshake to succeeds to insert the quic_conn in the accept queue. A tasklet is then woken up to call listener_accept to accept the quic_conn. The most important effect is that the connection/mux layers are not instantiated at the same time as the quic_conn. This forces to delay some process to be sure that the mux is allocated : * initialization of mux transport parameters * installation of the app-ops Also, the mux instance is not checked now to wake up the quic_conn tasklet. This is safe because the xprt-quic code is now ready to handle the absence of the connection/mux layers. Note that this commit has a deep impact as it changes significantly the lower QUIC architecture. Most notably, it breaks the 0-RTT feature. --- diff --git a/include/haproxy/quic_sock.h b/include/haproxy/quic_sock.h index 2a2818bc51..1f2628f0d1 100644 --- a/include/haproxy/quic_sock.h +++ b/include/haproxy/quic_sock.h @@ -39,6 +39,8 @@ int quic_sock_accepting_conn(const struct receiver *rx); struct connection *quic_sock_accept_conn(struct listener *l, int *status); void quic_sock_fd_iocb(int fd); +void quic_accept_push_qc(struct quic_conn *qc); + #endif /* USE_QUIC */ #endif /* _HAPROXY_QUIC_SOCK_H */ diff --git a/include/haproxy/receiver-t.h b/include/haproxy/receiver-t.h index 7c04d48714..ddf3c3564d 100644 --- a/include/haproxy/receiver-t.h +++ b/include/haproxy/receiver-t.h @@ -65,7 +65,6 @@ struct receiver { struct rx_settings *settings; /* points to the settings used by this receiver */ struct list proto_list; /* list in the protocol header */ #ifdef USE_QUIC - struct mt_list pkts; /* QUIC Initial packets to accept new connections */ struct eb_root odcids; /* QUIC original destination connection IDs. */ struct eb_root cids; /* QUIC connection IDs. */ __decl_thread(HA_RWLOCK_T cids_lock); /* RW lock for connection IDs tree accesses */ diff --git a/include/haproxy/xprt_quic-t.h b/include/haproxy/xprt_quic-t.h index bbf1b99ac1..2ae067f116 100644 --- a/include/haproxy/xprt_quic-t.h +++ b/include/haproxy/xprt_quic-t.h @@ -732,6 +732,7 @@ struct quic_conn { struct quic_path *path; struct listener *li; /* only valid for frontend connections */ + struct mt_list accept_list; /* chaining element used for accept, only valid for frontend connections */ /* MUX */ struct qcc *qcc; struct task *timer_task; diff --git a/src/proto_quic.c b/src/proto_quic.c index dc3d26219f..4307ceee19 100644 --- a/src/proto_quic.c +++ b/src/proto_quic.c @@ -520,7 +520,6 @@ static void quic_add_listener(struct protocol *proto, struct listener *listener) { listener->flags |= LI_F_QUIC_LISTENER; - MT_LIST_INIT(&listener->rx.pkts); listener->rx.odcids = EB_ROOT_UNIQUE; listener->rx.cids = EB_ROOT_UNIQUE; listener->rx.flags |= RX_F_LOCAL_ACCEPT; diff --git a/src/quic_sock.c b/src/quic_sock.c index 866eaa2878..ee6a2fad7d 100644 --- a/src/quic_sock.c +++ b/src/quic_sock.c @@ -140,30 +140,25 @@ int quic_sock_accepting_conn(const struct receiver *rx) struct connection *quic_sock_accept_conn(struct listener *l, int *status) { struct quic_conn *qc; - struct quic_rx_packet *pkt; - int ret; + struct li_per_thread *lthr = &l->per_thr[tid]; - qc = NULL; - pkt = MT_LIST_POP(&l->rx.pkts, struct quic_rx_packet *, rx_list); - /* Should never happen. */ - if (!pkt) - goto err; + qc = MT_LIST_POP(<hr->quic_accept.conns, struct quic_conn *, accept_list); + if (!qc) + goto done; - qc = pkt->qc; - if (!new_quic_cli_conn(qc, l, &pkt->saddr)) + if (!new_quic_cli_conn(qc, l, &qc->peer_addr)) goto err; - ret = CO_AC_DONE; - done: - if (status) - *status = ret; - + *status = CO_AC_DONE; return qc ? qc->conn : NULL; err: - ret = CO_AC_PAUSE; - goto done; + /* in case of error reinsert the element to process it later. */ + MT_LIST_INSERT(<hr->quic_accept.conns, &qc->accept_list); + + *status = CO_AC_PAUSE; + return NULL; } /* Function called on a read event from a listening socket. It tries @@ -228,6 +223,32 @@ void quic_sock_fd_iocb(int fd) /* per-thread accept queues */ struct quic_accept_queue *quic_accept_queues; +/* Install on the queue ready to be accepted. The queue task is then woken + * up. + */ +void quic_accept_push_qc(struct quic_conn *qc) +{ + struct quic_accept_queue *queue = &quic_accept_queues[qc->tid]; + struct li_per_thread *lthr = &qc->li->per_thr[qc->tid]; + + BUG_ON(MT_LIST_INLIST(&qc->accept_list)); + + /* 1. insert the listener in the accept queue + * + * Use TRY_APPEND as there is a possible race even with INLIST if + * multiple threads try to add the same listener instance from several + * quic_conn. + */ + if (!MT_LIST_INLIST(&(lthr->quic_accept.list))) + MT_LIST_TRY_APPEND(&queue->listeners, &(lthr->quic_accept.list)); + + /* 2. insert the quic_conn in the listener per-thread queue. */ + MT_LIST_APPEND(<hr->quic_accept.conns, &qc->accept_list); + + /* 3. wake up the queue tasklet */ + tasklet_wakeup(quic_accept_queues[qc->tid].tasklet); +} + /* Tasklet handler to accept QUIC connections. Call listener_accept on every * listener instances registered in the accept queue. */ diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 572fa76476..0b65d888ea 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -2523,8 +2523,6 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *arg) if (!quic_transport_params_store(qc, 0, extension_data, extension_data + extension_len)) goto abort; - - quic_mux_transport_params_update(qc->qcc); } #endif /* USE_QUIC */ diff --git a/src/xprt_quic.c b/src/xprt_quic.c index 26e085f148..588d8a24a2 100644 --- a/src/xprt_quic.c +++ b/src/xprt_quic.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -1048,12 +1049,6 @@ int quic_set_app_ops(struct quic_conn *qc, const unsigned char *alpn, size_t alp else return 0; - if (qcc_install_app_ops(qc->qcc, qc->app_ops)) - return 0; - - /* mux-quic can now be considered ready. */ - qc->mux_state = QC_MUX_READY; - return 1; } @@ -1892,10 +1887,14 @@ static inline int qc_provide_cdata(struct quic_enc_level *el, } TRACE_PROTO("SSL handshake OK", QUIC_EV_CONN_HDSHK, qc, &state); - if (qc_is_listener(ctx->qc)) + if (qc_is_listener(ctx->qc)) { HA_ATOMIC_STORE(&qc->state, QUIC_HS_ST_CONFIRMED); - else + /* The connection is ready to be accepted. */ + quic_accept_push_qc(qc); + } + else { HA_ATOMIC_STORE(&qc->state, QUIC_HS_ST_COMPLETE); + } } else { ssl_err = SSL_process_quic_post_handshake(ctx->ssl); if (ssl_err != 1) { @@ -3662,6 +3661,9 @@ static struct quic_conn *qc_new_conn(unsigned int version, int ipv4, qc->path = &qc->paths[0]; quic_path_init(qc->path, ipv4, default_quic_cc_algo, qc); + /* required to use MTLIST_IN_LIST */ + MT_LIST_INIT(&qc->accept_list); + TRACE_LEAVE(QUIC_EV_CONN_INIT, qc); return qc; @@ -4557,9 +4559,6 @@ static ssize_t qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, if (likely(!qc_to_purge)) { /* Enqueue this packet. */ pkt->qc = qc; - MT_LIST_APPEND(&l->rx.pkts, &pkt->rx_list); - /* Try to accept a new connection. */ - listener_accept(l); } else { quic_conn_drop(qc_to_purge); @@ -4663,7 +4662,7 @@ static ssize_t qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, * initialized. */ conn_ctx = HA_ATOMIC_LOAD(&qc->xprt_ctx); - if (conn_ctx && HA_ATOMIC_LOAD(&qc->qcc)) + if (conn_ctx) tasklet_wakeup(conn_ctx->wait_event.tasklet); TRACE_LEAVE(QUIC_EV_CONN_LPKT, qc ? qc : NULL, pkt); @@ -5484,6 +5483,15 @@ static int qc_xprt_start(struct connection *conn, void *ctx) return 0; } + quic_mux_transport_params_update(qc->qcc); + if (qcc_install_app_ops(qc->qcc, qc->app_ops)) { + TRACE_PROTO("Cannot install app layer", QUIC_EV_CONN_LPKT, qc); + return 0; + } + + /* mux-quic can now be considered ready. */ + qc->mux_state = QC_MUX_READY; + tasklet_wakeup(qctx->wait_event.tasklet); return 1; }