diff --git a/include/proto/connection.h b/include/proto/connection.h index 9ee9e9abb..6bef475ce 100644 --- a/include/proto/connection.h +++ b/include/proto/connection.h @@ -37,6 +37,241 @@ static inline void conn_data_close(struct connection *conn) conn->data->close(conn); } +/* set polling depending on the change between the CURR part of the + * flags and the new flags in connection C. The connection flags are + * updated with the new flags at the end of the operation. Only the bits + * relevant to CO_FL_CURR_* from are considered. + */ +void conn_set_polling(struct connection *c, unsigned int new); + +/* update polling depending on the change between the CURR part of the + * flags and the DATA part of the flags in connection C. The connection + * is assumed to already be in the data phase. + */ +static inline void conn_update_data_polling(struct connection *c) +{ + conn_set_polling(c, c->flags << 8); +} + +/* update polling depending on the change between the CURR part of the + * flags and the SOCK part of the flags in connection C. The connection + * is assumed to already be in the handshake phase. + */ +static inline void conn_update_sock_polling(struct connection *c) +{ + conn_set_polling(c, c->flags << 4); +} + +/* returns non-zero if data flags from c->flags changes from what is in the + * current section of c->flags. + */ +static inline unsigned int conn_data_polling_changes(const struct connection *c) +{ + return ((c->flags << 8) ^ c->flags) & 0xF0000000; +} + +/* returns non-zero if sock flags from c->flags changes from what is in the + * current section of c->flags. + */ +static inline unsigned int conn_sock_polling_changes(const struct connection *c) +{ + return ((c->flags << 4) ^ c->flags) & 0xF0000000; +} + +/* Automatically updates polling on connection depending on the DATA flags + * if no handshake is in progress. + */ +static inline void conn_cond_update_data_polling(struct connection *c) +{ + if (!(c->flags & CO_FL_POLL_SOCK) && conn_data_polling_changes(c)) + conn_update_data_polling(c); +} + +/* Automatically updates polling on connection depending on the SOCK flags + * if a handshake is in progress. + */ +static inline void conn_cond_update_sock_polling(struct connection *c) +{ + if ((c->flags & CO_FL_POLL_SOCK) && conn_sock_polling_changes(c)) + conn_update_sock_polling(c); +} + +/* Automatically update polling on connection depending on the DATA and + * SOCK flags, and on whether a handshake is in progress or not. This may be + * called at any moment when there is a doubt about the effectiveness of the + * polling state, for instance when entering or leaving the handshake state. + */ +static inline void conn_cond_update_polling(struct connection *c) +{ + if (!(c->flags & CO_FL_POLL_SOCK) && conn_data_polling_changes(c)) + conn_update_data_polling(c); + else if ((c->flags & CO_FL_POLL_SOCK) && conn_sock_polling_changes(c)) + conn_update_sock_polling(c); +} + +/***** Event manipulation primitives for use by DATA I/O callbacks *****/ +/* The __conn_* versions do not propagate to lower layers and are only meant + * to be used by handlers called by the connection handler. The other ones + * may be used anywhere. + */ +static inline void __conn_data_want_recv(struct connection *c) +{ + c->flags |= CO_FL_DATA_RD_ENA; +} + +static inline void __conn_data_stop_recv(struct connection *c) +{ + c->flags &= ~CO_FL_DATA_RD_ENA; +} + +static inline void __conn_data_poll_recv(struct connection *c) +{ + c->flags |= CO_FL_DATA_RD_POL | CO_FL_DATA_RD_ENA; +} + +static inline void __conn_data_want_send(struct connection *c) +{ + c->flags |= CO_FL_DATA_WR_ENA; +} + +static inline void __conn_data_stop_send(struct connection *c) +{ + c->flags &= ~CO_FL_DATA_WR_ENA; +} + +static inline void __conn_data_poll_send(struct connection *c) +{ + c->flags |= CO_FL_DATA_WR_POL | CO_FL_DATA_WR_ENA; +} + +static inline void __conn_data_stop_both(struct connection *c) +{ + c->flags &= ~(CO_FL_DATA_WR_ENA | CO_FL_DATA_RD_ENA); +} + +static inline void conn_data_want_recv(struct connection *c) +{ + __conn_data_want_recv(c); + conn_cond_update_data_polling(c); +} + +static inline void conn_data_stop_recv(struct connection *c) +{ + __conn_data_stop_recv(c); + conn_cond_update_data_polling(c); +} + +static inline void conn_data_poll_recv(struct connection *c) +{ + __conn_data_poll_recv(c); + conn_cond_update_data_polling(c); +} + +static inline void conn_data_want_send(struct connection *c) +{ + __conn_data_want_send(c); + conn_cond_update_data_polling(c); +} + +static inline void conn_data_stop_send(struct connection *c) +{ + __conn_data_stop_send(c); + conn_cond_update_data_polling(c); +} + +static inline void conn_data_poll_send(struct connection *c) +{ + __conn_data_poll_send(c); + conn_cond_update_data_polling(c); +} + +static inline void conn_data_stop_both(struct connection *c) +{ + __conn_data_stop_both(c); + conn_cond_update_data_polling(c); +} + +/***** Event manipulation primitives for use by handshake I/O callbacks *****/ +/* The __conn_* versions do not propagate to lower layers and are only meant + * to be used by handlers called by the connection handler. The other ones + * may be used anywhere. + */ +static inline void __conn_sock_want_recv(struct connection *c) +{ + c->flags |= CO_FL_SOCK_RD_ENA; +} + +static inline void __conn_sock_stop_recv(struct connection *c) +{ + c->flags &= ~CO_FL_SOCK_RD_ENA; +} + +static inline void __conn_sock_poll_recv(struct connection *c) +{ + c->flags |= CO_FL_SOCK_RD_POL | CO_FL_SOCK_RD_ENA; +} + +static inline void __conn_sock_want_send(struct connection *c) +{ + c->flags |= CO_FL_SOCK_WR_ENA; +} + +static inline void __conn_sock_stop_send(struct connection *c) +{ + c->flags &= ~CO_FL_SOCK_WR_ENA; +} + +static inline void __conn_sock_poll_send(struct connection *c) +{ + c->flags |= CO_FL_SOCK_WR_POL | CO_FL_SOCK_WR_ENA; +} + +static inline void __conn_sock_stop_both(struct connection *c) +{ + c->flags &= ~(CO_FL_SOCK_WR_ENA | CO_FL_SOCK_RD_ENA); +} + +static inline void conn_sock_want_recv(struct connection *c) +{ + __conn_sock_want_recv(c); + conn_cond_update_sock_polling(c); +} + +static inline void conn_sock_stop_recv(struct connection *c) +{ + __conn_sock_stop_recv(c); + conn_cond_update_sock_polling(c); +} + +static inline void conn_sock_poll_recv(struct connection *c) +{ + __conn_sock_poll_recv(c); + conn_cond_update_sock_polling(c); +} + +static inline void conn_sock_want_send(struct connection *c) +{ + __conn_sock_want_send(c); + conn_cond_update_sock_polling(c); +} + +static inline void conn_sock_stop_send(struct connection *c) +{ + __conn_sock_stop_send(c); + conn_cond_update_sock_polling(c); +} + +static inline void conn_sock_poll_send(struct connection *c) +{ + __conn_sock_poll_send(c); + conn_cond_update_sock_polling(c); +} + +static inline void conn_sock_stop_both(struct connection *c) +{ + __conn_sock_stop_both(c); + conn_cond_update_sock_polling(c); +} #endif /* _PROTO_CONNECTION_H */ diff --git a/include/types/connection.h b/include/types/connection.h index beaf6bb55..8dd4f20a3 100644 --- a/include/types/connection.h +++ b/include/types/connection.h @@ -31,6 +31,27 @@ struct sock_ops; struct protocol; +/* Polling flags that are manipulated by I/O callbacks and handshake callbacks + * indicate what they expect from a file descriptor at each layer. For each + * direction, we have 2 bits, one stating whether any suspected activity on the + * FD induce a call to the iocb, and another one indicating that the FD has + * already returned EAGAIN and that polling on it is essential before calling + * the iocb again : + * POL ENA state + * 0 0 STOPPED : any activity on this FD is ignored + * 0 1 ENABLED : any (suspected) activity may call the iocb + * 1 0 STOPPED : as above + * 1 1 POLLED : the FD is being polled for activity + * + * - Enabling an I/O event consists in ORing with 1. + * - Stopping an I/O event consists in ANDing with ~1. + * - Polling for an I/O event consists in ORing with ~3. + * + * The last computed state is remembered in CO_FL_CURR_* so that differential + * changes can be applied. For pollers that do not support speculative I/O, + * POLLED is the same as ENABLED and the POL flag can safely be ignored. + */ + /* flags for use in connection->flags */ enum { CO_FL_NONE = 0x00000000, @@ -46,6 +67,38 @@ enum { /* below we have all handshake flags grouped into one */ CO_FL_HANDSHAKE = CO_FL_SI_SEND_PROXY, + + /* when any of these flags is set, polling is defined by socket-layer + * operations, as opposed to data-layer. + */ + CO_FL_POLL_SOCK = CO_FL_HANDSHAKE | CO_FL_WAIT_L4_CONN | CO_FL_WAIT_L6_CONN, + + /* flags used to remember what shutdown have been performed/reported */ + CO_FL_DATA_RD_SH = 0x00010000, /* DATA layer was notified about shutr/read0 */ + CO_FL_DATA_WR_SH = 0x00020000, /* DATA layer asked for shutw */ + CO_FL_SOCK_RD_SH = 0x00040000, /* SOCK layer was notified about shutr/read0 */ + CO_FL_SOCK_WR_SH = 0x00080000, /* SOCK layer asked for shutw */ + + /****** NOTE: do not change the values of the flags below ******/ + CO_FL_RD_ENA = 1, CO_FL_RD_POL = 2, CO_FL_WR_ENA = 4, CO_FL_WR_POL = 8, + + /* flags describing the DATA layer expectations regarding polling */ + CO_FL_DATA_RD_ENA = CO_FL_RD_ENA << 20, /* receiving is allowed */ + CO_FL_DATA_RD_POL = CO_FL_RD_POL << 20, /* receiving needs to poll first */ + CO_FL_DATA_WR_ENA = CO_FL_WR_ENA << 20, /* sending is desired */ + CO_FL_DATA_WR_POL = CO_FL_WR_POL << 20, /* sending needs to poll first */ + + /* flags describing the SOCK layer expectations regarding polling */ + CO_FL_SOCK_RD_ENA = CO_FL_RD_ENA << 24, /* receiving is allowed */ + CO_FL_SOCK_RD_POL = CO_FL_RD_POL << 24, /* receiving needs to poll first */ + CO_FL_SOCK_WR_ENA = CO_FL_WR_ENA << 24, /* sending is desired */ + CO_FL_SOCK_WR_POL = CO_FL_WR_POL << 24, /* sending needs to poll first */ + + /* flags storing the current polling state */ + CO_FL_CURR_RD_ENA = CO_FL_RD_ENA << 28, /* receiving is allowed */ + CO_FL_CURR_RD_POL = CO_FL_RD_POL << 28, /* receiving needs to poll first */ + CO_FL_CURR_WR_ENA = CO_FL_WR_ENA << 28, /* sending is desired */ + CO_FL_CURR_WR_POL = CO_FL_WR_POL << 28, /* sending needs to poll first */ }; /* This structure describes a connection with its methods and data. diff --git a/src/connection.c b/src/connection.c index 63f143e5b..712dfbaa3 100644 --- a/src/connection.c +++ b/src/connection.c @@ -88,3 +88,34 @@ int conn_fd_handler(int fd) fdtab[fd].ev &= ~(FD_POLL_IN | FD_POLL_OUT | FD_POLL_HUP | FD_POLL_ERR); return ret; } + +/* set polling depending on the change between the CURR part of the + * flags and the new flags in connection C. The connection flags are + * updated with the new flags at the end of the operation. Only the bits + * relevant to CO_FL_CURR_* from are considered. + */ +void conn_set_polling(struct connection *c, unsigned int new) +{ + unsigned int old = c->flags; /* for CO_FL_CURR_* */ + + /* update read status if needed */ + if ((old & (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL)) != (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL) && + (new & (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL)) == (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL)) + fd_poll_recv(c->t.sock.fd); + else if (!(old & CO_FL_CURR_RD_ENA) && (new & CO_FL_CURR_RD_ENA)) + fd_want_recv(c->t.sock.fd); + else if ((old & CO_FL_CURR_RD_ENA) && !(new & CO_FL_CURR_RD_ENA)) + fd_stop_recv(c->t.sock.fd); + + /* update write status if needed */ + if ((old & (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL)) != (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL) && + (new & (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL)) == (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL)) + fd_poll_send(c->t.sock.fd); + else if (!(old & CO_FL_CURR_WR_ENA) && (new & CO_FL_CURR_WR_ENA)) + fd_want_send(c->t.sock.fd); + else if ((old & CO_FL_CURR_WR_ENA) && !(new & CO_FL_CURR_WR_ENA)) + fd_stop_send(c->t.sock.fd); + + c->flags &= ~(CO_FL_CURR_WR_POL|CO_FL_CURR_WR_ENA|CO_FL_CURR_RD_POL|CO_FL_CURR_RD_ENA); + c->flags |= new & (CO_FL_CURR_WR_POL|CO_FL_CURR_WR_ENA|CO_FL_CURR_RD_POL|CO_FL_CURR_RD_ENA); +}