void qcc_set_error(struct qcc *qcc, int err, int app);
int qcc_report_glitch(struct qcc *qcc, int inc);
struct qcs *qcc_init_stream_local(struct qcc *qcc, int bidi);
+void qcs_send_metadata(struct qcs *qcs);
struct stconn *qcs_attach_sc(struct qcs *qcs, struct buffer *buf, char fin);
int qcs_is_close_local(struct qcs *qcs);
int qcs_is_close_remote(struct qcs *qcs);
return NULL;
}
+/* Mark <qcs> as reserved for metadata transfer. As such, future txbuf
+ * allocation won't be accounted against connection limit.
+ */
+void qcs_send_metadata(struct qcs *qcs)
+{
+ /* Reserved for stream with Tx capability. */
+ BUG_ON(!qcs->stream);
+ /* Cannot use if some data already transferred for this stream. */
+ BUG_ON(!LIST_ISEMPTY(&qcs->stream->buf_list));
+
+ qcs->stream->flags |= QC_SD_FL_OOB_BUF;
+}
+
struct stconn *qcs_attach_sc(struct qcs *qcs, struct buffer *buf, char fin)
{
struct qcc *qcc = qcs->qcc;
* cause when the buffer cannot be allocated. It is set to 0 if the connection
* buffer limit is reached. For fatal errors, its value is non-zero.
*
+ * Streams reserved for application protocol metadata transfer are not subject
+ * to the buffer limit per connection. Hence, for them only a memory error
+ * can prevent a buffer allocation.
+ *
* Returns buffer pointer. May be NULL on allocation failure, in which case
* <err> will refer to the cause.
*/
{
struct qcc *qcc = qcs->qcc;
struct buffer *out = qc_stream_buf_get(qcs->stream);
+ const int unlimited = qcs->stream->flags & QC_SD_FL_OOB_BUF;
/* Stream must not try to reallocate a buffer if currently waiting for one. */
BUG_ON(LIST_INLIST(&qcs->el_buf));
*err = 0;
if (!out) {
- if (qcc->flags & QC_CF_CONN_FULL) {
- LIST_APPEND(&qcc->buf_wait_list, &qcs->el_buf);
- tot_time_start(&qcs->timer.buf);
- goto out;
- }
+ if (likely(!unlimited)) {
+ if ((qcc->flags & QC_CF_CONN_FULL)) {
+ LIST_APPEND(&qcc->buf_wait_list, &qcs->el_buf);
+ tot_time_start(&qcs->timer.buf);
+ goto out;
+ }
- if (!qcc->tx.avail_bufs) {
- TRACE_STATE("hitting stream desc buffer limit", QMUX_EV_QCS_SEND, qcc->conn, qcs);
- LIST_APPEND(&qcc->buf_wait_list, &qcs->el_buf);
- tot_time_start(&qcs->timer.buf);
- qcc->flags |= QC_CF_CONN_FULL;
- goto out;
+ if (!qcc->tx.avail_bufs) {
+ TRACE_STATE("hitting stream desc buffer limit", QMUX_EV_QCS_SEND, qcc->conn, qcs);
+ LIST_APPEND(&qcc->buf_wait_list, &qcs->el_buf);
+ tot_time_start(&qcs->timer.buf);
+ qcc->flags |= QC_CF_CONN_FULL;
+ goto out;
+ }
}
out = qc_stream_buf_alloc(qcs->stream, qcs->tx.fc.off_real);
goto out;
}
- --qcc->tx.avail_bufs;
+ if (likely(!unlimited))
+ --qcc->tx.avail_bufs;
}
out:
/* notify MUX about available buffers. */
if (qc->mux_state == QC_MUX_READY) {
- /* notify MUX about available buffers. */
- qcc_notify_buf(qc->qcc, 1);
+ if (!(stream->flags & QC_SD_FL_OOB_BUF)) {
+ /* notify MUX about available buffers. */
+ qcc_notify_buf(qc->qcc, 1);
+ }
}
}
b_free(&buf->buf);
LIST_DELETE(&buf->list);
pool_free(pool_head_quic_stream_buf, buf);
-
++free_count;
}
}
offer_buffers(NULL, free_count);
if (qc->mux_state == QC_MUX_READY) {
- /* notify MUX about available buffers. */
- qcc_notify_buf(qc->qcc, free_count);
+ if (!(stream->flags & QC_SD_FL_OOB_BUF)) {
+ /* notify MUX about available buffers. */
+ qcc_notify_buf(qc->qcc, free_count);
+ }
}
}