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);
 }
 

Reply via email to