From: Amaury Denoyelle Date: Fri, 29 Nov 2024 13:28:09 +0000 (+0100) Subject: BUG/MEDIUM: quic: prevent stream freeze on pacing X-Git-Tag: v3.2-dev1~63 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9d4c26ebaa5c54714eae27a39422e0d47970c628;p=thirdparty%2Fhaproxy.git BUG/MEDIUM: quic: prevent stream freeze on pacing On snd_buf completion, QUIC MUX tasklet is scheduled if newly data has been transferred from the stream layer. Thanks to qcc_wakeup(), pacing status is removed from tasklet, which ensure next emission will reset Tx frames and use the new data. Tasklet is not scheduled if MUX is already subscribed on send due to a previous blocking condition. This is an optimization to prevent an unneeded IO handler execution. However, this causes a bug if an emission is currently delayed due to pacing. As pacing status is not removed on snd_buf, next emission process will continue emission with older data without refreshing the newly transferred one. This causes a transfer freeze. Unless there is some activity on the connection, the transfer will be eventually aborted due to idle timeout. To fix this, remove TASK_F_USR1 if tasklet wakeup is not called due to send subscription. Note that this code is also duplicated in done_ff for zero-copy transfer. This must be backported up to 3.1. --- diff --git a/src/mux_quic.c b/src/mux_quic.c index 64e6df2113..182a531dae 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -3254,8 +3254,15 @@ static size_t qmux_strm_snd_buf(struct stconn *sc, struct buffer *buf, const size_t data = qcs_prep_bytes(qcs) - old_data; if (data || fin) qcc_send_stream(qcs, 0, data); + + /* Wake up MUX to emit newly transferred data. If blocked on + * send, ensure next emission will refresh data by removing + * pacing status info. + */ if (!(qcs->qcc->wait_event.events & SUB_RETRY_SEND)) qcc_wakeup(qcs->qcc); + else + HA_ATOMIC_AND(&qcs->qcc->wait_event.tasklet->state, ~TASK_F_USR1); } end: @@ -3375,8 +3382,12 @@ static size_t qmux_strm_done_ff(struct stconn *sc) if (data || qcs->flags & QC_SF_FIN_STREAM) qcc_send_stream(qcs, 0, data); + + /* Similar to snd_buf callback. */ if (!(qcs->qcc->wait_event.events & SUB_RETRY_SEND)) qcc_wakeup(qcc); + else + HA_ATOMIC_AND(&qcs->qcc->wait_event.tasklet->state, ~TASK_F_USR1); end: TRACE_LEAVE(QMUX_EV_STRM_SEND, qcs->qcc->conn, qcs);