MINOR: mux-h2: add a per-connection list of blocked streams

Currently the H2 mux doesn't have a list of all the streams blocking on
the H2 side. It only knows about those trying to send or waiting for a
connection window update. It is problematic to enforce timeouts because
we never know if a stream has to live as long as the data layer wants or
has to be timed out becase it's waiting for a stream window update. This
patch adds a new list, "blocked_list", to store streams blocking on
stream flow control, or later, dependencies. Streams blocked on sfctl
are now added there. It doesn't modify the rest of the logic.
This commit is contained in:
Willy Tarreau 2019-10-02 10:49:59 +02:00
parent 35fb846333
commit 9edf6dbecc
1 changed files with 14 additions and 7 deletions

View File

@ -131,6 +131,7 @@ struct h2c {
struct eb_root streams_by_id; /* all active streams by their ID */ struct eb_root streams_by_id; /* all active streams by their ID */
struct list send_list; /* list of blocked streams requesting to send */ struct list send_list; /* list of blocked streams requesting to send */
struct list fctl_list; /* list of streams blocked by connection's fctl */ struct list fctl_list; /* list of streams blocked by connection's fctl */
struct list blocked_list; /* list of streams blocked for other reasons (e.g. sfctl, dep) */
struct list sending_list; /* list of h2s scheduled to send data */ struct list sending_list; /* list of h2s scheduled to send data */
struct buffer_wait buf_wait; /* wait list for buffer allocations */ struct buffer_wait buf_wait; /* wait list for buffer allocations */
struct wait_event wait_event; /* To be used if we're waiting for I/Os */ struct wait_event wait_event; /* To be used if we're waiting for I/Os */
@ -169,9 +170,9 @@ enum h2_ss {
/* stream flags indicating the reason the stream is blocked */ /* stream flags indicating the reason the stream is blocked */
#define H2_SF_BLK_MBUSY 0x00000010 // blocked waiting for mux access (transient) #define H2_SF_BLK_MBUSY 0x00000010 // blocked waiting for mux access (transient)
#define H2_SF_BLK_MROOM 0x00000020 // blocked waiting for room in the mux #define H2_SF_BLK_MROOM 0x00000020 // blocked waiting for room in the mux (must be in send list)
#define H2_SF_BLK_MFCTL 0x00000040 // blocked due to mux fctl #define H2_SF_BLK_MFCTL 0x00000040 // blocked due to mux fctl (must be in fctl list)
#define H2_SF_BLK_SFCTL 0x00000080 // blocked due to stream fctl #define H2_SF_BLK_SFCTL 0x00000080 // blocked due to stream fctl (must be in blocked list)
#define H2_SF_BLK_ANY 0x000000F0 // any of the reasons above #define H2_SF_BLK_ANY 0x000000F0 // any of the reasons above
/* stream flags indicating how data is supposed to be sent */ /* stream flags indicating how data is supposed to be sent */
@ -829,6 +830,7 @@ static int h2_init(struct connection *conn, struct proxy *prx, struct session *s
h2c->streams_by_id = EB_ROOT; h2c->streams_by_id = EB_ROOT;
LIST_INIT(&h2c->send_list); LIST_INIT(&h2c->send_list);
LIST_INIT(&h2c->fctl_list); LIST_INIT(&h2c->fctl_list);
LIST_INIT(&h2c->blocked_list);
LIST_INIT(&h2c->sending_list); LIST_INIT(&h2c->sending_list);
LIST_INIT(&h2c->buf_wait.list); LIST_INIT(&h2c->buf_wait.list);
@ -1901,7 +1903,8 @@ static void h2c_unblock_sfctl(struct h2c *h2c)
h2s = container_of(node, struct h2s, by_id); h2s = container_of(node, struct h2s, by_id);
if (h2s->flags & H2_SF_BLK_SFCTL && h2s_mws(h2s) > 0) { if (h2s->flags & H2_SF_BLK_SFCTL && h2s_mws(h2s) > 0) {
h2s->flags &= ~H2_SF_BLK_SFCTL; h2s->flags &= ~H2_SF_BLK_SFCTL;
if (h2s->send_wait && !LIST_ADDED(&h2s->list)) LIST_DEL_INIT(&h2s->list);
if (h2s->send_wait)
LIST_ADDQ(&h2c->send_list, &h2s->list); LIST_ADDQ(&h2c->send_list, &h2s->list);
} }
node = eb32_next(node); node = eb32_next(node);
@ -2239,7 +2242,8 @@ static int h2c_handle_window_update(struct h2c *h2c, struct h2s *h2s)
h2s->sws += inc; h2s->sws += inc;
if (h2s_mws(h2s) > 0 && (h2s->flags & H2_SF_BLK_SFCTL)) { if (h2s_mws(h2s) > 0 && (h2s->flags & H2_SF_BLK_SFCTL)) {
h2s->flags &= ~H2_SF_BLK_SFCTL; h2s->flags &= ~H2_SF_BLK_SFCTL;
if (h2s->send_wait && !LIST_ADDED(&h2s->list)) LIST_DEL_INIT(&h2s->list);
if (h2s->send_wait)
LIST_ADDQ(&h2c->send_list, &h2s->list); LIST_ADDQ(&h2c->send_list, &h2s->list);
} }
} }
@ -5120,6 +5124,7 @@ static size_t h2s_frt_make_resp_data(struct h2s *h2s, struct buffer *buf, size_t
h2s->flags |= H2_SF_BLK_SFCTL; h2s->flags |= H2_SF_BLK_SFCTL;
if (LIST_ADDED(&h2s->list)) if (LIST_ADDED(&h2s->list))
LIST_DEL_INIT(&h2s->list); LIST_DEL_INIT(&h2s->list);
LIST_ADDQ(&h2c->blocked_list, &h2s->list);
TRACE_STATE("stream window <=0, flow-controlled", H2_EV_TX_FRAME|H2_EV_TX_DATA|H2_EV_H2S_FCTL, h2c->conn, h2s); TRACE_STATE("stream window <=0, flow-controlled", H2_EV_TX_FRAME|H2_EV_TX_DATA|H2_EV_H2S_FCTL, h2c->conn, h2s);
goto end; goto end;
} }
@ -5737,10 +5742,12 @@ static size_t h2_snd_buf(struct conn_stream *cs, struct buffer *buf, size_t coun
else else
cs->flags |= CS_FL_ERR_PENDING; cs->flags |= CS_FL_ERR_PENDING;
} }
if (total > 0) {
/* Ok we managed to send something, leave the send_list */ if (total > 0 && !(h2s->flags & H2_SF_BLK_SFCTL)) {
/* Ok we managed to send something, leave the send_list if we were still there */
LIST_DEL_INIT(&h2s->list); LIST_DEL_INIT(&h2s->list);
} }
TRACE_LEAVE(H2_EV_H2S_SEND|H2_EV_STRM_SEND, h2s->h2c->conn, h2s); TRACE_LEAVE(H2_EV_H2S_SEND|H2_EV_STRM_SEND, h2s->h2c->conn, h2s);
return total; return total;
} }