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