From: Christopher Faulet Date: Fri, 5 Jul 2019 11:44:29 +0000 (+0200) Subject: BUG/MEDIUM: stream-int: Don't rely on CF_WRITE_PARTIAL to unblock opposite si X-Git-Tag: v2.1-dev1~26 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=037b3ebd3502e9eb7bbe7e56187ce1f929b4a02e;p=thirdparty%2Fhaproxy.git BUG/MEDIUM: stream-int: Don't rely on CF_WRITE_PARTIAL to unblock opposite si In the function stream_int_notify(), when the opposite stream-interface is blocked because there is no more room into the input buffer, if the flag CF_WRITE_PARTIAL is set on this buffer, it is unblocked. It is a way to unblock the reads on the other side because some data was sent. But it is a problem during the fast-forwarding because only the stream is able to remove the flag CF_WRITE_PARTIAL. So it is possible to have this flag because of a previous send while the input buffer of the opposite stream-interface is now full. In such case, the opposite stream-interface will be woken up for nothing because its input buffer is full. If the same happens on the opposite side, we will have a loop consumming all the CPU. To fix the bug, the opposite side is now only notify if there is some available room in its input buffer in the function si_cs_send(), so only if some data was sent. This patch must be backported to 2.0 and 1.9. --- diff --git a/include/proto/channel.h b/include/proto/channel.h index 5e5d8228c2..4872aa3cc1 100644 --- a/include/proto/channel.h +++ b/include/proto/channel.h @@ -950,6 +950,7 @@ static inline void co_skip(struct channel *chn, int len) /* notify that some data was written to the SI from the buffer */ chn->flags |= CF_WRITE_PARTIAL | CF_WROTE_DATA; + chn_prod(chn)->flags &= ~SI_FL_RXBLK_ROOM; // si_rx_room_rdy() } /* HTX version of co_skip(). This function skips at most bytes from the @@ -967,6 +968,7 @@ static inline void co_htx_skip(struct channel *chn, struct htx *htx, int len) /* notify that some data was written to the SI from the buffer */ chn->flags |= CF_WRITE_PARTIAL | CF_WROTE_DATA; + chn_prod(chn)->flags &= ~SI_FL_RXBLK_ROOM; // si_rx_room_rdy() } } diff --git a/src/stream_interface.c b/src/stream_interface.c index 481938f8ac..9b9a8e9fac 100644 --- a/src/stream_interface.c +++ b/src/stream_interface.c @@ -482,10 +482,6 @@ static void stream_int_notify(struct stream_interface *si) ic->rex = tick_add_ifset(now_ms, ic->rto); } - if ((sio->flags & SI_FL_RXBLK_ROOM) && - ((oc->flags & CF_WRITE_PARTIAL) || channel_is_empty(oc))) - si_rx_room_rdy(sio); - if (oc->flags & CF_DONT_READ) si_rx_chan_blk(sio); else @@ -761,6 +757,8 @@ int si_cs_send(struct conn_stream *cs) oc->flags |= CF_WRITE_PARTIAL | CF_WROTE_DATA; if (si->state == SI_ST_CON) si->state = SI_ST_RDY; + + si_rx_room_rdy(si_opposite(si)); } if (conn->flags & CO_FL_ERROR || cs->flags & (CS_FL_ERROR|CS_FL_ERR_PENDING)) { @@ -920,8 +918,7 @@ void si_sync_send(struct stream_interface *si) if (cs->conn->flags & CO_FL_ERROR) return; - if (si_cs_send(cs)) - si_rx_room_rdy(si_opposite(si)); + si_cs_send(cs); } /* Updates at once the channel flags, and timers of both stream interfaces of a