mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-04-01 14:38:28 +00:00
MAJOR: connection: remove the CO_FL_CURR_*_POL flag
This is the first step of a series of changes aiming at making the polling totally event-driven. This first change consists in only remembering at the connection level whether an FD was enabled or not, regardless of the fact it was being polled or cached. From now on, an EAGAIN will always be considered as a change so that the pollers are able to manage a cache and to flush it based on such events. One of the noticeable effect is that conn_fd_handler() is called once more per session (6 instead of 5 min) but other update functions are less called. Note that the performance loss caused by this change at the moment is quite significant, around 2.5%, but the change is needed to have SSL working correctly in all situations, even when data were read from the socket and stored in the invisible cache, waiting for some room in the channel's buffer.
This commit is contained in:
parent
815f5ecffa
commit
c8dd77fddf
@ -96,49 +96,51 @@ void conn_update_data_polling(struct connection *c);
|
|||||||
int conn_local_send_proxy(struct connection *conn, unsigned int flag);
|
int conn_local_send_proxy(struct connection *conn, unsigned int flag);
|
||||||
|
|
||||||
/* inspects c->flags and returns non-zero if DATA ENA changes from the CURR ENA
|
/* inspects c->flags and returns non-zero if DATA ENA changes from the CURR ENA
|
||||||
* or if the WAIT flags set new flags that were not in CURR POL. Additionally,
|
* or if the WAIT flags are set with their respective ENA flags. Additionally,
|
||||||
* non-zero is also returned if an error was reported on the connection. This
|
* non-zero is also returned if an error was reported on the connection. This
|
||||||
* function is used quite often and is inlined. In order to proceed optimally
|
* function is used quite often and is inlined. In order to proceed optimally
|
||||||
* with very little code and CPU cycles, the bits are arranged so that a change
|
* with very little code and CPU cycles, the bits are arranged so that a change
|
||||||
* can be detected by a simple left shift, a xor, and a mask. This operation
|
* can be detected by a few left shifts, a xor, and a mask. These operations
|
||||||
* detects when POLL:DATA differs from WAIT:CURR. In order to detect the ERROR
|
* detect when W&D are both enabled for either direction, when C&D differ for
|
||||||
* flag without additional work, we remove it from the copy of the original
|
* either direction and when Error is set. The trick consists in first keeping
|
||||||
* flags (unshifted) before doing the XOR. This operation is parallelized with
|
* only the bits we're interested in, since they don't collide when shifted,
|
||||||
* the shift and does not induce additional cycles. This explains why we check
|
* and to perform the AND at the end. In practice, the compiler is able to
|
||||||
* the error bit shifted left in the mask. Last, the final operation is an AND
|
* replace the last AND with a TEST in boolean conditions. This results in
|
||||||
* which the compiler is able to replace with a TEST in boolean conditions. The
|
* checks that are done in 4-6 cycles and less than 30 bytes.
|
||||||
* result is that all these checks are done in 5-6 cycles only and less than 20
|
|
||||||
* bytes.
|
|
||||||
*/
|
*/
|
||||||
static inline unsigned int conn_data_polling_changes(const struct connection *c)
|
static inline unsigned int conn_data_polling_changes(const struct connection *c)
|
||||||
{
|
{
|
||||||
unsigned int f = c->flags << 2;
|
unsigned int f = c->flags;
|
||||||
return ((c->flags & ~(CO_FL_ERROR << 2)) ^ f) &
|
f &= CO_FL_DATA_WR_ENA | CO_FL_DATA_RD_ENA | CO_FL_CURR_WR_ENA |
|
||||||
((CO_FL_ERROR<<2)|CO_FL_WAIT_WR|CO_FL_CURR_WR_ENA|CO_FL_WAIT_RD|CO_FL_CURR_RD_ENA) &
|
CO_FL_CURR_RD_ENA | CO_FL_ERROR | CO_FL_WAIT_WR | CO_FL_WAIT_RD;
|
||||||
~(f & (CO_FL_WAIT_WR|CO_FL_WAIT_RD));
|
|
||||||
|
f = (f & (f << 2)) | /* test W & D */
|
||||||
|
((f ^ (f << 1)) & (CO_FL_CURR_WR_ENA|CO_FL_CURR_RD_ENA)); /* test C ^ D */
|
||||||
|
return f & (CO_FL_WAIT_WR | CO_FL_WAIT_RD | CO_FL_CURR_WR_ENA | CO_FL_CURR_RD_ENA | CO_FL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* inspects c->flags and returns non-zero if SOCK ENA changes from the CURR ENA
|
/* inspects c->flags and returns non-zero if SOCK ENA changes from the CURR ENA
|
||||||
* or if the WAIT flags set new flags that were not in CURR POL. Additionally,
|
* or if the WAIT flags are set with their respective ENA flags. Additionally,
|
||||||
* non-zero is also returned if an error was reported on the connection. This
|
* non-zero is also returned if an error was reported on the connection. This
|
||||||
* function is used quite often and is inlined. In order to proceed optimally
|
* function is used quite often and is inlined. In order to proceed optimally
|
||||||
* with very little code and CPU cycles, the bits are arranged so that a change
|
* with very little code and CPU cycles, the bits are arranged so that a change
|
||||||
* can be detected by a simple left shift, a xor, and a mask. This operation
|
* can be detected by a few left shifts, a xor, and a mask. These operations
|
||||||
* detects when CURR:POLL differs from SOCK:WAIT. In order to detect the ERROR
|
* detect when W&S are both enabled for either direction, when C&S differ for
|
||||||
* flag without additional work, we remove it from the copy of the original
|
* either direction and when Error is set. The trick consists in first keeping
|
||||||
* flags (unshifted) before doing the XOR. This operation is parallelized with
|
* only the bits we're interested in, since they don't collide when shifted,
|
||||||
* the shift and does not induce additional cycles. This explains why we check
|
* and to perform the AND at the end. In practice, the compiler is able to
|
||||||
* the error bit shifted left in the mask. Last, the final operation is an AND
|
* replace the last AND with a TEST in boolean conditions. This results in
|
||||||
* which the compiler is able to replace with a TEST in boolean conditions. The
|
* checks that are done in 4-6 cycles and less than 30 bytes.
|
||||||
* result is that all these checks are done in 5-6 cycles only and less than 20
|
|
||||||
* bytes.
|
|
||||||
*/
|
*/
|
||||||
static inline unsigned int conn_sock_polling_changes(const struct connection *c)
|
static inline unsigned int conn_sock_polling_changes(const struct connection *c)
|
||||||
{
|
{
|
||||||
unsigned int f = c->flags << 2;
|
unsigned int f = c->flags;
|
||||||
return ((c->flags & ~(CO_FL_ERROR << 2)) ^ f) &
|
f &= CO_FL_SOCK_WR_ENA | CO_FL_SOCK_RD_ENA | CO_FL_CURR_WR_ENA |
|
||||||
((CO_FL_ERROR<<2)|CO_FL_WAIT_WR|CO_FL_SOCK_WR_ENA|CO_FL_WAIT_RD|CO_FL_SOCK_RD_ENA) &
|
CO_FL_CURR_RD_ENA | CO_FL_ERROR | CO_FL_WAIT_WR | CO_FL_WAIT_RD;
|
||||||
~(f & (CO_FL_WAIT_WR|CO_FL_WAIT_RD));
|
|
||||||
|
f = (f & (f << 3)) | /* test W & S */
|
||||||
|
((f ^ (f << 2)) & (CO_FL_CURR_WR_ENA|CO_FL_CURR_RD_ENA)); /* test C ^ S */
|
||||||
|
return f & (CO_FL_WAIT_WR | CO_FL_WAIT_RD | CO_FL_CURR_WR_ENA | CO_FL_CURR_RD_ENA | CO_FL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Automatically updates polling on connection <c> depending on the DATA flags
|
/* Automatically updates polling on connection <c> depending on the DATA flags
|
||||||
|
@ -55,29 +55,18 @@ struct task;
|
|||||||
* - Stopping an I/O event consists in ANDing with ~1.
|
* - Stopping an I/O event consists in ANDing with ~1.
|
||||||
* - Polling for an I/O event consists in ORing with ~3.
|
* - Polling for an I/O event consists in ORing with ~3.
|
||||||
*
|
*
|
||||||
* The last computed state is remembered in CO_FL_CURR_* so that differential
|
* The last ENA state is remembered in CO_FL_CURR_* so that differential
|
||||||
* changes can be applied. After bits are applied, the POLL status bits are
|
* changes can be applied. After bits are applied, the POLL status bits are
|
||||||
* cleared so that it is possible to detect when an EAGAIN was encountered. For
|
* cleared so that it is possible to detect when an EAGAIN was encountered. For
|
||||||
* pollers that do not support speculative I/O, POLLED is the same as ENABLED
|
* pollers that do not support speculative I/O, POLLED is the same as ENABLED
|
||||||
* and the POL flag can safely be ignored. However it makes a difference for
|
* and the POL flag can safely be ignored. However it makes a difference for
|
||||||
* the connection handler.
|
* the connection handler.
|
||||||
*
|
*
|
||||||
* The ENA flags are per-layer (one pair for SOCK, another one for DATA).
|
* The ENA flags are per-layer (one pair for SOCK, another one for DATA). The
|
||||||
* The POL flags are only for the socket layer since they indicate that EAGAIN
|
* POL flags are irrelevant to these layers and only reflect the fact that
|
||||||
* was encountered. Thus, the DATA layer uses its own ENA flag and the socket
|
* EAGAIN was encountered, they're materialised by the CO_FL_WAIT_* connection
|
||||||
* layer's POL flag.
|
* flags. POL flags always indicate a polling change because it is assumed that
|
||||||
*
|
* the poller uses a cache and does not always poll.
|
||||||
* The bits are arranged so that it is possible to detect a change by performing
|
|
||||||
* only a left shift followed by a xor and applying a mask to the result. The
|
|
||||||
* principle is that depending on what we want to check (data polling changes or
|
|
||||||
* sock polling changes), we mask different bits. The bits are arranged this way :
|
|
||||||
*
|
|
||||||
* S(ock) - W(ait) - C(urr) - P(oll) - D(ata)
|
|
||||||
*
|
|
||||||
* SOCK changes are reported when (S != C) || (W != P) => (S:W) != (C:P)
|
|
||||||
* DATA changes are reported when (D != C) || (W != P) => (W:C) != (P:D)
|
|
||||||
* The R and W bits are split apart so that we never shift more than 2 bits at
|
|
||||||
* a time, allowing move+shift to be done as a single operation on x86.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* flags for use in connection->flags */
|
/* flags for use in connection->flags */
|
||||||
@ -85,16 +74,15 @@ enum {
|
|||||||
CO_FL_NONE = 0x00000000, /* Just for initialization purposes */
|
CO_FL_NONE = 0x00000000, /* Just for initialization purposes */
|
||||||
|
|
||||||
/* Do not change these values without updating conn_*_poll_changes() ! */
|
/* Do not change these values without updating conn_*_poll_changes() ! */
|
||||||
CO_FL_DATA_RD_ENA = 0x00000001, /* receiving data is allowed */
|
CO_FL_SOCK_RD_ENA = 0x00000001, /* receiving handshakes is allowed */
|
||||||
CO_FL_CURR_RD_POL = 0x00000002, /* receiving needs to poll first */
|
CO_FL_DATA_RD_ENA = 0x00000002, /* receiving data is allowed */
|
||||||
CO_FL_CURR_RD_ENA = 0x00000004, /* receiving is currently allowed */
|
CO_FL_CURR_RD_ENA = 0x00000004, /* receiving is currently allowed */
|
||||||
CO_FL_WAIT_RD = 0x00000008, /* receiving needs to poll first */
|
CO_FL_WAIT_RD = 0x00000008, /* receiving needs to poll first */
|
||||||
CO_FL_SOCK_RD_ENA = 0x00000010, /* receiving handshakes is allowed */
|
|
||||||
|
CO_FL_SOCK_WR_ENA = 0x00000010, /* sending handshakes is desired */
|
||||||
CO_FL_DATA_WR_ENA = 0x00000020, /* sending data is desired */
|
CO_FL_DATA_WR_ENA = 0x00000020, /* sending data is desired */
|
||||||
CO_FL_CURR_WR_POL = 0x00000040, /* sending needs to poll first */
|
CO_FL_CURR_WR_ENA = 0x00000040, /* sending is currently desired */
|
||||||
CO_FL_CURR_WR_ENA = 0x00000080, /* sending is currently desired */
|
CO_FL_WAIT_WR = 0x00000080, /* sending needs to poll first */
|
||||||
CO_FL_WAIT_WR = 0x00000100, /* sending needs to poll first */
|
|
||||||
CO_FL_SOCK_WR_ENA = 0x00000200, /* sending handshakes is desired */
|
|
||||||
|
|
||||||
/* These flags are used by data layers to indicate they had to stop
|
/* These flags are used by data layers to indicate they had to stop
|
||||||
* sending data because a buffer was empty (WAIT_DATA) or stop receiving
|
* sending data because a buffer was empty (WAIT_DATA) or stop receiving
|
||||||
|
@ -175,33 +175,31 @@ void conn_update_data_polling(struct connection *c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* update read status if needed */
|
/* update read status if needed */
|
||||||
if (unlikely((f & (CO_FL_CURR_RD_ENA|CO_FL_DATA_RD_ENA)) == CO_FL_CURR_RD_ENA)) {
|
if (unlikely((f & (CO_FL_DATA_RD_ENA|CO_FL_WAIT_RD)) == (CO_FL_DATA_RD_ENA|CO_FL_WAIT_RD))) {
|
||||||
f &= ~(CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL);
|
|
||||||
fd_stop_recv(c->t.sock.fd);
|
|
||||||
}
|
|
||||||
else if (unlikely((f & (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL)) != (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL) &&
|
|
||||||
(f & (CO_FL_DATA_RD_ENA|CO_FL_WAIT_RD)) == (CO_FL_DATA_RD_ENA|CO_FL_WAIT_RD))) {
|
|
||||||
f |= (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL);
|
|
||||||
fd_poll_recv(c->t.sock.fd);
|
fd_poll_recv(c->t.sock.fd);
|
||||||
|
f |= CO_FL_CURR_RD_ENA;
|
||||||
}
|
}
|
||||||
else if (unlikely((f & (CO_FL_CURR_RD_ENA|CO_FL_DATA_RD_ENA)) == CO_FL_DATA_RD_ENA)) {
|
else if (unlikely((f & (CO_FL_CURR_RD_ENA|CO_FL_DATA_RD_ENA)) == CO_FL_DATA_RD_ENA)) {
|
||||||
f |= CO_FL_CURR_RD_ENA;
|
|
||||||
fd_want_recv(c->t.sock.fd);
|
fd_want_recv(c->t.sock.fd);
|
||||||
|
f |= CO_FL_CURR_RD_ENA;
|
||||||
|
}
|
||||||
|
else if (unlikely((f & (CO_FL_CURR_RD_ENA|CO_FL_DATA_RD_ENA)) == CO_FL_CURR_RD_ENA)) {
|
||||||
|
fd_stop_recv(c->t.sock.fd);
|
||||||
|
f &= ~CO_FL_CURR_RD_ENA;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update write status if needed */
|
/* update write status if needed */
|
||||||
if (unlikely((f & (CO_FL_CURR_WR_ENA|CO_FL_DATA_WR_ENA)) == CO_FL_CURR_WR_ENA)) {
|
if (unlikely((f & (CO_FL_DATA_WR_ENA|CO_FL_WAIT_WR)) == (CO_FL_DATA_WR_ENA|CO_FL_WAIT_WR))) {
|
||||||
f &= ~(CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL);
|
|
||||||
fd_stop_send(c->t.sock.fd);
|
|
||||||
}
|
|
||||||
else if (unlikely((f & (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL)) != (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL) &&
|
|
||||||
(f & (CO_FL_DATA_WR_ENA|CO_FL_WAIT_WR)) == (CO_FL_DATA_WR_ENA|CO_FL_WAIT_WR))) {
|
|
||||||
f |= (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL);
|
|
||||||
fd_poll_send(c->t.sock.fd);
|
fd_poll_send(c->t.sock.fd);
|
||||||
|
f |= CO_FL_CURR_WR_ENA;
|
||||||
}
|
}
|
||||||
else if (unlikely((f & (CO_FL_CURR_WR_ENA|CO_FL_DATA_WR_ENA)) == CO_FL_DATA_WR_ENA)) {
|
else if (unlikely((f & (CO_FL_CURR_WR_ENA|CO_FL_DATA_WR_ENA)) == CO_FL_DATA_WR_ENA)) {
|
||||||
f |= CO_FL_CURR_WR_ENA;
|
|
||||||
fd_want_send(c->t.sock.fd);
|
fd_want_send(c->t.sock.fd);
|
||||||
|
f |= CO_FL_CURR_WR_ENA;
|
||||||
|
}
|
||||||
|
else if (unlikely((f & (CO_FL_CURR_WR_ENA|CO_FL_DATA_WR_ENA)) == CO_FL_CURR_WR_ENA)) {
|
||||||
|
fd_stop_send(c->t.sock.fd);
|
||||||
|
f &= ~CO_FL_CURR_WR_ENA;
|
||||||
}
|
}
|
||||||
c->flags = f;
|
c->flags = f;
|
||||||
}
|
}
|
||||||
@ -225,33 +223,31 @@ void conn_update_sock_polling(struct connection *c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* update read status if needed */
|
/* update read status if needed */
|
||||||
if (unlikely((f & (CO_FL_CURR_RD_ENA|CO_FL_SOCK_RD_ENA)) == CO_FL_CURR_RD_ENA)) {
|
if (unlikely((f & (CO_FL_SOCK_RD_ENA|CO_FL_WAIT_RD)) == (CO_FL_SOCK_RD_ENA|CO_FL_WAIT_RD))) {
|
||||||
f &= ~(CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL);
|
|
||||||
fd_stop_recv(c->t.sock.fd);
|
|
||||||
}
|
|
||||||
else if (unlikely((f & (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL)) != (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL) &&
|
|
||||||
(f & (CO_FL_SOCK_RD_ENA|CO_FL_WAIT_RD)) == (CO_FL_SOCK_RD_ENA|CO_FL_WAIT_RD))) {
|
|
||||||
f |= (CO_FL_CURR_RD_ENA|CO_FL_CURR_RD_POL);
|
|
||||||
fd_poll_recv(c->t.sock.fd);
|
fd_poll_recv(c->t.sock.fd);
|
||||||
|
f |= CO_FL_CURR_RD_ENA;
|
||||||
}
|
}
|
||||||
else if (unlikely((f & (CO_FL_CURR_RD_ENA|CO_FL_SOCK_RD_ENA)) == CO_FL_SOCK_RD_ENA)) {
|
else if (unlikely((f & (CO_FL_CURR_RD_ENA|CO_FL_SOCK_RD_ENA)) == CO_FL_SOCK_RD_ENA)) {
|
||||||
f |= CO_FL_CURR_RD_ENA;
|
|
||||||
fd_want_recv(c->t.sock.fd);
|
fd_want_recv(c->t.sock.fd);
|
||||||
|
f |= CO_FL_CURR_RD_ENA;
|
||||||
|
}
|
||||||
|
else if (unlikely((f & (CO_FL_CURR_RD_ENA|CO_FL_SOCK_RD_ENA)) == CO_FL_CURR_RD_ENA)) {
|
||||||
|
fd_stop_recv(c->t.sock.fd);
|
||||||
|
f &= ~CO_FL_CURR_RD_ENA;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update write status if needed */
|
/* update write status if needed */
|
||||||
if (unlikely((f & (CO_FL_CURR_WR_ENA|CO_FL_SOCK_WR_ENA)) == CO_FL_CURR_WR_ENA)) {
|
if (unlikely((f & (CO_FL_SOCK_WR_ENA|CO_FL_WAIT_WR)) == (CO_FL_SOCK_WR_ENA|CO_FL_WAIT_WR))) {
|
||||||
f &= ~(CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL);
|
|
||||||
fd_stop_send(c->t.sock.fd);
|
|
||||||
}
|
|
||||||
else if (unlikely((f & (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL)) != (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL) &&
|
|
||||||
(f & (CO_FL_SOCK_WR_ENA|CO_FL_WAIT_WR)) == (CO_FL_SOCK_WR_ENA|CO_FL_WAIT_WR))) {
|
|
||||||
f |= (CO_FL_CURR_WR_ENA|CO_FL_CURR_WR_POL);
|
|
||||||
fd_poll_send(c->t.sock.fd);
|
fd_poll_send(c->t.sock.fd);
|
||||||
|
f |= CO_FL_CURR_WR_ENA;
|
||||||
}
|
}
|
||||||
else if (unlikely((f & (CO_FL_CURR_WR_ENA|CO_FL_SOCK_WR_ENA)) == CO_FL_SOCK_WR_ENA)) {
|
else if (unlikely((f & (CO_FL_CURR_WR_ENA|CO_FL_SOCK_WR_ENA)) == CO_FL_SOCK_WR_ENA)) {
|
||||||
f |= CO_FL_CURR_WR_ENA;
|
|
||||||
fd_want_send(c->t.sock.fd);
|
fd_want_send(c->t.sock.fd);
|
||||||
|
f |= CO_FL_CURR_WR_ENA;
|
||||||
|
}
|
||||||
|
else if (unlikely((f & (CO_FL_CURR_WR_ENA|CO_FL_SOCK_WR_ENA)) == CO_FL_CURR_WR_ENA)) {
|
||||||
|
fd_stop_send(c->t.sock.fd);
|
||||||
|
f &= ~CO_FL_CURR_WR_ENA;
|
||||||
}
|
}
|
||||||
c->flags = f;
|
c->flags = f;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user