From 71320fc9c1a17ac0d2c64a88af0b4e438ecfe35e Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Thu, 30 Jan 2025 11:31:59 +0100 Subject: [PATCH] MINOR: tevt/connection: Add support for POLL_HUP/POLL_ERR events Connection errors can be detected via connect/recv/send syscall, but also because it was reported by the poller. So dedicated events, at the FD level, are introduced to make the difference. term_events tool was updated accordingly. --- dev/term_events/term_events.c | 8 ++++---- include/haproxy/connection-t.h | 4 ++++ src/raw_sock.c | 28 +++++++++++++++++----------- src/sock.c | 9 ++++++--- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/dev/term_events/term_events.c b/dev/term_events/term_events.c index 243850094..4e9b8d0ed 100644 --- a/dev/term_events/term_events.c +++ b/dev/term_events/term_events.c @@ -23,10 +23,10 @@ static const char *tevt_unknown_types[16] = { }; static const char *tevt_fd_types[16] = { - [ 0] = "-", [ 1] = "shutw", [ 2] = "shutr", [ 3] = "rcv_err", - [ 4] = "snd_err", [ 5] = "-", [ 6] = "-", [ 7] = "conn_err", - [ 8] = "intercepted", [ 9] = "-", [10] = "-", [11] = "-", - [12] = "-", [13] = "-", [14] = "-", [15] = "-", + [ 0] = "-", [ 1] = "shutw", [ 2] = "shutr", [ 3] = "rcv_err", + [ 4] = "snd_err", [ 5] = "-", [ 6] = "-", [ 7] = "conn_err", + [ 8] = "intercepted", [ 9] = "conn_poll_err", [10] = "poll_err", [11] = "poll_hup", + [12] = "-", [13] = "-", [14] = "-", [15] = "-", }; static const char *tevt_hs_types[16] = { diff --git a/include/haproxy/connection-t.h b/include/haproxy/connection-t.h index 05561d20a..98f6cc471 100644 --- a/include/haproxy/connection-t.h +++ b/include/haproxy/connection-t.h @@ -763,6 +763,10 @@ enum fd_term_event_type { /* unused: 5, 6 */ fd_tevt_type_connect_err = 7, fd_tevt_type_intercepted = 8, + + fd_tevt_type_connect_poll_err = 9, + fd_tevt_type_poll_err = 10, + fd_tevt_type_poll_hup = 11, }; enum hs_term_event_type { diff --git a/src/raw_sock.c b/src/raw_sock.c index 255a8c01a..9264e3130 100644 --- a/src/raw_sock.c +++ b/src/raw_sock.c @@ -72,12 +72,14 @@ int raw_sock_to_pipe(struct connection *conn, void *xprt_ctx, struct pipe *pipe, */ if (unlikely(!(fdtab[conn->handle.fd].state & FD_POLL_IN))) { /* stop here if we reached the end of data */ - if ((fdtab[conn->handle.fd].state & (FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_HUP) + if ((fdtab[conn->handle.fd].state & (FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_HUP) { + conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_poll_hup); goto out_read0; + } /* report error on POLL_ERR before connection establishment */ if ((fdtab[conn->handle.fd].state & FD_POLL_ERR) && (conn->flags & CO_FL_WAIT_L4_CONN)) { - conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_rcv_err); + conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_poll_err); conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH; conn_set_errcode(conn, CO_ER_POLLERR); errno = 0; /* let the caller do a getsockopt() if it wants it */ @@ -93,8 +95,10 @@ int raw_sock_to_pipe(struct connection *conn, void *xprt_ctx, struct pipe *pipe, SPLICE_F_MOVE|SPLICE_F_NONBLOCK); if (ret <= 0) { - if (ret == 0) + if (ret == 0) { + conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_shutr); goto out_read0; + } if (errno == EAGAIN || errno == EWOULDBLOCK) { /* there are two reasons for EAGAIN : @@ -157,7 +161,6 @@ int raw_sock_to_pipe(struct connection *conn, void *xprt_ctx, struct pipe *pipe, return retval; out_read0: - conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_shutr); conn_sock_read0(conn); conn->flags &= ~CO_FL_WAIT_L4_CONN; goto leave; @@ -179,7 +182,6 @@ int raw_sock_from_pipe(struct connection *conn, void *xprt_ctx, struct pipe *pip if (conn->flags & CO_FL_SOCK_WR_SH) { /* it's already closed */ - conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_snd_err); conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH; errno = EPIPE; conn_set_errno(conn, errno); @@ -251,12 +253,14 @@ static size_t raw_sock_to_buf(struct connection *conn, void *xprt_ctx, struct bu if (unlikely(!(fdtab[conn->handle.fd].state & FD_POLL_IN))) { /* stop here if we reached the end of data */ - if ((fdtab[conn->handle.fd].state & (FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_HUP) + if ((fdtab[conn->handle.fd].state & (FD_POLL_ERR|FD_POLL_HUP)) == FD_POLL_HUP) { + conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_poll_hup); goto read0; + } /* report error on POLL_ERR before connection establishment */ if ((fdtab[conn->handle.fd].state & FD_POLL_ERR) && (conn->flags & CO_FL_WAIT_L4_CONN)) { - conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_rcv_err); + conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_connect_poll_err); conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH; conn_set_errcode(conn, CO_ER_POLLERR); goto leave; @@ -294,8 +298,10 @@ static size_t raw_sock_to_buf(struct connection *conn, void *xprt_ctx, struct bu * to read an unlikely close from the client since we'll * close first anyway. */ - if (fdtab[conn->handle.fd].state & FD_POLL_HUP) + if (fdtab[conn->handle.fd].state & FD_POLL_HUP) { + conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_poll_hup); goto read0; + } if (!(fdtab[conn->handle.fd].state & FD_LINGER_RISK) || (cur_poller.flags & HAP_POLL_F_RDHUP)) { @@ -308,6 +314,7 @@ static size_t raw_sock_to_buf(struct connection *conn, void *xprt_ctx, struct bu break; } else if (ret == 0) { + conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_shutr); goto read0; } else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOTCONN) { @@ -330,7 +337,6 @@ static size_t raw_sock_to_buf(struct connection *conn, void *xprt_ctx, struct bu return done; read0: - conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_shutr); conn_sock_read0(conn); conn->flags &= ~CO_FL_WAIT_L4_CONN; @@ -342,7 +348,7 @@ static size_t raw_sock_to_buf(struct connection *conn, void *xprt_ctx, struct bu * an error without checking. */ if (unlikely(!done && fdtab[conn->handle.fd].state & FD_POLL_ERR)) { - conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_rcv_err); + conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_poll_err); conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH; conn_set_errcode(conn, CO_ER_POLLERR); } @@ -377,7 +383,7 @@ static size_t raw_sock_from_buf(struct connection *conn, void *xprt_ctx, const s if (unlikely(fdtab[conn->handle.fd].state & FD_POLL_ERR)) { /* an error was reported on the FD, we can't send anymore */ - conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_snd_err); + conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_poll_err); conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH; conn_set_errcode(conn, CO_ER_POLLERR); errno = EPIPE; diff --git a/src/sock.c b/src/sock.c index 160016e65..feb052691 100644 --- a/src/sock.c +++ b/src/sock.c @@ -988,8 +988,10 @@ int sock_conn_check(struct connection *conn) if (errno == EALREADY || errno == EINPROGRESS) goto wait; - if (errno && errno != EISCONN) + if (errno && errno != EISCONN) { + conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_connect_err); goto out_error; + } } done: @@ -1007,7 +1009,6 @@ int sock_conn_check(struct connection *conn) /* Write error on the file descriptor. Report it to the connection * and disable polling on this FD. */ - conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_connect_err); conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH; HA_ATOMIC_AND(&fdtab[fd].state, ~FD_LINGER_RISK); fd_stop_both(fd); @@ -1018,8 +1019,10 @@ int sock_conn_check(struct connection *conn) * in some corner cases while the system disagrees and reports an error * on the FD. */ - if (fdtab[fd].state & FD_POLL_ERR) + if (fdtab[fd].state & FD_POLL_ERR) { + conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_connect_poll_err); goto out_error; + } fd_cant_send(fd); fd_want_send(fd);