[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.
This commit is contained in:
Willy Tarreau 2009-03-29 10:18:41 +02:00
parent c6dcad6e74
commit d06e71179a

View File

@ -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;
}