Skip to content

Commit 5eb97fa

Browse files
a-denoyellehaproxyFred
authored andcommitted
MEDIUM: mux-quic: do not account release buf in alloc window
Since a recent rework, a new tx buffer can only be allocated if the size of all allocated buffers in used does not exceed the underlying connection congestion window. This model caused a severe throughput degradation when a single stream is used and the congestion window is smaller than bufsize, due a poor network conditions. In this case, the stream can allocate a single buffer only. Once it is full, stream emission is interrupted until every ACKs are received to free the buffer. This is an extremely unefficient transfer model. Such issue can be reproduced using the following ngtcp2 with 10% rate loss : ngtcp2-client -q --no-quic-dump --no-http-dump --exit-on-all-streams-close \ -r 0.1 127.0.0.1 20443 "https://[::]:20443/?s=100m" To solve this, buffer allocation limit must be relaxed in part. This is the purpose of this two-part commits serie. This first patch updates QUIC MUX buffer accounting : as soon as a Tx buffer is fully sent and released, it is removed from connection buffer window. This single change fixes completely the degraded case described above. However, it is not sufficient as it virtually removes any limit on stream buffer allocation. Indeed, each time a new buffer is released, a QCS can reallocate a new buffer, which could increase heavily memory consumption. As such, another limitation must be implemented to prevent this. This will be the purpose of the next commit.
1 parent db13df3 commit 5eb97fa

File tree

2 files changed

+19
-23
lines changed

2 files changed

+19
-23
lines changed

src/mux_quic.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,7 @@ int qcc_realign_stream_txbuf(const struct qcs *qcs, struct buffer *out)
11581158
*/
11591159
int qcc_release_stream_txbuf(struct qcs *qcs)
11601160
{
1161+
struct buffer *buf = qc_stream_buf_get(qcs->stream);
11611162
const uint64_t bytes = qcs_prep_bytes(qcs);
11621163

11631164
/* Cannot release buffer if prepared data is not fully sent. */
@@ -1166,7 +1167,10 @@ int qcc_release_stream_txbuf(struct qcs *qcs)
11661167
return 1;
11671168
}
11681169

1170+
/* Free released buf size from buf window. */
11691171
qc_stream_buf_release(qcs->stream);
1172+
qcc_notify_buf(qcs->qcc, b_size(buf));
1173+
11701174
return 0;
11711175
}
11721176

@@ -1975,7 +1979,7 @@ void qcc_streams_sent_done(struct qcs *qcs, uint64_t data, uint64_t offset)
19751979
/* Release buffer if everything sent and buf is full or stream is waiting for room. */
19761980
if (!qcs_prep_bytes(qcs) &&
19771981
(b_full(&qcs->stream->buf->buf) || qcs->flags & QC_SF_BLK_MROOM)) {
1978-
qc_stream_buf_release(qcs->stream);
1982+
qcc_release_stream_txbuf(qcs);
19791983
qcs->flags &= ~QC_SF_BLK_MROOM;
19801984
qcs_notify_send(qcs);
19811985
}

src/quic_stream.c

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,22 @@ static void qc_stream_buf_free(struct qc_stream_desc *stream,
3030
{
3131
struct quic_conn *qc = stream->qc;
3232
struct buffer *buf = &(*stream_buf)->buf;
33-
uint64_t free_size;
3433

3534
LIST_DEL_INIT(&(*stream_buf)->list);
3635

3736
/* Reset current buf ptr if deleted instance is the same one. */
38-
if (*stream_buf == stream->buf)
37+
if (*stream_buf == stream->buf) {
3938
stream->buf = NULL;
4039

41-
free_size = b_size(buf);
40+
/* Only non-released buffer are notified to MUX layer. */
41+
if (qc->mux_state == QC_MUX_READY) {
42+
if (!(stream->flags & QC_SD_FL_OOB_BUF)) {
43+
/* notify MUX about available buffers. */
44+
qcc_notify_buf(qc->qcc, b_size(buf));
45+
}
46+
}
47+
}
48+
4249
if ((*stream_buf)->sbuf) {
4350
pool_free(pool_head_sbuf, buf->area);
4451
}
@@ -48,14 +55,6 @@ static void qc_stream_buf_free(struct qc_stream_desc *stream,
4855
}
4956
pool_free(pool_head_quic_stream_buf, *stream_buf);
5057
*stream_buf = NULL;
51-
52-
/* notify MUX about available buffers. */
53-
if (qc->mux_state == QC_MUX_READY) {
54-
if (!(stream->flags & QC_SD_FL_OOB_BUF)) {
55-
/* notify MUX about available buffers. */
56-
qcc_notify_buf(qc->qcc, free_size);
57-
}
58-
}
5958
}
6059

6160
/* Allocate a new stream descriptor with id <id>. The caller is responsible to
@@ -124,6 +123,9 @@ void qc_stream_desc_release(struct qc_stream_desc *stream,
124123
/* final_size cannot be greater than all currently stored data. */
125124
BUG_ON(final_size > tail_offset);
126125

126+
/* release buffer and notify MUX. */
127+
qcc_notify_buf(stream->qc->qcc, b_size(buf));
128+
127129
/* Remove unsent data from current buffer. */
128130
if (final_size < tail_offset)
129131
b_sub(buf, tail_offset - final_size);
@@ -209,15 +211,13 @@ void qc_stream_desc_free(struct qc_stream_desc *stream, int closing)
209211
struct quic_conn *qc = stream->qc;
210212
struct eb64_node *frm_node;
211213
unsigned int free_count = 0;
212-
uint64_t free_size = 0;
213214

214215
/* This function only deals with released streams. */
215216
BUG_ON(!(stream->flags & QC_SD_FL_RELEASE));
216217

217218
/* free remaining stream buffers */
218219
list_for_each_entry_safe(buf, buf_back, &stream->buf_list, list) {
219220
if (!(b_data(&buf->buf)) || closing) {
220-
free_size += b_size(&buf->buf);
221221
if (buf->sbuf)
222222
pool_free(pool_head_sbuf, buf->buf.area);
223223
else
@@ -228,17 +228,9 @@ void qc_stream_desc_free(struct qc_stream_desc *stream, int closing)
228228
}
229229
}
230230

231-
if (free_count) {
231+
if (free_count)
232232
offer_buffers(NULL, free_count);
233233

234-
if (qc->mux_state == QC_MUX_READY) {
235-
if (!(stream->flags & QC_SD_FL_OOB_BUF)) {
236-
/* notify MUX about available buffers. */
237-
qcc_notify_buf(qc->qcc, free_size);
238-
}
239-
}
240-
}
241-
242234
/* qc_stream_desc might be freed before having received all its ACKs.
243235
* This is the case if some frames were retransmitted.
244236
*/

0 commit comments

Comments
 (0)