Module Name: src Committed By: jmcneill Date: Wed Nov 29 19:21:45 UTC 2017
Modified Files: src/sys/net: if_tap.c Log Message: Make tap(4) MP-safe. To generate a diff of this commit: cvs rdiff -u -r1.101 -r1.102 src/sys/net/if_tap.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/net/if_tap.c diff -u src/sys/net/if_tap.c:1.101 src/sys/net/if_tap.c:1.102 --- src/sys/net/if_tap.c:1.101 Mon Oct 30 16:01:19 2017 +++ src/sys/net/if_tap.c Wed Nov 29 19:21:44 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: if_tap.c,v 1.101 2017/10/30 16:01:19 ozaki-r Exp $ */ +/* $NetBSD: if_tap.c,v 1.102 2017/11/29 19:21:44 jmcneill Exp $ */ /* * Copyright (c) 2003, 2004, 2008, 2009 The NetBSD Foundation. @@ -33,7 +33,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_tap.c,v 1.101 2017/10/30 16:01:19 ozaki-r Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_tap.c,v 1.102 2017/11/29 19:21:44 jmcneill Exp $"); #if defined(_KERNEL_OPT) @@ -54,6 +54,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_tap.c,v 1 #include <sys/kmem.h> #include <sys/module.h> #include <sys/mutex.h> +#include <sys/condvar.h> #include <sys/poll.h> #include <sys/proc.h> #include <sys/select.h> @@ -109,8 +110,8 @@ struct tap_softc { #define TAP_GOING 0x00000008 /* interface is being destroyed */ struct selinfo sc_rsel; pid_t sc_pgid; /* For async. IO */ - kmutex_t sc_rdlock; - kmutex_t sc_kqlock; + kmutex_t sc_lock; + kcondvar_t sc_cv; void *sc_sih; struct timespec sc_atime; struct timespec sc_mtime; @@ -182,7 +183,7 @@ const struct cdevsw tap_cdevsw = { .d_mmap = nommap, .d_kqfilter = tap_cdev_kqfilter, .d_discard = nodiscard, - .d_flag = D_OTHER + .d_flag = D_OTHER | D_MPSAFE }; #define TAP_CLONER 0xfffff /* Maximal minor value */ @@ -315,22 +316,8 @@ tap_attach(device_t parent, device_t sel sc->sc_flags = 0; selinit(&sc->sc_rsel); - /* - * Initialize the two locks for the device. - * - * We need a lock here because even though the tap device can be - * opened only once, the file descriptor might be passed to another - * process, say a fork(2)ed child. - * - * The Giant saves us from most of the hassle, but since the read - * operation can sleep, we don't want two processes to wake up at - * the same moment and both try and dequeue a single packet. - * - * The queue for event listeners (used by kqueue(9), see below) has - * to be protected too, so use a spin lock. - */ - mutex_init(&sc->sc_rdlock, MUTEX_DEFAULT, IPL_NONE); - mutex_init(&sc->sc_kqlock, MUTEX_DEFAULT, IPL_VM); + cv_init(&sc->sc_cv, "tapread"); + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NET); if (!pmf_device_register(self, NULL, NULL)) aprint_error_dev(self, "couldn't establish power handler\n"); @@ -385,12 +372,12 @@ tap_attach(device_t parent, device_t sel aprint_error_dev(self, "if_initialize failed(%d)\n", error); ifmedia_removeall(&sc->sc_im); pmf_device_deregister(self); - mutex_destroy(&sc->sc_rdlock); - mutex_destroy(&sc->sc_kqlock); + mutex_destroy(&sc->sc_lock); seldestroy(&sc->sc_rsel); return; /* Error */ } + ifp->if_percpuq = if_percpuq_create(ifp); ether_ifattach(ifp, enaddr); if_register(ifp); @@ -428,13 +415,10 @@ tap_detach(device_t self, int flags) struct tap_softc *sc = device_private(self); struct ifnet *ifp = &sc->sc_ec.ec_if; int error; - int s; sc->sc_flags |= TAP_GOING; - s = splnet(); tap_stop(ifp, 1); if_down(ifp); - splx(s); if (sc->sc_sih != NULL) { softint_disestablish(sc->sc_sih); @@ -454,8 +438,8 @@ tap_detach(device_t self, int flags) if_detach(ifp); ifmedia_removeall(&sc->sc_im); seldestroy(&sc->sc_rsel); - mutex_destroy(&sc->sc_rdlock); - mutex_destroy(&sc->sc_kqlock); + mutex_destroy(&sc->sc_lock); + cv_destroy(&sc->sc_cv); pmf_device_deregister(self); @@ -516,12 +500,13 @@ tap_start(struct ifnet *ifp) struct tap_softc *sc = (struct tap_softc *)ifp->if_softc; struct mbuf *m0; + mutex_enter(&sc->sc_lock); if ((sc->sc_flags & TAP_INUSE) == 0) { /* Simply drop packets */ for(;;) { IFQ_DEQUEUE(&ifp->if_snd, m0); if (m0 == NULL) - return; + goto done; ifp->if_opackets++; bpf_mtap(ifp, m0); @@ -530,11 +515,13 @@ tap_start(struct ifnet *ifp) } } else if (!IFQ_IS_EMPTY(&ifp->if_snd)) { ifp->if_flags |= IFF_OACTIVE; - wakeup(sc); + cv_broadcast(&sc->sc_cv); selnotify(&sc->sc_rsel, 0, 1); if (sc->sc_flags & TAP_ASYNCIO) softint_schedule(sc->sc_sih); } +done: + mutex_exit(&sc->sc_lock); } static void @@ -645,11 +632,13 @@ tap_stop(struct ifnet *ifp, int disable) { struct tap_softc *sc = (struct tap_softc *)ifp->if_softc; + mutex_enter(&sc->sc_lock); ifp->if_flags &= ~IFF_RUNNING; - wakeup(sc); + cv_broadcast(&sc->sc_cv); selnotify(&sc->sc_rsel, 0, 1); if (sc->sc_flags & TAP_ASYNCIO) softint_schedule(sc->sc_sih); + mutex_exit(&sc->sc_lock); } /* @@ -931,7 +920,7 @@ tap_dev_read(int unit, struct uio *uio, struct tap_softc *sc = device_lookup_private(&tap_cd, unit); struct ifnet *ifp; struct mbuf *m, *n; - int error = 0, s; + int error = 0; if (sc == NULL) return ENXIO; @@ -946,43 +935,34 @@ tap_dev_read(int unit, struct uio *uio, * In the TAP_NBIO case, we have to make sure we won't be sleeping */ if ((sc->sc_flags & TAP_NBIO) != 0) { - if (!mutex_tryenter(&sc->sc_rdlock)) + if (!mutex_tryenter(&sc->sc_lock)) return EWOULDBLOCK; } else { - mutex_enter(&sc->sc_rdlock); + mutex_enter(&sc->sc_lock); } - s = splnet(); if (IFQ_IS_EMPTY(&ifp->if_snd)) { ifp->if_flags &= ~IFF_OACTIVE; - /* - * We must release the lock before sleeping, and re-acquire it - * after. - */ - mutex_exit(&sc->sc_rdlock); if (sc->sc_flags & TAP_NBIO) error = EWOULDBLOCK; else - error = tsleep(sc, PSOCK|PCATCH, "tap", 0); - splx(s); + error = cv_wait_sig(&sc->sc_cv, &sc->sc_lock); - if (error != 0) + if (error != 0) { + mutex_exit(&sc->sc_lock); return error; + } /* The device might have been downed */ - if ((ifp->if_flags & IFF_UP) == 0) + if ((ifp->if_flags & IFF_UP) == 0) { + mutex_exit(&sc->sc_lock); return EHOSTDOWN; - if ((sc->sc_flags & TAP_NBIO)) { - if (!mutex_tryenter(&sc->sc_rdlock)) - return EWOULDBLOCK; - } else { - mutex_enter(&sc->sc_rdlock); } - s = splnet(); } IFQ_DEQUEUE(&ifp->if_snd, m); + mutex_exit(&sc->sc_lock); + ifp->if_flags &= ~IFF_OACTIVE; - splx(s); if (m == NULL) { error = 0; goto out; @@ -1004,7 +984,6 @@ tap_dev_read(int unit, struct uio *uio, m_freem(m); out: - mutex_exit(&sc->sc_rdlock); return error; } @@ -1061,7 +1040,6 @@ tap_dev_write(int unit, struct uio *uio, struct ifnet *ifp; struct mbuf *m, **mp; int error = 0; - int s; if (sc == NULL) return ENXIO; @@ -1098,9 +1076,7 @@ tap_dev_write(int unit, struct uio *uio, m_set_rcvif(m, ifp); - s = splnet(); - if_input(ifp, m); - splx(s); + if_percpuq_enqueue(ifp->if_percpuq, m); return 0; } @@ -1221,9 +1197,9 @@ tap_dev_poll(int unit, int events, struc if (m != NULL) revents |= events & (POLLIN|POLLRDNORM); else { - mutex_spin_enter(&sc->sc_kqlock); + mutex_spin_enter(&sc->sc_lock); selrecord(l, &sc->sc_rsel); - mutex_spin_exit(&sc->sc_kqlock); + mutex_spin_exit(&sc->sc_lock); } splx(s); } @@ -1272,9 +1248,9 @@ tap_dev_kqfilter(int unit, struct knote } kn->kn_hook = sc; - mutex_spin_enter(&sc->sc_kqlock); + mutex_spin_enter(&sc->sc_lock); SLIST_INSERT_HEAD(&sc->sc_rsel.sel_klist, kn, kn_selnext); - mutex_spin_exit(&sc->sc_kqlock); + mutex_spin_exit(&sc->sc_lock); KERNEL_UNLOCK_ONE(NULL); return 0; } @@ -1285,9 +1261,9 @@ tap_kqdetach(struct knote *kn) struct tap_softc *sc = (struct tap_softc *)kn->kn_hook; KERNEL_LOCK(1, NULL); - mutex_spin_enter(&sc->sc_kqlock); + mutex_spin_enter(&sc->sc_lock); SLIST_REMOVE(&sc->sc_rsel.sel_klist, kn, knote, kn_selnext); - mutex_spin_exit(&sc->sc_kqlock); + mutex_spin_exit(&sc->sc_lock); KERNEL_UNLOCK_ONE(NULL); }