From: Willy Tarreau Date: Sun, 29 Mar 2009 08:18:41 +0000 (+0200) Subject: [BUG] stream_sock: check for shut{r,w} before refreshing some timeouts X-Git-Tag: v1.3.17~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d06e71179a5f7e42a65fab45748ef0fa4b1850ce;p=thirdparty%2Fhaproxy.git [BUG] stream_sock: check for shut{r,w} before refreshing some timeouts Under some circumstances, it appears possible to refresh a timeout just after a side has been shut. For instance, if poll() plans to call both read and write, and the read side calls chk_snd() which in turn causes a shutw to occur, then stream_sock_write could update its write timeout. The same problem happens the other way. The timeout checks will then not catch these cases because they ignore timeouts in case of shut{r,w}. This is very likely to be the major cause of the 100% CPU usages reported by Bart Bobrowski. The fix consists in always ensuring that a side is not shut before updating its timeout. --- diff --git a/src/stream_sock.c b/src/stream_sock.c index 9337df81db..82bf8caf66 100644 --- a/src/stream_sock.c +++ b/src/stream_sock.c @@ -242,6 +242,10 @@ int stream_sock_read(int fd) { if ((fdtab[fd].ev & (FD_POLL_IN|FD_POLL_HUP)) == FD_POLL_HUP) goto out_shutdown_r; + /* maybe we were called immediately after an asynchronous shutr */ + if (b->flags & BF_SHUTR) + goto out_wakeup; + #if defined(CONFIG_HAP_LINUX_SPLICE) if (b->to_forward && b->flags & BF_KERN_SPLICING) { @@ -461,7 +465,7 @@ int stream_sock_read(int fd) { EV_FD_CLR(fd, DIR_RD); b->rex = TICK_ETERNITY; } - else if ((b->flags & (BF_READ_PARTIAL|BF_FULL|BF_READ_NOEXP)) == BF_READ_PARTIAL) + else if ((b->flags & (BF_SHUTR|BF_READ_PARTIAL|BF_FULL|BF_READ_NOEXP)) == BF_READ_PARTIAL) b->rex = tick_add_ifset(now_ms, b->rto); /* we have to wake up if there is a special event or if we don't have @@ -647,6 +651,10 @@ int stream_sock_write(int fd) if (fdtab[fd].state == FD_STERROR || (fdtab[fd].ev & FD_POLL_ERR)) goto out_error; + /* we might have been called just after an asynchronous shutw */ + if (b->flags & BF_SHUTW) + goto out_wakeup; + if (likely(!(b->flags & BF_EMPTY))) { /* OK there are data waiting to be sent */ retval = stream_sock_write_loop(si, b); @@ -704,7 +712,7 @@ int stream_sock_write(int fd) goto out_wakeup; } - if (b->flags & BF_EMPTY) + if ((b->flags & (BF_EMPTY|BF_SHUTW)) == BF_EMPTY) si->flags |= SI_FL_WAIT_DATA; EV_FD_CLR(fd, DIR_WR); @@ -730,7 +738,7 @@ int stream_sock_write(int fd) } /* the producer might be waiting for more room to store data */ - if (likely((b->flags & (BF_WRITE_PARTIAL|BF_FULL)) == BF_WRITE_PARTIAL && + if (likely((b->flags & (BF_SHUTW|BF_WRITE_PARTIAL|BF_FULL)) == BF_WRITE_PARTIAL && (b->prod->flags & SI_FL_WAIT_ROOM))) b->prod->chk_rcv(b->prod); @@ -786,7 +794,6 @@ void stream_sock_shutw(struct stream_interface *si) } /* fall through */ case SI_ST_CON: - si->flags &= ~SI_FL_WAIT_ROOM; /* we may have to close a pending connection, and mark the * response buffer as shutr */ @@ -795,6 +802,7 @@ void stream_sock_shutw(struct stream_interface *si) case SI_ST_CER: si->state = SI_ST_DIS; default: + si->flags &= ~SI_FL_WAIT_ROOM; si->ib->flags |= BF_SHUTR; si->ib->rex = TICK_ETERNITY; si->exp = TICK_ETERNITY; @@ -893,13 +901,13 @@ void stream_sock_data_finish(struct stream_interface *si) EV_FD_COND_S(fd, DIR_WR); if (!tick_isset(ob->wex) || ob->flags & BF_WRITE_ACTIVITY) { ob->wex = tick_add_ifset(now_ms, ob->wto); - if (tick_isset(ob->wex) && tick_isset(ib->rex)) { + if (tick_isset(ib->rex)) { /* Note: depending on the protocol, we don't know if we're waiting * for incoming data or not. So in order to prevent the socket from * expiring read timeouts during writes, we refresh the read timeout, * except if it was already infinite. */ - ib->rex = ob->wex; + ib->rex = tick_add_ifset(now_ms, ib->rto); } } } @@ -991,8 +999,8 @@ void stream_sock_chk_snd(struct stream_interface *si) stream_sock_shutw(si); goto out_wakeup; } - - if ((ob->flags & (BF_EMPTY|BF_HIJACK|BF_WRITE_ENA)) == (BF_EMPTY|BF_WRITE_ENA)) + + if ((ob->flags & (BF_SHUTW|BF_EMPTY|BF_HIJACK|BF_WRITE_ENA)) == (BF_EMPTY|BF_WRITE_ENA)) si->flags |= SI_FL_WAIT_DATA; ob->wex = TICK_ETERNITY; }