DIR_WR=1,
};
-/* Polling status flags returned in fdtab[].ev :
- * FD_POLL_IN remains set as long as some data is pending for read.
- * FD_POLL_OUT remains set as long as the fd accepts to write data.
- * FD_POLL_ERR and FD_POLL_ERR remain set forever (until processed).
- */
-#define FD_POLL_IN 0x00000100
-#define FD_POLL_PRI 0x00000200
-#define FD_POLL_OUT 0x00000400
-#define FD_POLL_ERR 0x00000800
-#define FD_POLL_HUP 0x00001000
-
-#define FD_POLL_UPDT_MASK (FD_POLL_IN | FD_POLL_PRI | FD_POLL_OUT)
-/* FD_EV_* are the values used in fdtab[].state to define the polling states in
- * each direction. Most of them are manipulated using test-and-set operations
- * which require the bit position in the mask, which is given in the _BIT
- * variant.
+/* fdtab[].state is a composite state describing what is known about the FD.
+ * For now, the following information are stored in it:
+ * - event configuration and status for each direction (R,W) split into
+ * active, ready, shutdown categories (FD_EV_*). These are known by their
+ * bit values as well so that test-and-set bit operations are possible.
+ *
+ * - last known polling status (FD_POLL_*). For ease of troubleshooting,
+ * avoid visually mixing these ones with the other ones above. 3 of these
+ * flags are updated on each poll() report (FD_POLL_IN, FD_POLL_OUT,
+ * FD_POLL_PRI). FD_POLL_HUP and FD_POLL_ERR are "sticky" in that once they
+ * are reported, they will not be cleared until the FD is closed.
*/
/* bits positions for a few flags */
#define FD_EV_SHUT_W_BIT 6
#define FD_EV_ERR_RW_BIT 7
+#define FD_POLL_IN_BIT 8
+#define FD_POLL_PRI_BIT 9
+#define FD_POLL_OUT_BIT 10
+#define FD_POLL_ERR_BIT 11
+#define FD_POLL_HUP_BIT 12
+
+
/* and flag values */
#define FD_EV_ACTIVE_R (1U << FD_EV_ACTIVE_R_BIT)
#define FD_EV_ACTIVE_W (1U << FD_EV_ACTIVE_W_BIT)
*/
#define FD_EV_ERR_RW (1U << FD_EV_ERR_RW_BIT)
+/* mask covering all use cases above */
+#define FD_EV_ANY (FD_EV_ACTIVE_RW | FD_EV_READY_RW | FD_EV_SHUT_RW | FD_EV_ERR_RW)
+
+/* polling status */
+#define FD_POLL_IN (1U << FD_POLL_IN_BIT)
+#define FD_POLL_PRI (1U << FD_POLL_PRI_BIT)
+#define FD_POLL_OUT (1U << FD_POLL_OUT_BIT)
+#define FD_POLL_ERR (1U << FD_POLL_ERR_BIT)
+#define FD_POLL_HUP (1U << FD_POLL_HUP_BIT)
+#define FD_POLL_UPDT_MASK (FD_POLL_IN | FD_POLL_PRI | FD_POLL_OUT)
+#define FD_POLL_ANY_MASK (FD_POLL_IN | FD_POLL_PRI | FD_POLL_OUT | FD_POLL_ERR | FD_POLL_HUP)
+
/* This is the value used to mark a file descriptor as dead. This value is
* negative, this is important so that tests on fd < 0 properly match. It
struct fdlist_entry update; /* Entry in the global update list */
void (*iocb)(int fd); /* I/O handler */
void *owner; /* the connection or listener associated with this fd, NULL if closed */
- unsigned char state; /* FD state for read and write directions (FD_EV_*) */
- unsigned int ev; /* event seen in return of poll() : FD_POLL_* */
+ unsigned int state; /* FD state for read and write directions (FD_EV_*) + FD_POLL_* */
unsigned char linger_risk:1; /* 1 if we must kill lingering before closing */
unsigned char cloned:1; /* 1 if a cloned socket, requires EPOLL_CTL_DEL on close */
unsigned char initialized:1; /* 1 if init phase was done on this fd (e.g. set non-blocking) */
((evts & FD_EV_ERR_RW) ? FD_POLL_ERR : 0);
/* SHUTW reported while FD was active for writes is an error */
- if ((fdtab[fd].ev & FD_EV_ACTIVE_W) && (evts & FD_EV_SHUT_W))
+ if ((fdtab[fd].state & FD_EV_ACTIVE_W) && (evts & FD_EV_SHUT_W))
new_flags |= FD_POLL_ERR;
/* compute the inactive events reported late that must be stopped */
must_stop = FD_POLL_OUT;
}
- old = fdtab[fd].ev;
+ old = fdtab[fd].state;
new = (old & ~FD_POLL_UPDT_MASK) | new_flags;
if (unlikely(locked)) {
/* Locked FDs (those with more than 2 threads) are atomically updated */
- while (unlikely(new != old && !_HA_ATOMIC_CAS(&fdtab[fd].ev, &old, new)))
+ while (unlikely(new != old && !_HA_ATOMIC_CAS(&fdtab[fd].state, &old, new)))
new = (old & ~FD_POLL_UPDT_MASK) | new_flags;
} else {
if (new != old)
- fdtab[fd].ev = new;
+ fdtab[fd].state = new;
}
- if (fdtab[fd].ev & (FD_POLL_IN | FD_POLL_HUP | FD_POLL_ERR))
+ if (fdtab[fd].state & (FD_POLL_IN | FD_POLL_HUP | FD_POLL_ERR))
fd_may_recv(fd);
- if (fdtab[fd].ev & (FD_POLL_OUT | FD_POLL_ERR))
+ if (fdtab[fd].state & (FD_POLL_OUT | FD_POLL_ERR))
fd_may_send(fd);
if (fdtab[fd].iocb && fd_active(fd)) {
fdtab[fd].owner = owner;
fdtab[fd].iocb = iocb;
- fdtab[fd].ev = 0;
+ fdtab[fd].state = 0;
fdtab[fd].linger_risk = 0;
fdtab[fd].cloned = 0;
fdtab[fd].et_possible = 0;
suspicious = 1;
chunk_printf(&trash,
- " %5d : st=0x%02x(R:%c%c W:%c%c) ev=0x%02x(%c%c%c%c%c) [%c%c] tmask=0x%lx umask=0x%lx owner=%p iocb=%p(",
+ " %5d : st=0x%04x(R:%c%c W:%c%c %c%c%c%c%c) [%c%c] tmask=0x%lx umask=0x%lx owner=%p iocb=%p(",
fd,
fdt.state,
(fdt.state & FD_EV_READY_R) ? 'R' : 'r',
(fdt.state & FD_EV_ACTIVE_R) ? 'A' : 'a',
(fdt.state & FD_EV_READY_W) ? 'R' : 'r',
(fdt.state & FD_EV_ACTIVE_W) ? 'A' : 'a',
- fdt.ev >> 8,
- (fdt.ev & FD_POLL_HUP) ? 'H' : 'h',
- (fdt.ev & FD_POLL_ERR) ? 'E' : 'e',
- (fdt.ev & FD_POLL_OUT) ? 'O' : 'o',
- (fdt.ev & FD_POLL_PRI) ? 'P' : 'p',
- (fdt.ev & FD_POLL_IN) ? 'I' : 'i',
+ (fdt.state & FD_POLL_HUP) ? 'H' : 'h',
+ (fdt.state & FD_POLL_ERR) ? 'E' : 'e',
+ (fdt.state & FD_POLL_OUT) ? 'O' : 'o',
+ (fdt.state & FD_POLL_PRI) ? 'P' : 'p',
+ (fdt.state & FD_POLL_IN) ? 'I' : 'i',
fdt.linger_risk ? 'L' : 'l',
fdt.cloned ? 'C' : 'c',
fdt.thread_mask, fdt.update_mask,
/* no need to go further if we can't retrieve the nameserver */
if ((ns = dgram->owner) == NULL) {
- _HA_ATOMIC_AND(&fdtab[fd].ev, ~(FD_POLL_HUP|FD_POLL_ERR));
+ _HA_ATOMIC_AND(&fdtab[fd].state, ~(FD_POLL_HUP|FD_POLL_ERR));
fd_stop_recv(fd);
return;
}
/* no need to go further if we can't retrieve the nameserver */
if ((ns = dgram->owner) == NULL) {
- _HA_ATOMIC_AND(&fdtab[fd].ev, ~(FD_POLL_HUP|FD_POLL_ERR));
+ _HA_ATOMIC_AND(&fdtab[fd].state, ~(FD_POLL_HUP|FD_POLL_ERR));
fd_stop_send(fd);
return;
}
if(!l)
ABORT_NOW();
- if (fdtab[fd].ev & FD_POLL_IN) {
+ if (fdtab[fd].state & FD_POLL_IN) {
if (!fd_recv_ready(fd))
return;
switch (errno) {
case EAGAIN:
ret = CO_AC_DONE; /* nothing more to accept */
- if (fdtab[l->rx.fd].ev & (FD_POLL_HUP|FD_POLL_ERR)) {
+ if (fdtab[l->rx.fd].state & (FD_POLL_HUP|FD_POLL_ERR)) {
/* the listening socket might have been disabled in a shared
* process and we're a collateral victim. We'll just pause for
* a while in case it comes back. In the mean time, we need to
* clear this sticky flag.
*/
- _HA_ATOMIC_AND(&fdtab[l->rx.fd].ev, ~(FD_POLL_HUP|FD_POLL_ERR));
+ _HA_ATOMIC_AND(&fdtab[l->rx.fd].state, ~(FD_POLL_HUP|FD_POLL_ERR));
ret = CO_AC_PAUSE;
}
fd_cant_recv(l->rx.fd);
if (!l)
ABORT_NOW();
- if (!(fdtab[fd].ev & FD_POLL_IN) || !fd_recv_ready(fd))
+ if (!(fdtab[fd].state & FD_POLL_IN) || !fd_recv_ready(fd))
return;
buf = get_trash_chunk();
* Since older splice() implementations were buggy and returned
* EAGAIN on end of read, let's bypass the call to splice() now.
*/
- if (unlikely(!(fdtab[conn->handle.fd].ev & FD_POLL_IN))) {
+ if (unlikely(!(fdtab[conn->handle.fd].state & FD_POLL_IN))) {
/* stop here if we reached the end of data */
- if ((fdtab[conn->handle.fd].ev & (FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_HUP)
+ if ((fdtab[conn->handle.fd].state & (FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_HUP)
goto out_read0;
/* report error on POLL_ERR before connection establishment */
- if ((fdtab[conn->handle.fd].ev & FD_POLL_ERR) && (conn->flags & CO_FL_WAIT_L4_CONN)) {
+ if ((fdtab[conn->handle.fd].state & FD_POLL_ERR) && (conn->flags & CO_FL_WAIT_L4_CONN)) {
conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH;
errno = 0; /* let the caller do a getsockopt() if it wants it */
goto leave;
conn->flags &= ~CO_FL_WAIT_ROOM;
errno = 0;
- if (unlikely(!(fdtab[conn->handle.fd].ev & FD_POLL_IN))) {
+ if (unlikely(!(fdtab[conn->handle.fd].state & FD_POLL_IN))) {
/* stop here if we reached the end of data */
- if ((fdtab[conn->handle.fd].ev & (FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_HUP)
+ if ((fdtab[conn->handle.fd].state & (FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_HUP)
goto read0;
/* report error on POLL_ERR before connection establishment */
- if ((fdtab[conn->handle.fd].ev & FD_POLL_ERR) && (conn->flags & CO_FL_WAIT_L4_CONN)) {
+ if ((fdtab[conn->handle.fd].state & FD_POLL_ERR) && (conn->flags & CO_FL_WAIT_L4_CONN)) {
conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH;
goto leave;
}
* to read an unlikely close from the client since we'll
* close first anyway.
*/
- if (fdtab[conn->handle.fd].ev & FD_POLL_HUP)
+ if (fdtab[conn->handle.fd].state & FD_POLL_HUP)
goto read0;
if ((!fdtab[conn->handle.fd].linger_risk) ||
* of recv()'s return value 0, so we have no way to tell there was
* an error without checking.
*/
- if (unlikely(fdtab[conn->handle.fd].ev & FD_POLL_ERR))
+ if (unlikely(fdtab[conn->handle.fd].state & FD_POLL_ERR))
conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH;
goto leave;
}
switch (errno) {
case EAGAIN:
ret = CO_AC_DONE; /* nothing more to accept */
- if (fdtab[l->rx.fd].ev & (FD_POLL_HUP|FD_POLL_ERR)) {
+ if (fdtab[l->rx.fd].state & (FD_POLL_HUP|FD_POLL_ERR)) {
/* the listening socket might have been disabled in a shared
* process and we're a collateral victim. We'll just pause for
* a while in case it comes back. In the mean time, we need to
* clear this sticky flag.
*/
- _HA_ATOMIC_AND(&fdtab[l->rx.fd].ev, ~(FD_POLL_HUP|FD_POLL_ERR));
+ _HA_ATOMIC_AND(&fdtab[l->rx.fd].state, ~(FD_POLL_HUP|FD_POLL_ERR));
ret = CO_AC_PAUSE;
}
fd_cant_recv(l->rx.fd);
*/
if (cur_poller.flags & HAP_POLL_F_ERRHUP) {
/* modern poller, able to report ERR/HUP */
- if ((fdtab[fd].ev & (FD_POLL_IN|FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_IN)
+ if ((fdtab[fd].state & (FD_POLL_IN|FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_IN)
goto done;
- if ((fdtab[fd].ev & (FD_POLL_OUT|FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_OUT)
+ if ((fdtab[fd].state & (FD_POLL_OUT|FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_OUT)
goto done;
- if (!(fdtab[fd].ev & (FD_POLL_ERR|FD_POLL_HUP)))
+ if (!(fdtab[fd].state & (FD_POLL_ERR|FD_POLL_HUP)))
goto wait;
/* error present, fall through common error check path */
}
int fd = conn->handle.fd;
int len;
- if (fdtab[fd].ev & (FD_POLL_ERR|FD_POLL_HUP))
+ if (fdtab[fd].state & (FD_POLL_ERR|FD_POLL_HUP))
goto shut;
if (!fd_recv_ready(fd))
conn->flags &= ~CO_FL_WAIT_ROOM;
errno = 0;
- if (unlikely(!(fdtab[conn->handle.fd].ev & FD_POLL_IN))) {
+ if (unlikely(!(fdtab[conn->handle.fd].state & FD_POLL_IN))) {
/* stop here if we reached the end of data */
- if ((fdtab[conn->handle.fd].ev & (FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_HUP)
+ if ((fdtab[conn->handle.fd].state & (FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_HUP)
goto read0;
/* report error on POLL_ERR before connection establishment */
- if ((fdtab[conn->handle.fd].ev & FD_POLL_ERR) && (conn->flags & CO_FL_WAIT_L4_CONN)) {
+ if ((fdtab[conn->handle.fd].state & FD_POLL_ERR) && (conn->flags & CO_FL_WAIT_L4_CONN)) {
conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH;
goto leave;
}
* to read an unlikely close from the client since we'll
* close first anyway.
*/
- if (fdtab[conn->handle.fd].ev & FD_POLL_HUP)
+ if (fdtab[conn->handle.fd].state & FD_POLL_HUP)
goto read0;
if ((!fdtab[conn->handle.fd].linger_risk) ||
* of recv()'s return value 0, so we have no way to tell there was
* an error without checking.
*/
- if (unlikely(fdtab[conn->handle.fd].ev & FD_POLL_ERR))
+ if (unlikely(fdtab[conn->handle.fd].state & FD_POLL_ERR))
conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH;
goto leave;
}
*/
void quic_fd_handler(int fd)
{
- if (fdtab[fd].ev & FD_POLL_IN)
+ if (fdtab[fd].state & FD_POLL_IN)
quic_conn_handler(fd, fdtab[fd].owner, &qc_lstnr_pkt_rcv);
}
*/
void quic_conn_fd_handler(int fd)
{
- if (fdtab[fd].ev & FD_POLL_IN)
+ if (fdtab[fd].state & FD_POLL_IN)
quic_conn_handler(fd, fdtab[fd].owner, &qc_srv_pkt_rcv);
}