Module Name: src Committed By: pooka Date: Wed Apr 2 14:48:04 UTC 2014
Modified Files: src/lib/librumpclient: rumpclient.c Log Message: On Linux, poll signalfd() when waiting for a kernel response. This allows the same type of race-free handling of signals as kqueue() allows on NetBSD. One of the noticeable things is that you can now interrupt rumprun ping mid-interval on Linux. per suggestion from Justin Cormack To generate a diff of this commit: cvs rdiff -u -r1.57 -r1.58 src/lib/librumpclient/rumpclient.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/lib/librumpclient/rumpclient.c diff -u src/lib/librumpclient/rumpclient.c:1.57 src/lib/librumpclient/rumpclient.c:1.58 --- src/lib/librumpclient/rumpclient.c:1.57 Wed Feb 26 02:03:40 2014 +++ src/lib/librumpclient/rumpclient.c Wed Apr 2 14:48:03 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: rumpclient.c,v 1.57 2014/02/26 02:03:40 pooka Exp $ */ +/* $NetBSD: rumpclient.c,v 1.58 2014/04/02 14:48:03 pooka Exp $ */ /* * Copyright (c) 2010, 2011 Antti Kantee. All Rights Reserved. @@ -38,17 +38,19 @@ * notifications but defer their handling to a stage where we do not * hold the communication lock. Taking a signal while holding on to * that lock may cause a deadlock. Therefore, block signals throughout - * the RPC when using poll. This unfortunately means that the normal - * SIGINT way of stopping a process while it is undergoing rump kernel - * RPC will not work. If anyone know which Linux system call handles - * the above scenario correctly, I'm all ears. + * the RPC when using poll. On Linux, we use signalfd in the same role + * as kqueue on NetBSD to be able to take signals while waiting for a + * response from the server. */ #ifdef __NetBSD__ #define USE_KQUEUE #endif +#ifdef __linux__ +#define USE_SIGNALFD +#endif -__RCSID("$NetBSD: rumpclient.c,v 1.57 2014/02/26 02:03:40 pooka Exp $"); +__RCSID("$NetBSD: rumpclient.c,v 1.58 2014/04/02 14:48:03 pooka Exp $"); #include <sys/param.h> #include <sys/mman.h> @@ -96,6 +98,12 @@ int (*host_kevent)(int, const struct kev struct kevent *, size_t, const struct timespec *); #endif +#ifdef USE_SIGNALFD +#include <sys/signalfd.h> + +int (*host_signalfd)(int, const sigset_t *, int); +#endif + int (*host_execve)(const char *, char *const[], char *const[]); #include "sp_common.c" @@ -105,7 +113,7 @@ static struct spclient clispc = { .spc_fd = -1, }; -static int kq = -1; +static int holyfd; static sigset_t fullset; static int doconnect(void); @@ -237,7 +245,7 @@ cliwaitresp(struct spclient *spc, struct * typically we don't have a frame waiting * when we come in here, so call kevent now */ - rv = host_kevent(kq, NULL, 0, + rv = host_kevent(holyfd, NULL, 0, kev, __arraycount(kev), NULL); if (__predict_false(rv == -1)) { @@ -267,13 +275,19 @@ cliwaitresp(struct spclient *spc, struct * determine what happens next. */ activity: -#else /* USE_KQUEUE */ - struct pollfd pfd; - - pfd.fd = clispc.spc_fd; - pfd.events = POLLIN; +#else /* !USE_KQUEUE */ + struct pollfd pfd[2]; - rv = host_poll(&pfd, 1, -1); + pfd[0].fd = clispc.spc_fd; + pfd[0].events = POLLIN; + pfd[1].fd = holyfd; + pfd[1].events = POLLIN; + + rv = host_poll(pfd, 2, -1); + if (pfd[1].revents & POLLIN) { + dosig = 1; + goto cleanup; + } #endif /* !USE_KQUEUE */ switch (readframe(spc)) { @@ -677,6 +691,52 @@ dupgood(int myfd, int mustchange) return myfd; } +#if defined(USE_KQUEUE) + +static int +makeholyfd(void) +{ + struct kevent kev[NSIG+1]; + int i, fd; + + /* setup kqueue, we want all signals and the fd */ + if ((fd = dupgood(host_kqueue(), 0)) == -1) { + ERRLOG(("rump_sp: cannot setup kqueue")); + return -1; + } + + for (i = 0; i < NSIG; i++) { + EV_SET(&kev[i], i+1, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0, 0, 0); + } + EV_SET(&kev[NSIG], clispc.spc_fd, + EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, 0); + if (host_kevent(fd, kev, NSIG+1, NULL, 0, NULL) == -1) { + ERRLOG(("rump_sp: kevent() failed")); + return -1; + + return fd; +} + +#elif defined(USE_SIGNALFD) /* !USE_KQUEUE */ + +static int +makeholyfd(void) +{ + + return host_signalfd(-1, &fullset, 0); +} + +#else /* !USE_KQUEUE && !USE_SIGNALFD */ + +static int +makeholyfd(void) +{ + + return -1; +} + +#endif + static int doconnect(void) { @@ -686,9 +746,9 @@ doconnect(void) int s, error, flags; ssize_t n; - if (kq != -1) - host_close(kq); - kq = -1; + if (holyfd != -1) + host_close(holyfd); + holyfd = -1; s = -1; if (clispc.spc_fd != -1) @@ -759,29 +819,7 @@ doconnect(void) clispc.spc_fd = s; clispc.spc_state = SPCSTATE_RUNNING; clispc.spc_reconnecting = 0; - -#ifdef USE_KQUEUE -{ - struct kevent kev[NSIG+1]; - int i; - - /* setup kqueue, we want all signals and the fd */ - if ((kq = dupgood(host_kqueue(), 0)) == -1) { - ERRLOG(("rump_sp: cannot setup kqueue")); - return -1; - } - - for (i = 0; i < NSIG; i++) { - EV_SET(&kev[i], i+1, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0, 0, 0); - } - EV_SET(&kev[NSIG], clispc.spc_fd, - EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, 0); - if (host_kevent(kq, kev, NSIG+1, NULL, 0, NULL) == -1) { - ERRLOG(("rump_sp: kevent() failed")); - return -1; - } -} -#endif /* USE_KQUEUE */ + holyfd = makeholyfd(); return 0; } @@ -831,8 +869,10 @@ rumpclient_init(void) return 0; /* kq does not traverse fork() */ +#ifdef USE_KQUEUE if (init_done != 0) - kq = -1; + holyfd = -1; +#endif init_done = mypid; sigfillset(&fullset); @@ -883,6 +923,10 @@ rumpclient_init(void) #endif #endif /* USE_KQUEUE */ +#ifdef USE_SIGNALFD + FINDSYM(signalfd) +#endif + #undef FINDSYM #undef FINDSY2 @@ -903,7 +947,7 @@ rumpclient_init(void) goto out; if ((p = getenv("RUMPCLIENT__EXECFD")) != NULL) { - sscanf(p, "%d,%d", &clispc.spc_fd, &kq); + sscanf(p, "%d,%d", &clispc.spc_fd, &holyfd); unsetenv("RUMPCLIENT__EXECFD"); hstype = HANDSHAKE_EXEC; } else { @@ -932,7 +976,7 @@ rumpclient_init(void) struct rumpclient_fork { uint32_t fork_auth[AUTHLEN]; struct spclient fork_spc; - int fork_kq; + int fork_holyfd; }; struct rumpclient_fork * @@ -959,7 +1003,7 @@ rumpclient_prefork(void) free(resp); rpf->fork_spc = clispc; - rpf->fork_kq = kq; + rpf->fork_holyfd = holyfd; out: pthread_sigmask(SIG_SETMASK, &omask, NULL); @@ -976,7 +1020,14 @@ rumpclient_fork_init(struct rumpclient_f memset(&clispc, 0, sizeof(clispc)); clispc.spc_fd = osock; - kq = -1; /* kqueue descriptor is not copied over fork() */ +#ifdef USE_KQUEUE + holyfd = -1; /* kqueue descriptor is not copied over fork() */ +#else + if (holyfd != -1) { + host_close(holyfd); + holyfd = -1; + } +#endif if (doinit() == -1) return -1; @@ -1008,7 +1059,7 @@ rumpclient_fork_vparent(struct rumpclien { clispc = rpf->fork_spc; - kq = rpf->fork_kq; + holyfd = rpf->fork_holyfd; } void @@ -1030,9 +1081,9 @@ rumpclient__closenotify(int *fdp, enum r switch (variant) { case RUMPCLIENT_CLOSE_FCLOSEM: - untilfd = MAX(clispc.spc_fd, kq); + untilfd = MAX(clispc.spc_fd, holyfd); for (; fd <= untilfd; fd++) { - if (fd == clispc.spc_fd || fd == kq) + if (fd == clispc.spc_fd || fd == holyfd) continue; rv = host_close(fd); if (rv == -1) @@ -1061,23 +1112,20 @@ rumpclient__closenotify(int *fdp, enum r EVFILT_READ, EV_DELETE, 0, 0, 0); EV_SET(&kev[1], newfd, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, 0); - if (host_kevent(kq, kev, 2, NULL, 0, NULL) == -1) { + if (host_kevent(holyfd, kev, 2, NULL, 0, NULL) == -1) { int sverrno = errno; host_close(newfd); errno = sverrno; return -1; - } + }} +#endif /* !USE_KQUEUE */ clispc.spc_fd = newfd; - } } - if (fd == kq) { - newfd = dupgood(kq, 1); + if (holyfd != -1 && fd == holyfd) { + newfd = dupgood(holyfd, 1); if (newfd == -1) return -1; - kq = newfd; -#else /* USE_KQUEUE */ - clispc.spc_fd = newfd; -#endif /* !USE_KQUEUE */ + holyfd = newfd; } break; } @@ -1108,7 +1156,7 @@ rumpclient_exec(const char *path, char * int rv, sverrno; snprintf(buf, sizeof(buf), "RUMPCLIENT__EXECFD=%d,%d", - clispc.spc_fd, kq); + clispc.spc_fd, holyfd); envstr = malloc(strlen(buf)+1); if (envstr == NULL) { return ENOMEM;