From 3ee1fe1608cca9f000bed59ba987c864adf37cd6 Mon Sep 17 00:00:00 2001 From: Dave Wallace Date: Fri, 23 Feb 2018 01:09:11 -0500 Subject: [PATCH] LDP: Refactor epoll_ctl wrapper. - Add AF_UNIX transaction to sock_test_client/server echo test to verify mixed epoll ldp implementation. - Misc cleanup / refactoring of ldp code. - Fix LDP_DEBUG in test/socket_test.sh Change-Id: Ib524c824728f109007d8c4d07d74603b6c687902 Signed-off-by: Dave Wallace --- src/vcl/ldp.c | 125 ++++++++++++++++++++++------------- src/vcl/sock_test.h | 12 ++++ src/vcl/sock_test_client.c | 101 +++++++++++++++++++++++++++- src/vcl/sock_test_server.c | 158 +++++++++++++++++++++++++++++++++++++++++++- test/scripts/socket_test.sh | 4 +- 5 files changed, 350 insertions(+), 50 deletions(-) diff --git a/src/vcl/ldp.c b/src/vcl/ldp.c index e7a80cb7a24..d31cd2cabd4 100644 --- a/src/vcl/ldp.c +++ b/src/vcl/ldp.c @@ -796,43 +796,17 @@ ldp_pselect (int nfds, fd_set * __restrict readfds, return -1; } - if (nfds <= ldp->sid_bit_val) - { - func_str = "libc_pselect"; - - if (LDP_DEBUG > 3) - clib_warning - ("LDP<%d>: calling %s(): nfds %d, readfds %p, writefds %p, " - "exceptfds %p, timeout %p, sigmask %p", getpid (), func_str, nfds, - readfds, writefds, exceptfds, timeout, sigmask); - - rv = libc_pselect (nfds, readfds, writefds, exceptfds, - timeout, sigmask); - goto done; - } - - if (PREDICT_FALSE (ldp->sid_bit_val > FD_SETSIZE / 2)) - { - clib_warning ("LDP<%d>: ERROR: LDP sid bit value %d (0x%x) > " - "FD_SETSIZE/2 %d (0x%x)!", getpid (), - ldp->sid_bit_val, ldp->sid_bit_val, - FD_SETSIZE / 2, FD_SETSIZE / 2); - errno = EOVERFLOW; - return -1; - } - if (timeout) { time_out = (timeout->tv_sec == 0 && timeout->tv_nsec == 0) ? (f64) 0 : (f64) timeout->tv_sec + - (f64) timeout->tv_nsec / (f64) 1000000000 + - (f64) (timeout->tv_nsec % 1000000000) / (f64) 1000000000; + (f64) timeout->tv_nsec / (f64) 1000000000; /* select as fine grained sleep */ if (!nfds) { if (LDP_DEBUG > 3) - clib_warning ("LDP<%d>: sleeping for %f seconds", + clib_warning ("LDP<%d>: sleeping for %.02f seconds", getpid (), time_out); time_out += clib_time_now (&ldp->clib_time); @@ -849,6 +823,32 @@ ldp_pselect (int nfds, fd_set * __restrict readfds, else time_out = -1; + + if (nfds <= ldp->sid_bit_val) + { + func_str = "libc_pselect"; + + if (LDP_DEBUG > 3) + clib_warning + ("LDP<%d>: calling %s(): nfds %d, readfds %p, writefds %p, " + "exceptfds %p, timeout %p, sigmask %p", getpid (), func_str, nfds, + readfds, writefds, exceptfds, timeout, sigmask); + + rv = libc_pselect (nfds, readfds, writefds, exceptfds, + timeout, sigmask); + goto done; + } + + if (PREDICT_FALSE (ldp->sid_bit_val > FD_SETSIZE / 2)) + { + clib_warning ("LDP<%d>: ERROR: LDP sid bit value %d (0x%x) > " + "FD_SETSIZE/2 %d (0x%x)!", getpid (), + ldp->sid_bit_val, ldp->sid_bit_val, + FD_SETSIZE / 2, FD_SETSIZE / 2); + errno = EOVERFLOW; + return -1; + } + sid_bits = libc_bits = 0; if (readfds) { @@ -3014,13 +3014,18 @@ epoll_ctl (int epfd, int op, int fd, struct epoll_event *event) if ((errno = -ldp_init ())) return -1; - if (vep_idx != INVALID_SESSION_ID) + if (PREDICT_TRUE (vep_idx != INVALID_SESSION_ID)) { u32 sid = ldp_sid_from_fd (fd); + if (LDP_DEBUG > 1) + clib_warning ("LDP<%d>: epfd %d (0x%x), vep_idx %d (0x%x), " + "sid %d (0x%x)", getpid (), epfd, epfd, + vep_idx, vep_idx, sid, sid); + if (sid != INVALID_SESSION_ID) { - func_str = "vppcom_epoll_create"; + func_str = "vppcom_epoll_ctl"; if (LDP_DEBUG > 1) clib_warning ("LDP<%d>: epfd %d (0x%x): calling %s(): " @@ -3037,30 +3042,45 @@ epoll_ctl (int epfd, int op, int fd, struct epoll_event *event) } else { - int epfd; + int libc_epfd; u32 size = sizeof (epfd); func_str = "vppcom_session_attr[GET_LIBC_EPFD]"; - epfd = vppcom_session_attr (vep_idx, VPPCOM_ATTR_GET_LIBC_EPFD, - 0, 0); - if (!epfd) + libc_epfd = vppcom_session_attr (vep_idx, + VPPCOM_ATTR_GET_LIBC_EPFD, 0, 0); + if (LDP_DEBUG > 1) + clib_warning ("LDP<%d>: epfd %d (0x%x), vep_idx %d (0x%x): " + "%s() returned libc_epfd %d (0x%x)", + getpid (), epfd, epfd, vep_idx, vep_idx, + func_str, libc_epfd, libc_epfd); + + if (!libc_epfd) { func_str = "libc_epoll_create1"; if (LDP_DEBUG > 1) - clib_warning ("LDP<%d>: calling %s(): EPOLL_CLOEXEC", - getpid (), func_str); + clib_warning ("LDP<%d>: epfd %d (0x%x), vep_idx %d (0x%x): " + "calling %s(): EPOLL_CLOEXEC", + getpid (), epfd, epfd, vep_idx, vep_idx, + func_str); - epfd = libc_epoll_create1 (EPOLL_CLOEXEC); - if (epfd < 0) + libc_epfd = libc_epoll_create1 (EPOLL_CLOEXEC); + if (libc_epfd < 0) { - rv = epfd; + rv = libc_epfd; goto done; } func_str = "vppcom_session_attr[SET_LIBC_EPFD]"; + if (LDP_DEBUG > 1) + clib_warning ("LDP<%d>: epfd %d (0x%x): calling %s(): " + "vep_idx %d (0x%x), VPPCOM_ATTR_SET_LIBC_EPFD, " + "libc_epfd %d (0x%x), size %d", + getpid (), epfd, epfd, func_str, + vep_idx, vep_idx, libc_epfd, libc_epfd, size); + rv = vppcom_session_attr (vep_idx, VPPCOM_ATTR_SET_LIBC_EPFD, - &epfd, &size); + &libc_epfd, &size); if (rv < 0) { errno = -rv; @@ -3068,18 +3088,32 @@ epoll_ctl (int epfd, int op, int fd, struct epoll_event *event) goto done; } } - else if (PREDICT_FALSE (epfd < 0)) + else if (PREDICT_FALSE (libc_epfd < 0)) { errno = -epfd; rv = -1; goto done; } - rv = libc_epoll_ctl (epfd, op, fd, event); + func_str = "libc_epoll_ctl"; + + if (LDP_DEBUG > 1) + clib_warning ("LDP<%d>: epfd %d (0x%x): calling %s(): " + "libc_epfd %d (0x%x), op %d, " + "fd %d (0x%x), event %p", + getpid (), epfd, epfd, func_str, + libc_epfd, libc_epfd, op, fd, fd, event); + + rv = libc_epoll_ctl (libc_epfd, op, fd, event); } } else { + /* The LDP epoll_create1 always creates VCL epfd's. + * The app should never have a kernel base epoll fd unless it + * was acquired outside of the LD_PRELOAD process context. + * In any case, if we get one, punt it to libc_epoll_ctl. + */ func_str = "libc_epoll_ctl"; if (LDP_DEBUG > 1) @@ -3152,9 +3186,10 @@ ldp_epoll_pwait (int epfd, struct epoll_event *events, if (LDP_DEBUG > 2) clib_warning ("LDP<%d>: epfd %d (0x%x): vep_idx %d (0x%x), " "libc_epfd %d (0x%x), events %p, maxevents %d, " - "timeout %d, sigmask %p", getpid (), epfd, epfd, - vep_idx, vep_idx, libc_epfd, libc_epfd, events, - maxevents, timeout, sigmask); + "timeout %d, sigmask %p: time_to_wait %.02f", + getpid (), epfd, epfd, vep_idx, vep_idx, + libc_epfd, libc_epfd, events, maxevents, timeout, + sigmask, time_to_wait, time_out); do { if (!ldp->epoll_wait_vcl) diff --git a/src/vcl/sock_test.h b/src/vcl/sock_test.h index b4a22adbe20..0729c310f50 100644 --- a/src/vcl/sock_test.h +++ b/src/vcl/sock_test.h @@ -48,6 +48,11 @@ #define SOCK_TEST_CFG_BUF_SIZE_MIN 128 #define SOCK_TEST_CFG_MAX_TEST_SCKTS 5 +#define SOCK_TEST_AF_UNIX_FILENAME "/tmp/ldp_server_af_unix_socket" +#define SOCK_TEST_MIXED_EPOLL_DATA "Hello, world! (over an AF_UNIX socket)" +#define SOCK_TEST_AF_UNIX_ACCEPT_DATA 0xaf0000af +#define SOCK_TEST_AF_UNIX_FD_MASK 0x00af0000 + typedef enum { SOCK_TEST_TYPE_NONE, @@ -302,6 +307,13 @@ sock_test_stats_dump (char * header, sock_test_stats_t * stats, stats->stop.tv_sec, stats->stop.tv_nsec); printf (SOCK_TEST_SEPARATOR_STRING); + +#if SOCK_SERVER_USE_EPOLL && !defined (VCL_TEST) + printf (" af_unix xacts: %lu (0x%08lx)\n", + sock_server_main.af_unix_xacts); + + printf (SOCK_TEST_SEPARATOR_STRING); +#endif } static inline int diff --git a/src/vcl/sock_test_client.c b/src/vcl/sock_test_client.c index ddb9e7fb20a..7a735f5f3c0 100644 --- a/src/vcl/sock_test_client.c +++ b/src/vcl/sock_test_client.c @@ -23,11 +23,17 @@ #include #include #include +#ifndef VCL_TEST +#include +#endif typedef struct { #ifdef VCL_TEST vppcom_endpt_t server_endpt; +#else + int af_unix_echo_tx; + int af_unix_echo_rx; #endif struct sockaddr_in server_addr; sock_test_socket_t ctrl_socket; @@ -201,6 +207,94 @@ echo_test_client () } clock_gettime (CLOCK_REALTIME, &ctrl->stats.stop); +#ifndef VCL_TEST + { + int fd, errno_val; + struct sockaddr_un serveraddr; + uint8_t buffer[256]; + size_t nbytes = strlen (SOCK_TEST_MIXED_EPOLL_DATA) + 1; + struct timeval timeout; + + /* Open AF_UNIX socket and send an echo to test mixed epoll on server. + */ + fd = socket (AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + { + errno_val = errno; + perror ("ERROR in echo_test_client(): socket(AF_UNIX) failed"); + fprintf (stderr, + "CLIENT: ERROR: socket(AF_UNIX, SOCK_STREAM, 0) failed " + "(errno = %d)!\n", errno_val); + goto out; + } + memset (&serveraddr, 0, sizeof (serveraddr)); + serveraddr.sun_family = AF_UNIX; + strcpy (serveraddr.sun_path, SOCK_TEST_AF_UNIX_FILENAME); + rv = connect (fd, (struct sockaddr *) &serveraddr, SUN_LEN (&serveraddr)); + if (rv < 0) + { + errno_val = errno; + perror ("ERROR in echo_test_client(): connect() failed"); + fprintf (stderr, "CLIENT: ERROR: connect(fd %d, \"%s\", %lu) " + "failed (errno = %d)!\n", fd, SOCK_TEST_AF_UNIX_FILENAME, + SUN_LEN (&serveraddr), errno_val); + goto done; + } + + scm->af_unix_echo_tx++; + strcpy ((char *) buffer, SOCK_TEST_MIXED_EPOLL_DATA); + timeout.tv_sec = 0; + timeout.tv_usec = 250000; + select (0, NULL, NULL, NULL, &timeout); /* delay .25 secs */ + rv = write (fd, buffer, nbytes); + if (rv < 0) + { + errno_val = errno; + perror ("ERROR in echo_test_client(): write() failed"); + fprintf (stderr, "CLIENT: ERROR: write(fd %d, \"%s\", %lu) " + "failed (errno = %d)!\n", fd, buffer, nbytes, errno_val); + goto done; + } + else if (rv < nbytes) + { + fprintf (stderr, "CLIENT: ERROR: write(fd %d, \"%s\", %lu) " + "returned %d!\n", fd, buffer, nbytes, rv); + goto done; + } + + printf ("CLIENT (AF_UNIX): TX (%d bytes) - '%s'\n", rv, buffer); + memset (buffer, 0, sizeof (buffer)); + rv = read (fd, buffer, nbytes); + if (rv < 0) + { + errno_val = errno; + perror ("ERROR in echo_test_client(): read() failed"); + fprintf (stderr, "CLIENT: ERROR: read(fd %d, %p, %lu) " + "failed (errno = %d)!\n", fd, buffer, nbytes, errno_val); + goto done; + } + else if (rv < nbytes) + { + fprintf (stderr, "CLIENT: ERROR: read(fd %d, %p, %lu) " + "returned %d!\n", fd, buffer, nbytes, rv); + goto done; + } + + if (!strncmp (SOCK_TEST_MIXED_EPOLL_DATA, (const char *) buffer, nbytes)) + { + printf ("CLIENT (AF_UNIX): RX (%d bytes) - '%s'\n", rv, buffer); + scm->af_unix_echo_rx++; + } + else + printf ("CLIENT (AF_UNIX): ERROR: RX (%d bytes) - '%s'\n", rv, buffer); + + done: + close (fd); + out: + ; + } +#endif + for (i = 0; i < ctrl->cfg.num_test_sockets; i++) { tsock = &scm->test_socket[i]; @@ -427,6 +521,10 @@ exit_client (void) sock_test_socket_t *tsock; int i; +#ifndef VCL_TEST + printf ("CLIENT: af_unix_echo_tx %d, af_unix_echo_rx %d\n", + scm->af_unix_echo_tx, scm->af_unix_echo_rx); +#endif for (i = 0; i < ctrl->cfg.num_test_sockets; i++) { tsock = &scm->test_socket[i]; @@ -1085,10 +1183,11 @@ main (int argc, char **argv) #ifdef VCL_TEST vppcom_session_close (ctrl->fd); vppcom_app_destroy (); + return 0; #else close (ctrl->fd); + return (scm->af_unix_echo_tx == scm->af_unix_echo_rx) ? 0 : -1; #endif - return 0; } /* diff --git a/src/vcl/sock_test_server.c b/src/vcl/sock_test_server.c index 54769817c0f..c0baefa99de 100644 --- a/src/vcl/sock_test_server.c +++ b/src/vcl/sock_test_server.c @@ -30,6 +30,9 @@ #if SOCK_SERVER_USE_EPOLL #include +#if !defined(VCL_TEST) +#include +#endif #endif #ifdef VCL_TEST @@ -64,6 +67,13 @@ typedef struct int epfd; struct epoll_event listen_ev; struct epoll_event wait_events[SOCK_SERVER_MAX_EPOLL_EVENTS]; +#if !defined (VCL_TEST) + int af_unix_listen_fd; + int af_unix_fd; + struct epoll_event af_unix_listen_ev; + struct sockaddr_un serveraddr; + uint32_t af_unix_xacts; +#endif #endif size_t num_conn; size_t conn_pool_size; @@ -282,6 +292,75 @@ stream_test_server (sock_server_conn_t * conn, int rx_bytes) } } +#if SOCK_SERVER_USE_EPOLL && !defined (VCL_TEST) +static inline void +af_unix_echo (void) +{ + sock_server_main_t *ssm = &sock_server_main; + int af_unix_client_fd; + int rv; + int errno_val; + uint8_t buffer[256]; + size_t nbytes = strlen (SOCK_TEST_MIXED_EPOLL_DATA) + 1; + +#if HAVE_ACCEPT4 + af_unix_client_fd = accept4 (ssm->af_unix_listen_fd, + (struct sockaddr *) NULL, NULL, NULL); +#else + af_unix_client_fd = accept (ssm->af_unix_listen_fd, + (struct sockaddr *) NULL, NULL); +#endif + if (af_unix_client_fd < 0) + { + errno_val = errno; + perror ("ERROR in af_unix_accept()"); + fprintf (stderr, "SERVER: ERROR: accept failed " + "(errno = %d)!\n", errno_val); + return; + } + + printf ("SERVER: Got an AF_UNIX connection -- fd = %d (0x%08x)!\n", + af_unix_client_fd, af_unix_client_fd); + + memset (buffer, 0, sizeof (buffer)); + + rv = read (af_unix_client_fd, buffer, nbytes); + if (rv < 0) + { + errno_val = errno; + perror ("ERROR in af_unix_echo(): read() failed"); + fprintf (stderr, "SERVER: ERROR: read(af_unix_client_fd %d (0x%x), " + "\"%s\", nbytes %lu) failed (errno = %d)!\n", + af_unix_client_fd, af_unix_client_fd, buffer, nbytes, + errno_val); + goto done; + } + + printf ("SERVER (AF_UNIX): RX (%d bytes) - '%s'\n", rv, buffer); + + if (!strncmp (SOCK_TEST_MIXED_EPOLL_DATA, (const char *) buffer, nbytes)) + { + rv = write (af_unix_client_fd, buffer, nbytes); + if (rv < 0) + { + errno_val = errno; + perror ("ERROR in af_unix_echo(): write() failed"); + fprintf (stderr, + "SERVER: ERROR: write(af_unix_client_fd %d (0x%x), " + "\"%s\", nbytes %ld) failed (errno = %d)!\n", + af_unix_client_fd, af_unix_client_fd, buffer, nbytes, + errno_val); + goto done; + } + printf ("SERVER (AF_UNIX): TX (%d bytes) - '%s'\n", rv, buffer); + ssm->af_unix_xacts++; + } +done: + close (af_unix_client_fd); +} + +#endif + static inline void new_client (void) { @@ -398,6 +477,50 @@ main (int argc, char **argv) } #else ssm->listen_fd = socket (AF_INET, SOCK_STREAM, 0); +#if SOCK_SERVER_USE_EPOLL && !defined (VCL_TEST) + unlink ((const char *) SOCK_TEST_AF_UNIX_FILENAME); + ssm->af_unix_listen_fd = socket (AF_UNIX, SOCK_STREAM, 0); + if (ssm->af_unix_listen_fd < 0) + { + errno_val = errno; + perror ("ERROR in main(): socket(AF_UNIX) failed"); + fprintf (stderr, + "SERVER: ERROR: socket(AF_UNIX, SOCK_STREAM, 0) failed " + "(errno = %d)!\n", errno_val); + return ssm->af_unix_listen_fd; + } + + memset (&ssm->serveraddr, 0, sizeof (ssm->serveraddr)); + ssm->serveraddr.sun_family = AF_UNIX; + strcpy (ssm->serveraddr.sun_path, SOCK_TEST_AF_UNIX_FILENAME); + + rv = bind (ssm->af_unix_listen_fd, (struct sockaddr *) &ssm->serveraddr, + SUN_LEN (&ssm->serveraddr)); + if (rv < 0) + { + errno_val = errno; + perror ("ERROR in main(): bind(SOCK_TEST_AF_UNIX_FILENAME) failed"); + fprintf (stderr, "SERVER: ERROR: bind() fd %d, \"%s\": " + "failed (errno = %d)!\n", ssm->af_unix_listen_fd, + SOCK_TEST_AF_UNIX_FILENAME, errno_val); + close (ssm->af_unix_listen_fd); + unlink ((const char *) SOCK_TEST_AF_UNIX_FILENAME); + return rv; + } + + rv = listen (ssm->af_unix_listen_fd, 10); + if (rv < 0) + { + errno_val = errno; + perror ("ERROR in main(): listen(AF_UNIX) failed"); + fprintf (stderr, "SERVER: ERROR: listen() fd %d, \"%s\": " + "failed (errno = %d)!\n", ssm->af_unix_listen_fd, + SOCK_TEST_AF_UNIX_FILENAME, errno_val); + close (ssm->af_unix_listen_fd); + unlink ((const char *) SOCK_TEST_AF_UNIX_FILENAME); + return rv; + } +#endif /* SOCK_SERVER_USE_EPOLL */ #endif if (ssm->listen_fd < 0) { @@ -457,8 +580,6 @@ main (int argc, char **argv) return rv; } - printf ("\nSERVER: Waiting for a client to connect on port %d...\n", port); - #if ! SOCK_SERVER_USE_EPOLL FD_ZERO (&ssm->wr_fdset); @@ -492,6 +613,23 @@ main (int argc, char **argv) if (rv < 0) errno = -rv; #else + ssm->af_unix_listen_ev.events = EPOLLIN; + ssm->af_unix_listen_ev.data.u32 = SOCK_TEST_AF_UNIX_ACCEPT_DATA; + rv = epoll_ctl (ssm->epfd, EPOLL_CTL_ADD, ssm->af_unix_listen_fd, + &ssm->af_unix_listen_ev); + if (rv < 0) + { + errno_val = errno; + perror ("ERROR in main(): mixed epoll_ctl(EPOLL_CTL_ADD)"); + fprintf (stderr, "SERVER: ERROR: mixed epoll_ctl(epfd %d (0x%x), " + "EPOLL_CTL_ADD, af_unix_listen_fd %d (0x%x), EPOLLIN) failed " + "(errno = %d)!\n", ssm->epfd, ssm->epfd, + ssm->af_unix_listen_fd, ssm->af_unix_listen_fd, errno_val); + close (ssm->af_unix_listen_fd); + unlink ((const char *) SOCK_TEST_AF_UNIX_FILENAME); + return rv; + } + rv = epoll_ctl (ssm->epfd, EPOLL_CTL_ADD, ssm->listen_fd, &ssm->listen_ev); #endif if (rv < 0) @@ -504,6 +642,8 @@ main (int argc, char **argv) } #endif + printf ("\nSERVER: Waiting for a client to connect on port %d...\n", port); + while (1) { #if ! SOCK_SERVER_USE_EPOLL @@ -569,6 +709,14 @@ main (int argc, char **argv) new_client (); continue; } +#if !defined (VCL_TEST) + else if (ssm->wait_events[i].data.u32 == + SOCK_TEST_AF_UNIX_ACCEPT_DATA) + { + af_unix_echo (); + continue; + } +#endif conn = &ssm->conn_pool[ssm->wait_events[i].data.u32]; #endif client_fd = conn->fd; @@ -732,6 +880,12 @@ done: vppcom_app_destroy (); #else close (ssm->listen_fd); + +#if SOCK_SERVER_USE_EPOLL && !defined (VCL_TEST) + close (ssm->af_unix_listen_fd); + unlink ((const char *) SOCK_TEST_AF_UNIX_FILENAME); +#endif /* SOCK_SERVER_USE_EPOLL */ + #endif if (ssm->conn_pool) free (ssm->conn_pool); diff --git a/test/scripts/socket_test.sh b/test/scripts/socket_test.sh index 96a445eb815..5b6507461db 100755 --- a/test/scripts/socket_test.sh +++ b/test/scripts/socket_test.sh @@ -526,8 +526,8 @@ write_script_header() { if [ -n "$VCL_DEBUG" ] ; then echo "export VCL_DEBUG=$VCL_DEBUG" >> $1 fi - if [ -n "$VCOM_DEBUG" ] ; then - echo "export VCOM_DEBUG=$VCOM_DEBUG" >> $1 + if [ -n "$LDP_DEBUG" ] ; then + echo "export LDP_DEBUG=$LDP_DEBUG" >> $1 fi if [ -n "$VCOM_APP_NAME" ] ; then echo "export VCOM_APP_NAME=$VCOM_APP_NAME" >> $1 -- 2.16.6