BUG/MAJOR: stconn: Check support for zero-copy forwarding on both sides

There is a nego stage when a producer is ready to forward data to the other
side. At this stage, the zero-copy forwarding may be disabled if the
consumer does not support it. However, there is a flaw with this way to
proceed. If the channel buffer is not empty, we delay the zero-copy
forwarding to flush all data from the channel first. During this delay,
receives on the endpoint (at connection level for muxes), are blocked to be
sure to have the opportunity to switch on zero-copy forwarding. It is a
problem if the consumer cannot flush data from the channel's buffer, waiting
for more data for instance.

It is especially annoying with the CLI applet, because this scenario can
happen if a command is partially received. For instance without the LF at
the end. In this case, the CLI applet is blocked because it waits more
data. The frontend connexion is also blocked because channel's data must be
flushed before trying to receive more data. Worst, this happen at where no
timeout is armed. Thus the session is stuck infinitly, client aborts cannot
be detected because receives are blocked, and the applet cannot abort on its
side because there are pending outgoing data. It is clearly a situation
where it is easy to consume all CLI slots.

To fix the issue, thanks to previous commits, we now check zero-copy
forwarding support on both sides before proceeding.

This patch relies on the following commits:

  * MINOR: muxes: Announce support for zero-copy forwarding on consumer side
  * MINOR: stconn: Add SE flag to announce zero-copy forwarding on consumer side
  * MINOR: stconn: Rename SE_FL_MAY_FASTFWD and reorder bitfield
  * CLEANUP: stconn: Move SE flags set by app layer at the end of the bitfield

All the series must be backported to 2.9.
This commit is contained in:
Christopher Faulet 2024-02-14 15:29:38 +01:00
parent e2921ffad1
commit ddf6b7539c

View File

@ -544,6 +544,14 @@ static inline int sc_cond_forward_shut(struct stconn *sc)
return 1;
}
static inline int sc_is_fastfwd_supported(struct stconn *sc)
{
return (!(global.tune.no_zero_copy_fwd & NO_ZERO_COPY_FWD) &&
sc_ep_test(sc, SE_FL_MAY_FASTFWD_PROD) &&
sc_ep_test(sc_opposite(sc), SE_FL_MAY_FASTFWD_CONS) &&
sc_ic(sc)->to_forward);
}
/*
* This function performs a shutdown-read on a detached stream connector in a
* connected or init state (it does nothing for other states). It either shuts
@ -1267,8 +1275,7 @@ int sc_conn_recv(struct stconn *sc)
/* First, let's see if we may fast-forward data from a side to the other
* one without using the channel buffer.
*/
if (!(global.tune.no_zero_copy_fwd & NO_ZERO_COPY_FWD) &&
sc_ep_test(sc, SE_FL_MAY_FASTFWD_PROD) && ic->to_forward) {
if (sc_is_fastfwd_supported(sc)) {
if (channel_data(ic)) {
/* We're embarrassed, there are already data pending in
* the buffer and we don't want to have them at two
@ -1907,8 +1914,7 @@ int sc_applet_recv(struct stconn *sc)
/* First, let's see if we may fast-forward data from a side to the other
* one without using the channel buffer.
*/
if (!(global.tune.no_zero_copy_fwd & NO_ZERO_COPY_FWD) &&
sc_ep_test(sc, SE_FL_MAY_FASTFWD_PROD) && ic->to_forward) {
if (sc_is_fastfwd_supported(sc)) {
if (channel_data(ic)) {
/* We're embarrassed, there are already data pending in
* the buffer and we don't want to have them at two