Module Name:    src
Committed By:   snj
Date:           Wed Jun 21 17:39:24 UTC 2017

Modified Files:
        src/sys/net [netbsd-8]: if_vlan.c if_vlanvar.h

Log Message:
Pull up following revision(s) (requested by knakahara in ticket #41):
        sys/net/if_vlan.c: revision 1.98
        sys/net/if_vlanvar.h: revision 1.10
vlan(4) MP-ify. contributed by s-yamaguchi@IIJ, thanks.


To generate a diff of this commit:
cvs rdiff -u -r1.97 -r1.97.2.1 src/sys/net/if_vlan.c
cvs rdiff -u -r1.9 -r1.9.80.1 src/sys/net/if_vlanvar.h

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_vlan.c
diff -u src/sys/net/if_vlan.c:1.97 src/sys/net/if_vlan.c:1.97.2.1
--- src/sys/net/if_vlan.c:1.97	Mon May 29 02:55:49 2017
+++ src/sys/net/if_vlan.c	Wed Jun 21 17:39:24 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_vlan.c,v 1.97 2017/05/29 02:55:49 ozaki-r Exp $	*/
+/*	$NetBSD: if_vlan.c,v 1.97.2.1 2017/06/21 17:39:24 snj Exp $	*/
 
 /*-
  * Copyright (c) 2000, 2001 The NetBSD Foundation, Inc.
@@ -78,7 +78,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_vlan.c,v 1.97 2017/05/29 02:55:49 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_vlan.c,v 1.97.2.1 2017/06/21 17:39:24 snj Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -86,6 +86,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_vlan.c,v 
 #endif
 
 #include <sys/param.h>
+#include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/mbuf.h>
 #include <sys/queue.h>
@@ -95,6 +96,12 @@ __KERNEL_RCSID(0, "$NetBSD: if_vlan.c,v 
 #include <sys/proc.h>
 #include <sys/kauth.h>
 #include <sys/mutex.h>
+#include <sys/kmem.h>
+#include <sys/cpu.h>
+#include <sys/pserialize.h>
+#include <sys/psref.h>
+#include <sys/pslist.h>
+#include <sys/atomic.h>
 #include <sys/device.h>
 #include <sys/module.h>
 
@@ -116,6 +123,10 @@ __KERNEL_RCSID(0, "$NetBSD: if_vlan.c,v 
 
 #include "ioconf.h"
 
+#ifdef NET_MPSAFE
+#define VLAN_MPSAFE		1
+#endif
+
 struct vlan_mc_entry {
 	LIST_ENTRY(vlan_mc_entry)	mc_entries;
 	/*
@@ -131,21 +142,33 @@ struct vlan_mc_entry {
 
 #define	mc_enm		mc_u.mcu_enm
 
+
+struct ifvlan_linkmib {
+	struct ifvlan *ifvm_ifvlan;
+	const struct vlan_multisw *ifvm_msw;
+	int	ifvm_encaplen;	/* encapsulation length */
+	int	ifvm_mtufudge;	/* MTU fudged by this much */
+	int	ifvm_mintu;	/* min transmission unit */
+	uint16_t ifvm_proto;	/* encapsulation ethertype */
+	uint16_t ifvm_tag;	/* tag to apply on packets */
+	struct ifnet *ifvm_p;		/* parent interface of this vlan */
+
+	struct psref_target ifvm_psref;
+};
+
 struct ifvlan {
 	union {
 		struct ethercom ifvu_ec;
 	} ifv_u;
-	struct ifnet *ifv_p;	/* parent interface of this vlan */
-	struct ifv_linkmib {
-		const struct vlan_multisw *ifvm_msw;
-		int	ifvm_encaplen;	/* encapsulation length */
-		int	ifvm_mtufudge;	/* MTU fudged by this much */
-		int	ifvm_mintu;	/* min transmission unit */
-		uint16_t ifvm_proto;	/* encapsulation ethertype */
-		uint16_t ifvm_tag;	/* tag to apply on packets */
-	} ifv_mib;
+	struct ifvlan_linkmib *ifv_mib;	/*
+					 * reader must use vlan_getref_linkmib()
+					 * instead of direct dereference
+					 */
+	kmutex_t ifv_lock;		/* writer lock for ifv_mib */
+
 	LIST_HEAD(__vlan_mchead, vlan_mc_entry) ifv_mc_listhead;
 	LIST_ENTRY(ifvlan) ifv_list;
+	struct pslist_entry ifv_hash;
 	int ifv_flags;
 };
 
@@ -179,15 +202,47 @@ const struct vlan_multisw vlan_ether_mul
 
 static int	vlan_clone_create(struct if_clone *, int);
 static int	vlan_clone_destroy(struct ifnet *);
-static int	vlan_config(struct ifvlan *, struct ifnet *);
+static int	vlan_config(struct ifvlan *, struct ifnet *,
+    uint16_t);
 static int	vlan_ioctl(struct ifnet *, u_long, void *);
 static void	vlan_start(struct ifnet *);
+static int	vlan_transmit(struct ifnet *, struct mbuf *);
 static void	vlan_unconfig(struct ifnet *);
+static int	vlan_unconfig_locked(struct ifvlan *,
+    struct ifvlan_linkmib *);
+static void	vlan_hash_init(void);
+static int	vlan_hash_fini(void);
+static struct ifvlan_linkmib*	vlan_getref_linkmib(struct ifvlan *,
+    struct psref *);
+static void	vlan_putref_linkmib(struct ifvlan_linkmib *,
+    struct psref *);
+static void	vlan_linkmib_update(struct ifvlan *,
+    struct ifvlan_linkmib *);
+static struct ifvlan_linkmib*	vlan_lookup_tag_psref(struct ifnet *,
+    uint16_t, struct psref *);
+static int	tag_hash_func(uint16_t, u_long);
+
+LIST_HEAD(vlan_ifvlist, ifvlan);
+static struct {
+	kmutex_t lock;
+	struct vlan_ifvlist list;
+} ifv_list __cacheline_aligned;
 
-/* XXX This should be a hash table with the tag as the basis of the key. */
-static LIST_HEAD(, ifvlan) ifv_list;
 
-static kmutex_t ifv_mtx __cacheline_aligned;
+#if !defined(VLAN_TAG_HASH_SIZE)
+#define VLAN_TAG_HASH_SIZE 32
+#endif
+static struct {
+	kmutex_t lock;
+	struct pslist_head *lists;
+	u_long mask;
+} ifv_hash __cacheline_aligned = {
+	.lists = NULL,
+	.mask = 0,
+};
+
+pserialize_t vlan_psz __read_mostly;
+static struct psref_class *ifvm_psref_class __read_mostly;
 
 struct if_clone vlan_cloner =
     IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
@@ -208,10 +263,15 @@ vlanattach(int n)
 static void
 vlaninit(void)
 {
+	mutex_init(&ifv_list.lock, MUTEX_DEFAULT, IPL_NONE);
+	LIST_INIT(&ifv_list.list);
 
-	LIST_INIT(&ifv_list);
-	mutex_init(&ifv_mtx, MUTEX_DEFAULT, IPL_NONE);
+	mutex_init(&ifv_hash.lock, MUTEX_DEFAULT, IPL_NONE);
+	vlan_psz = pserialize_create();
+	ifvm_psref_class = psref_class_create("vlanlinkmib", IPL_SOFTNET);
 	if_clone_attach(&vlan_cloner);
+
+	vlan_hash_init();
 }
 
 static int
@@ -219,15 +279,24 @@ vlandetach(void)
 {
 	int error = 0;
 
-	if (!LIST_EMPTY(&ifv_list))
-		error = EBUSY;
-
-	if (error == 0) {
-		if_clone_detach(&vlan_cloner);
-		mutex_destroy(&ifv_mtx);
+	mutex_enter(&ifv_list.lock);
+	if (!LIST_EMPTY(&ifv_list.list)) {
+		mutex_exit(&ifv_list.lock);
+		return EBUSY;
 	}
+	mutex_exit(&ifv_list.lock);
 
-	return error;
+	error = vlan_hash_fini();
+	if (error != 0)
+		return error;
+
+	if_clone_detach(&vlan_cloner);
+	psref_class_destroy(ifvm_psref_class);
+	pserialize_destroy(vlan_psz);
+	mutex_destroy(&ifv_hash.lock);
+	mutex_destroy(&ifv_list.lock);
+
+	return 0;
 }
 
 static void
@@ -252,20 +321,32 @@ vlan_clone_create(struct if_clone *ifc, 
 {
 	struct ifvlan *ifv;
 	struct ifnet *ifp;
-	int s;
+	struct ifvlan_linkmib *mib;
 
 	ifv = malloc(sizeof(struct ifvlan), M_DEVBUF, M_WAITOK|M_ZERO);
+	mib = kmem_zalloc(sizeof(struct ifvlan_linkmib), KM_SLEEP);
 	ifp = &ifv->ifv_if;
 	LIST_INIT(&ifv->ifv_mc_listhead);
 
-	s = splnet();
-	LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list);
-	splx(s);
+	mib->ifvm_ifvlan = ifv;
+	mib->ifvm_p = NULL;
+	psref_target_init(&mib->ifvm_psref, ifvm_psref_class);
+
+	mutex_init(&ifv->ifv_lock, MUTEX_DEFAULT, IPL_NONE);
+	ifv->ifv_mib = mib;
+
+	mutex_enter(&ifv_list.lock);
+	LIST_INSERT_HEAD(&ifv_list.list, ifv, ifv_list);
+	mutex_exit(&ifv_list.lock);
 
 	if_initname(ifp, ifc->ifc_name, unit);
 	ifp->if_softc = ifv;
 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+#ifdef VLAN_MPSAFE
+	ifp->if_extflags = IFEF_START_MPSAFE;
+#endif
 	ifp->if_start = vlan_start;
+	ifp->if_transmit = vlan_transmit;
 	ifp->if_ioctl = vlan_ioctl;
 	IFQ_SET_READY(&ifp->if_snd);
 
@@ -280,47 +361,66 @@ static int
 vlan_clone_destroy(struct ifnet *ifp)
 {
 	struct ifvlan *ifv = ifp->if_softc;
-	int s;
 
-	s = splnet();
+	mutex_enter(&ifv_list.lock);
 	LIST_REMOVE(ifv, ifv_list);
+	mutex_exit(&ifv_list.lock);
+
 	vlan_unconfig(ifp);
 	if_detach(ifp);
-	splx(s);
 
+	psref_target_destroy(&ifv->ifv_mib->ifvm_psref, ifvm_psref_class);
+	kmem_free(ifv->ifv_mib, sizeof(struct ifvlan_linkmib));
+	mutex_destroy(&ifv->ifv_lock);
 	free(ifv, M_DEVBUF);
 
 	return (0);
 }
 
 /*
- * Configure a VLAN interface.  Must be called at splnet().
+ * Configure a VLAN interface.
  */
 static int
-vlan_config(struct ifvlan *ifv, struct ifnet *p)
+vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t tag)
 {
 	struct ifnet *ifp = &ifv->ifv_if;
-	int error;
+	struct ifvlan_linkmib *nmib = NULL;
+	struct ifvlan_linkmib *omib = NULL;
+	struct psref_target *nmib_psref = NULL;
+	int error = 0;
+	int idx;
+	bool omib_cleanup = false;
+
+	nmib = kmem_alloc(sizeof(*nmib), KM_SLEEP);
+
+	mutex_enter(&ifv->ifv_lock);
+	omib = ifv->ifv_mib;
+
+	if (omib->ifvm_p != NULL) {
+		error = EBUSY;
+		goto done;
+	}
 
-	if (ifv->ifv_p != NULL)
-		return (EBUSY);
+	*nmib = *omib;
+	nmib_psref = &nmib->ifvm_psref;
+
+	psref_target_init(nmib_psref, ifvm_psref_class);
 
 	switch (p->if_type) {
 	case IFT_ETHER:
 	    {
 		struct ethercom *ec = (void *) p;
-
-		ifv->ifv_msw = &vlan_ether_multisw;
-		ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
-		ifv->ifv_mintu = ETHERMIN;
+		nmib->ifvm_msw = &vlan_ether_multisw;
+		nmib->ifvm_encaplen = ETHER_VLAN_ENCAP_LEN;
+		nmib->ifvm_mintu = ETHERMIN;
 
 		if (ec->ec_nvlans++ == 0) {
 			if ((error = ether_enable_vlan_mtu(p)) >= 0) {
 				if (error) {
 					ec->ec_nvlans--;
-					return error;
+					goto done;
 				}
-				ifv->ifv_mtufudge = 0;
+				nmib->ifvm_mtufudge = 0;
 			} else {
 				/*
 				 * Fudge the MTU by the encapsulation size. This
@@ -329,8 +429,9 @@ vlan_config(struct ifvlan *ifv, struct i
 				 * the feature with other NetBSD
 				 * implementations, which might still be useful.
 				 */
-				ifv->ifv_mtufudge = ifv->ifv_encaplen;
+				nmib->ifvm_mtufudge = nmib->ifvm_encaplen;
 			}
+			error = 0;
 		}
 
 		/*
@@ -358,11 +459,13 @@ vlan_config(struct ifvlan *ifv, struct i
 	    }
 
 	default:
-		return (EPROTONOSUPPORT);
+		error = EPROTONOSUPPORT;
+		goto done;
 	}
 
-	ifv->ifv_p = p;
-	ifv->ifv_if.if_mtu = p->if_mtu - ifv->ifv_mtufudge;
+	nmib->ifvm_p = p;
+	nmib->ifvm_tag = tag;
+	ifv->ifv_if.if_mtu = p->if_mtu - nmib->ifvm_mtufudge;
 	ifv->ifv_if.if_flags = p->if_flags &
 	    (IFF_UP | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
 
@@ -372,32 +475,80 @@ vlan_config(struct ifvlan *ifv, struct i
 	 */
 	ifv->ifv_if.if_type = p->if_type;
 
-	return (0);
+	idx = tag_hash_func(tag, ifv_hash.mask);
+
+	mutex_enter(&ifv_hash.lock);
+	PSLIST_WRITER_INSERT_HEAD(&ifv_hash.lists[idx], ifv, ifv_hash);
+	mutex_exit(&ifv_hash.lock);
+
+	vlan_linkmib_update(ifv, nmib);
+	nmib = NULL;
+	nmib_psref = NULL;
+	omib_cleanup = true;
+
+done:
+	mutex_exit(&ifv->ifv_lock);
+
+	if (nmib_psref)
+		psref_target_destroy(nmib_psref, ifvm_psref_class);
+
+	if (nmib)
+		kmem_free(nmib, sizeof(*nmib));
+
+	if (omib_cleanup)
+		kmem_free(omib, sizeof(*omib));
+
+	return error;
 }
 
 /*
- * Unconfigure a VLAN interface.  Must be called at splnet().
+ * Unconfigure a VLAN interface.
  */
 static void
 vlan_unconfig(struct ifnet *ifp)
 {
 	struct ifvlan *ifv = ifp->if_softc;
+	struct ifvlan_linkmib *nmib = NULL;
+	int error;
+
+	nmib = kmem_alloc(sizeof(*nmib), KM_SLEEP);
+
+	mutex_enter(&ifv->ifv_lock);
+	error = vlan_unconfig_locked(ifv, nmib);
+	mutex_exit(&ifv->ifv_lock);
+
+	if (error)
+		kmem_free(nmib, sizeof(*nmib));
+}
+static int
+vlan_unconfig_locked(struct ifvlan *ifv, struct ifvlan_linkmib *nmib)
+{
 	struct ifnet *p;
+	struct ifnet *ifp = &ifv->ifv_if;
+	struct psref_target *nmib_psref = NULL;
+	struct ifvlan_linkmib *omib;
+	int error = 0;
+
+	KASSERT(mutex_owned(&ifv->ifv_lock));
 
-	mutex_enter(&ifv_mtx);
-	p = ifv->ifv_p;
+	omib = ifv->ifv_mib;
+	p = omib->ifvm_p;
 
 	if (p == NULL) {
-		mutex_exit(&ifv_mtx);
-		return;
+		error = -1;
+		goto done;
 	}
 
+	*nmib = *omib;
+	nmib_psref = &nmib->ifvm_psref;
+	psref_target_init(nmib_psref, ifvm_psref_class);
+
 	/*
  	 * Since the interface is being unconfigured, we need to empty the
 	 * list of multicast groups that we may have joined while we were
 	 * alive and remove them from the parent's list also.
 	 */
-	(*ifv->ifv_msw->vmsw_purgemulti)(ifv);
+	(*nmib->ifvm_msw->vmsw_purgemulti)(ifv);
 
 	/* Disconnect from parent. */
 	switch (p->if_type) {
@@ -420,22 +571,156 @@ vlan_unconfig(struct ifnet *ifp)
 #endif
 	}
 
-	ifv->ifv_p = NULL;
+	nmib->ifvm_p = NULL;
 	ifv->ifv_if.if_mtu = 0;
 	ifv->ifv_flags = 0;
 
+	mutex_enter(&ifv_hash.lock);
+	PSLIST_WRITER_REMOVE(ifv, ifv_hash);
+	pserialize_perform(vlan_psz);
+	mutex_exit(&ifv_hash.lock);
+
+	vlan_linkmib_update(ifv, nmib);
+
+	mutex_exit(&ifv->ifv_lock);
+
+	nmib_psref = NULL;
+	kmem_free(omib, sizeof(*omib));
+
 #ifdef INET6
 	/* To delete v6 link local addresses */
 	if (in6_present)
 		in6_ifdetach(ifp);
 #endif
+
 	if ((ifp->if_flags & IFF_PROMISC) != 0)
 		ifpromisc(ifp, 0);
 	if_down(ifp);
 	ifp->if_flags &= ~(IFF_UP|IFF_RUNNING);
 	ifp->if_capabilities = 0;
+	mutex_enter(&ifv->ifv_lock);
+done:
+
+	if (nmib_psref)
+		psref_target_destroy(nmib_psref, ifvm_psref_class);
 
-	mutex_exit(&ifv_mtx);
+	return error;
+}
+
+static void
+vlan_hash_init(void)
+{
+
+	ifv_hash.lists = hashinit(VLAN_TAG_HASH_SIZE, HASH_PSLIST, true,
+	    &ifv_hash.mask);
+}
+
+static int
+vlan_hash_fini(void)
+{
+	int i;
+
+	mutex_enter(&ifv_hash.lock);
+
+	for (i = 0; i < ifv_hash.mask + 1; i++) {
+		if (PSLIST_WRITER_FIRST(&ifv_hash.lists[i], struct ifvlan,
+		    ifv_hash) != NULL) {
+			mutex_exit(&ifv_hash.lock);
+			return EBUSY;
+		}
+	}
+
+	for (i = 0; i < ifv_hash.mask + 1; i++)
+		PSLIST_DESTROY(&ifv_hash.lists[i]);
+
+	mutex_exit(&ifv_hash.lock);
+
+	hashdone(ifv_hash.lists, HASH_PSLIST, ifv_hash.mask);
+
+	ifv_hash.lists = NULL;
+	ifv_hash.mask = 0;
+
+	return 0;
+}
+
+static int
+tag_hash_func(uint16_t tag, u_long mask)
+{
+	uint32_t hash;
+
+	hash = (tag >> 8) ^ tag;
+	hash = (hash >> 2) ^ hash;
+
+	return hash & mask;
+}
+
+static struct ifvlan_linkmib *
+vlan_getref_linkmib(struct ifvlan *sc, struct psref *psref)
+{
+	struct ifvlan_linkmib *mib;
+	int s;
+
+	s = pserialize_read_enter();
+	mib = sc->ifv_mib;
+	if (mib == NULL) {
+		pserialize_read_exit(s);
+		return NULL;
+	}
+	membar_datadep_consumer();
+	psref_acquire(psref, &mib->ifvm_psref, ifvm_psref_class);
+	pserialize_read_exit(s);
+
+	return mib;
+}
+
+static void
+vlan_putref_linkmib(struct ifvlan_linkmib *mib, struct psref *psref)
+{
+	if (mib == NULL)
+		return;
+	psref_release(psref, &mib->ifvm_psref, ifvm_psref_class);
+}
+
+static struct ifvlan_linkmib *
+vlan_lookup_tag_psref(struct ifnet *ifp, uint16_t tag, struct psref *psref)
+{
+	int idx;
+	int s;
+	struct ifvlan *sc;
+
+	idx = tag_hash_func(tag, ifv_hash.mask);
+
+	s = pserialize_read_enter();
+	PSLIST_READER_FOREACH(sc, &ifv_hash.lists[idx], struct ifvlan,
+	    ifv_hash) {
+		struct ifvlan_linkmib *mib = sc->ifv_mib;
+		if (mib == NULL)
+			continue;
+		if (mib->ifvm_tag != tag)
+			continue;
+		if (mib->ifvm_p != ifp)
+			continue;
+
+		psref_acquire(psref, &mib->ifvm_psref, ifvm_psref_class);
+		pserialize_read_exit(s);
+		return mib;
+	}
+	pserialize_read_exit(s);
+	return NULL;
+}
+
+static void
+vlan_linkmib_update(struct ifvlan *ifv, struct ifvlan_linkmib *nmib)
+{
+	struct ifvlan_linkmib *omib = ifv->ifv_mib;
+
+	KASSERT(mutex_owned(&ifv->ifv_lock));
+
+	membar_producer();
+	ifv->ifv_mib = nmib;
+
+	pserialize_perform(vlan_psz);
+	psref_target_destroy(&omib->ifvm_psref, ifvm_psref_class);
 }
 
 /*
@@ -446,38 +731,98 @@ void
 vlan_ifdetach(struct ifnet *p)
 {
 	struct ifvlan *ifv;
-	int s;
+	struct ifvlan_linkmib *mib, **nmibs;
+	struct psref psref;
+	int error;
+	int bound;
+	int i, cnt = 0;
 
-	s = splnet();
+	bound = curlwp_bind();
+	mutex_enter(&ifv_list.lock);
+	LIST_FOREACH(ifv, &ifv_list.list, ifv_list) {
+		mib = vlan_getref_linkmib(ifv, &psref);
+		if (mib == NULL)
+			continue;
 
-	for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
-	     ifv = LIST_NEXT(ifv, ifv_list)) {
-		if (ifv->ifv_p == p)
-			vlan_unconfig(&ifv->ifv_if);
+		if (mib->ifvm_p == p)
+			cnt++;
+
+		vlan_putref_linkmib(mib, &psref);
 	}
+	mutex_exit(&ifv_list.lock);
 
-	splx(s);
+	/*
+	 * The value of "cnt" does not increase while ifv_list.lock
+	 * and ifv->ifv_lock are released here, because the parent
+	 * interface is detaching.
+	 */
+	nmibs = kmem_alloc(sizeof(*nmibs) * cnt, KM_SLEEP);
+	for (i=0; i < cnt; i++) {
+		nmibs[i] = kmem_alloc(sizeof(*nmibs[i]), KM_SLEEP);
+	}
+
+	mutex_enter(&ifv_list.lock);
+
+	i = 0;
+	LIST_FOREACH(ifv, &ifv_list.list, ifv_list) {
+		mutex_enter(&ifv->ifv_lock);
+		if (ifv->ifv_mib->ifvm_p == p) {
+			KASSERTMSG(i < cnt, "no memory for unconfig, parent=%s",
+			    p->if_xname);
+			error = vlan_unconfig_locked(ifv, nmibs[i]);
+			if (!error) {
+				nmibs[i] = NULL;
+				i++;
+			}
+
+		}
+		mutex_exit(&ifv->ifv_lock);
+	}
+
+	mutex_exit(&ifv_list.lock);
+	curlwp_bindx(bound);
+
+	for (i=0; i < cnt; i++) {
+		if (nmibs[i])
+			kmem_free(nmibs[i], sizeof(*nmibs[i]));
+	}
+
+	kmem_free(nmibs, sizeof(*nmibs) * cnt);
+
+	return;
 }
 
 static int
 vlan_set_promisc(struct ifnet *ifp)
 {
 	struct ifvlan *ifv = ifp->if_softc;
+	struct ifvlan_linkmib *mib;
+	struct psref psref;
 	int error = 0;
+	int bound;
+
+	bound = curlwp_bind();
+	mib = vlan_getref_linkmib(ifv, &psref);
+	if (mib == NULL) {
+		curlwp_bindx(bound);
+		return EBUSY;
+	}
 
 	if ((ifp->if_flags & IFF_PROMISC) != 0) {
 		if ((ifv->ifv_flags & IFVF_PROMISC) == 0) {
-			error = ifpromisc(ifv->ifv_p, 1);
+			error = ifpromisc(mib->ifvm_p, 1);
 			if (error == 0)
 				ifv->ifv_flags |= IFVF_PROMISC;
 		}
 	} else {
 		if ((ifv->ifv_flags & IFVF_PROMISC) != 0) {
-			error = ifpromisc(ifv->ifv_p, 0);
+			error = ifpromisc(mib->ifvm_p, 0);
 			if (error == 0)
 				ifv->ifv_flags &= ~IFVF_PROMISC;
 		}
 	}
+	vlan_putref_linkmib(mib, &psref);
+	curlwp_bindx(bound);
 
 	return (error);
 }
@@ -492,20 +837,40 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd
 	struct ifnet *pr;
 	struct ifcapreq *ifcr;
 	struct vlanreq vlr;
-	int s, error = 0;
-
-	s = splnet();
+	struct ifvlan_linkmib *mib;
+	struct psref psref;
+	int error = 0;
+	int bound;
 
 	switch (cmd) {
 	case SIOCSIFMTU:
-		if (ifv->ifv_p == NULL)
+		bound = curlwp_bind();
+		mib = vlan_getref_linkmib(ifv, &psref);
+		if (mib == NULL) {
+			curlwp_bindx(bound);
+			error = EBUSY;
+			break;
+		}
+
+		if (mib->ifvm_p == NULL) {
+			vlan_putref_linkmib(mib, &psref);
+			curlwp_bindx(bound);
 			error = EINVAL;
-		else if (
-		    ifr->ifr_mtu > (ifv->ifv_p->if_mtu - ifv->ifv_mtufudge) ||
-		    ifr->ifr_mtu < (ifv->ifv_mintu - ifv->ifv_mtufudge))
+		} else if (
+		    ifr->ifr_mtu > (mib->ifvm_p->if_mtu - mib->ifvm_mtufudge) ||
+		    ifr->ifr_mtu < (mib->ifvm_mintu - mib->ifvm_mtufudge)) {
+			vlan_putref_linkmib(mib, &psref);
+			curlwp_bindx(bound);
 			error = EINVAL;
-		else if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET)
-			error = 0;
+		} else {
+			vlan_putref_linkmib(mib, &psref);
+			curlwp_bindx(bound);
+
+			error = ifioctl_common(ifp, cmd, data);
+			if (error == ENETRESET)
+					error = 0;
+		}
+
 		break;
 
 	case SIOCSETVLAN:
@@ -516,10 +881,23 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd
 			break;
 		if ((error = copyin(ifr->ifr_data, &vlr, sizeof(vlr))) != 0)
 			break;
+
 		if (vlr.vlr_parent[0] == '\0') {
-			if (ifv->ifv_p != NULL &&
+			bound = curlwp_bind();
+			mib = vlan_getref_linkmib(ifv, &psref);
+			if (mib == NULL) {
+				curlwp_bindx(bound);
+				error = EBUSY;
+				break;
+			}
+
+			if (mib->ifvm_p != NULL &&
 			    (ifp->if_flags & IFF_PROMISC) != 0)
-				error = ifpromisc(ifv->ifv_p, 0);
+				error = ifpromisc(mib->ifvm_p, 0);
+
+			vlan_putref_linkmib(mib, &psref);
+			curlwp_bindx(bound);
+
 			vlan_unconfig(ifp);
 			break;
 		}
@@ -531,9 +909,10 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd
 			error = ENOENT;
 			break;
 		}
-		if ((error = vlan_config(ifv, pr)) != 0)
+		error = vlan_config(ifv, pr, vlr.vlr_tag);
+		if (error != 0) {
 			break;
-		ifv->ifv_tag = vlr.vlr_tag;
+		}
 		ifp->if_flags |= IFF_RUNNING;
 
 		/* Update promiscuous mode, if necessary. */
@@ -542,11 +921,20 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd
 
 	case SIOCGETVLAN:
 		memset(&vlr, 0, sizeof(vlr));
-		if (ifv->ifv_p != NULL) {
+		bound = curlwp_bind();
+		mib = vlan_getref_linkmib(ifv, &psref);
+		if (mib == NULL) {
+			curlwp_bindx(bound);
+			error = EBUSY;
+			break;
+		}
+		if (mib->ifvm_p != NULL) {
 			snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), "%s",
-			    ifv->ifv_p->if_xname);
-			vlr.vlr_tag = ifv->ifv_tag;
+			    mib->ifvm_p->if_xname);
+			vlr.vlr_tag = mib->ifvm_tag;
 		}
+		vlan_putref_linkmib(mib, &psref);
+		curlwp_bindx(bound);
 		error = copyout(&vlr, ifr->ifr_data, sizeof(vlr));
 		break;
 
@@ -557,40 +945,97 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd
 		 * For promiscuous mode, we enable promiscuous mode on
 		 * the parent if we need promiscuous on the VLAN interface.
 		 */
-		if (ifv->ifv_p != NULL)
+		bound = curlwp_bind();
+		mib = vlan_getref_linkmib(ifv, &psref);
+		if (mib == NULL) {
+			curlwp_bindx(bound);
+			error = EBUSY;
+			break;
+		}
+
+		if (mib->ifvm_p != NULL)
 			error = vlan_set_promisc(ifp);
+		vlan_putref_linkmib(mib, &psref);
+		curlwp_bindx(bound);
 		break;
 
 	case SIOCADDMULTI:
-		error = (ifv->ifv_p != NULL) ?
-		    (*ifv->ifv_msw->vmsw_addmulti)(ifv, ifr) : EINVAL;
+		mutex_enter(&ifv->ifv_lock);
+		mib = ifv->ifv_mib;
+		if (mib == NULL) {
+			error = EBUSY;
+			mutex_exit(&ifv->ifv_lock);
+			break;
+		}
+
+		error = (mib->ifvm_p != NULL) ?
+		    (*mib->ifvm_msw->vmsw_addmulti)(ifv, ifr) : EINVAL;
+		mib = NULL;
+		mutex_exit(&ifv->ifv_lock);
 		break;
 
 	case SIOCDELMULTI:
-		error = (ifv->ifv_p != NULL) ?
-		    (*ifv->ifv_msw->vmsw_delmulti)(ifv, ifr) : EINVAL;
+		mutex_enter(&ifv->ifv_lock);
+		mib = ifv->ifv_mib;
+		if (mib == NULL) {
+			error = EBUSY;
+			mutex_exit(&ifv->ifv_lock);
+			break;
+		}
+		error = (mib->ifvm_p != NULL) ?
+		    (*mib->ifvm_msw->vmsw_delmulti)(ifv, ifr) : EINVAL;
+		mib = NULL;
+		mutex_exit(&ifv->ifv_lock);
 		break;
 
 	case SIOCSIFCAP:
 		ifcr = data;
 		/* make sure caps are enabled on parent */
-		if (ifv->ifv_p == NULL) {
+		bound = curlwp_bind();
+		mib = vlan_getref_linkmib(ifv, &psref);
+		if (mib == NULL) {
+			curlwp_bindx(bound);
+			error = EBUSY;
+			break;
+		}
+
+		if (mib->ifvm_p == NULL) {
+			vlan_putref_linkmib(mib, &psref);
+			curlwp_bindx(bound);
 			error = EINVAL;
 			break;
 		}
-		if ((ifv->ifv_p->if_capenable & ifcr->ifcr_capenable) !=
+		if ((mib->ifvm_p->if_capenable & ifcr->ifcr_capenable) !=
 		    ifcr->ifcr_capenable) {
+			vlan_putref_linkmib(mib, &psref);
+			curlwp_bindx(bound);
 			error = EINVAL;
 			break;
 		}
+
+		vlan_putref_linkmib(mib, &psref);
+		curlwp_bindx(bound);
+
 		if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET)
 			error = 0;
 		break;
 	case SIOCINITIFADDR:
-		if (ifv->ifv_p == NULL) {
+		bound = curlwp_bind();
+		mib = vlan_getref_linkmib(ifv, &psref);
+		if (mib == NULL) {
+			curlwp_bindx(bound);
+			error = EBUSY;
+			break;
+		}
+
+		if (mib->ifvm_p == NULL) {
 			error = EINVAL;
+			vlan_putref_linkmib(mib, &psref);
+			curlwp_bindx(bound);
 			break;
 		}
+		vlan_putref_linkmib(mib, &psref);
+		curlwp_bindx(bound);
 
 		ifp->if_flags |= IFF_UP;
 #ifdef INET
@@ -603,8 +1048,6 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd
 		error = ether_ioctl(ifp, cmd, data);
 	}
 
-	splx(s);
-
 	return (error);
 }
 
@@ -614,8 +1057,11 @@ vlan_ether_addmulti(struct ifvlan *ifv, 
 	const struct sockaddr *sa = ifreq_getaddr(SIOCADDMULTI, ifr);
 	struct vlan_mc_entry *mc;
 	uint8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
+	struct ifvlan_linkmib *mib;
 	int error;
 
+	KASSERT(mutex_owned(&ifv->ifv_lock));
+
 	if (sa->sa_len > sizeof(struct sockaddr_storage))
 		return (EINVAL);
 
@@ -643,7 +1089,9 @@ vlan_ether_addmulti(struct ifvlan *ifv, 
 	memcpy(&mc->mc_addr, sa, sa->sa_len);
 	LIST_INSERT_HEAD(&ifv->ifv_mc_listhead, mc, mc_entries);
 
-	error = if_mcast_op(ifv->ifv_p, SIOCADDMULTI, sa);
+	mib = ifv->ifv_mib;
+	error = if_mcast_op(mib->ifvm_p, SIOCADDMULTI, sa);
+
 	if (error != 0)
 		goto ioctl_failed;
 	return (error);
@@ -662,9 +1110,12 @@ vlan_ether_delmulti(struct ifvlan *ifv, 
 	const struct sockaddr *sa = ifreq_getaddr(SIOCDELMULTI, ifr);
 	struct ether_multi *enm;
 	struct vlan_mc_entry *mc;
+	struct ifvlan_linkmib *mib;
 	uint8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
 	int error;
 
+	KASSERT(mutex_owned(&ifv->ifv_lock));
+
 	/*
 	 * Find a key to lookup vlan_mc_entry.  We have to do this
 	 * before calling ether_delmulti for obvious reason.
@@ -678,7 +1129,9 @@ vlan_ether_delmulti(struct ifvlan *ifv, 
 		return (error);
 
 	/* We no longer use this multicast address.  Tell parent so. */
-	error = if_mcast_op(ifv->ifv_p, SIOCDELMULTI, sa);
+	mib = ifv->ifv_mib;
+	error = if_mcast_op(mib->ifvm_p, SIOCDELMULTI, sa);
+
 	if (error == 0) {
 		/* And forget about this address. */
 		for (mc = LIST_FIRST(&ifv->ifv_mc_listhead); mc != NULL;
@@ -702,11 +1155,17 @@ vlan_ether_delmulti(struct ifvlan *ifv, 
 static void
 vlan_ether_purgemulti(struct ifvlan *ifv)
 {
-	struct ifnet *ifp = ifv->ifv_p;		/* Parent. */
 	struct vlan_mc_entry *mc;
+	struct ifvlan_linkmib *mib;
+
+	KASSERT(mutex_owned(&ifv->ifv_lock));
+	mib = ifv->ifv_mib;
+	if (mib == NULL) {
+		return;
+	}
 
 	while ((mc = LIST_FIRST(&ifv->ifv_mc_listhead)) != NULL) {
-		(void)if_mcast_op(ifp, SIOCDELMULTI,
+		(void)if_mcast_op(mib->ifvm_p, SIOCDELMULTI,
 		    (const struct sockaddr *)&mc->mc_addr);
 		LIST_REMOVE(mc, mc_entries);
 		free(mc, M_DEVBUF);
@@ -717,15 +1176,23 @@ static void
 vlan_start(struct ifnet *ifp)
 {
 	struct ifvlan *ifv = ifp->if_softc;
-	struct ifnet *p = ifv->ifv_p;
-	struct ethercom *ec = (void *) ifv->ifv_p;
+	struct ifnet *p;
+	struct ethercom *ec;
 	struct mbuf *m;
+	struct ifvlan_linkmib *mib;
+	struct psref psref;
 	int error;
 
 #ifndef NET_MPSAFE
 	KASSERT(KERNEL_LOCKED_P());
 #endif
 
+	mib = vlan_getref_linkmib(ifv, &psref);
+	if (mib == NULL)
+		return;
+	p = mib->ifvm_p;
+	ec = (void *)mib->ifvm_p;
+
 	ifp->if_flags |= IFF_OACTIVE;
 
 	for (;;) {
@@ -773,16 +1240,16 @@ vlan_start(struct ifnet *ifp)
 				m_freem(m);
 				continue;
 			}
-			*(u_int *)(mtag + 1) = ifv->ifv_tag;
+			*(u_int *)(mtag + 1) = mib->ifvm_tag;
 			m_tag_prepend(m, mtag);
 		} else {
 			/*
 			 * insert the tag ourselves
 			 */
-			M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT);
+			M_PREPEND(m, mib->ifvm_encaplen, M_DONTWAIT);
 			if (m == NULL) {
 				printf("%s: unable to prepend encap header",
-				    ifv->ifv_p->if_xname);
+				    p->if_xname);
 				ifp->if_oerrors++;
 				continue;
 			}
@@ -797,7 +1264,7 @@ vlan_start(struct ifnet *ifp)
 					    sizeof(struct ether_vlan_header));
 				if (m == NULL) {
 					printf("%s: unable to pullup encap "
-					    "header", ifv->ifv_p->if_xname);
+					    "header", p->if_xname);
 					ifp->if_oerrors++;
 					continue;
 				}
@@ -807,12 +1274,12 @@ vlan_start(struct ifnet *ifp)
 				 * Ethernet header with 802.1Q encapsulation.
 				 */
 				memmove(mtod(m, void *),
-				    mtod(m, char *) + ifv->ifv_encaplen,
+				    mtod(m, char *) + mib->ifvm_encaplen,
 				    sizeof(struct ether_header));
 				evl = mtod(m, struct ether_vlan_header *);
 				evl->evl_proto = evl->evl_encap_proto;
 				evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
-				evl->evl_tag = htons(ifv->ifv_tag);
+				evl->evl_tag = htons(mib->ifvm_tag);
 
 				/*
 				 * To cater for VLAN-aware layer 2 ethernet
@@ -858,6 +1325,145 @@ vlan_start(struct ifnet *ifp)
 	}
 
 	ifp->if_flags &= ~IFF_OACTIVE;
+
+	/* Remove reference to mib before release */
+	p = NULL;
+	ec = NULL;
+
+	vlan_putref_linkmib(mib, &psref);
+}
+
+static int
+vlan_transmit(struct ifnet *ifp, struct mbuf *m)
+{
+	struct ifvlan *ifv = ifp->if_softc;
+	struct ifnet *p;
+	struct ethercom *ec;
+	struct ifvlan_linkmib *mib;
+	struct psref psref;
+	int error;
+
+	mib = vlan_getref_linkmib(ifv, &psref);
+	if (mib == NULL) {
+		m_freem(m);
+		return ENETDOWN;
+	}
+
+	p = mib->ifvm_p;
+	ec = (void *)mib->ifvm_p;
+
+	bpf_mtap(ifp, m);
+	/*
+	 * If the parent can insert the tag itself, just mark
+	 * the tag in the mbuf header.
+	 */
+	if (ec->ec_capabilities & ETHERCAP_VLAN_HWTAGGING) {
+		struct m_tag *mtag;
+
+		mtag = m_tag_get(PACKET_TAG_VLAN, sizeof(u_int),
+		    M_NOWAIT);
+		if (mtag == NULL) {
+			ifp->if_oerrors++;
+			m_freem(m);
+			error = ENOBUFS;
+			goto out;
+		}
+		*(u_int *)(mtag + 1) = mib->ifvm_tag;
+		m_tag_prepend(m, mtag);
+	} else {
+		/*
+		 * insert the tag ourselves
+		 */
+		M_PREPEND(m, mib->ifvm_encaplen, M_DONTWAIT);
+		if (m == NULL) {
+			printf("%s: unable to prepend encap header",
+			    p->if_xname);
+			ifp->if_oerrors++;
+			error = ENOBUFS;
+			goto out;
+		}
+
+		switch (p->if_type) {
+		case IFT_ETHER:
+		    {
+			struct ether_vlan_header *evl;
+
+			if (m->m_len < sizeof(struct ether_vlan_header))
+				m = m_pullup(m,
+				    sizeof(struct ether_vlan_header));
+			if (m == NULL) {
+				printf("%s: unable to pullup encap "
+				    "header", p->if_xname);
+				ifp->if_oerrors++;
+				error = ENOBUFS;
+				goto out;
+			}
+
+			/*
+			 * Transform the Ethernet header into an
+			 * Ethernet header with 802.1Q encapsulation.
+			 */
+			memmove(mtod(m, void *),
+			    mtod(m, char *) + mib->ifvm_encaplen,
+			    sizeof(struct ether_header));
+			evl = mtod(m, struct ether_vlan_header *);
+			evl->evl_proto = evl->evl_encap_proto;
+			evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
+			evl->evl_tag = htons(mib->ifvm_tag);
+
+			/*
+			 * To cater for VLAN-aware layer 2 ethernet
+			 * switches which may need to strip the tag
+			 * before forwarding the packet, make sure
+			 * the packet+tag is at least 68 bytes long.
+			 * This is necessary because our parent will
+			 * only pad to 64 bytes (ETHER_MIN_LEN) and
+			 * some switches will not pad by themselves
+			 * after deleting a tag.
+			 */
+			if (m->m_pkthdr.len <
+			    (ETHER_MIN_LEN - ETHER_CRC_LEN +
+			     ETHER_VLAN_ENCAP_LEN)) {
+				m_copyback(m, m->m_pkthdr.len,
+				    (ETHER_MIN_LEN - ETHER_CRC_LEN +
+				     ETHER_VLAN_ENCAP_LEN) -
+				     m->m_pkthdr.len,
+				    vlan_zero_pad_buff);
+			}
+			break;
+		    }
+
+#ifdef DIAGNOSTIC
+		default:
+			panic("vlan_transmit: impossible");
+#endif
+		}
+	}
+
+	if ((p->if_flags & IFF_RUNNING) == 0) {
+		m_freem(m);
+		error = ENETDOWN;
+		goto out;
+	}
+
+	error = if_transmit_lock(p, m);
+	if (error) {
+		/* mbuf is already freed */
+		ifp->if_oerrors++;
+	} else {
+		ifp->if_opackets++;
+		/*
+		 * obytes is incremented at ether_output() or bridge_enqueue().
+		 */
+	}
+
+out:
+	/* Remove reference to mib before release */
+	p = NULL;
+	ec = NULL;
+
+	vlan_putref_linkmib(mib, &psref);
+	return error;
 }
 
 /*
@@ -871,6 +1477,8 @@ vlan_input(struct ifnet *ifp, struct mbu
 	struct ifvlan *ifv;
 	u_int tag;
 	struct m_tag *mtag;
+	struct ifvlan_linkmib *mib;
+	struct psref psref;
 
 	mtag = m_tag_find(m, PACKET_TAG_VLAN, NULL);
 	if (mtag != NULL) {
@@ -912,27 +1520,29 @@ vlan_input(struct ifnet *ifp, struct mbu
 		}
 	}
 
-	for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
-	    ifv = LIST_NEXT(ifv, ifv_list))
-		if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
-			break;
-
-	if (ifv == NULL ||
-	    (ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
-	     (IFF_UP|IFF_RUNNING)) {
+	mib = vlan_lookup_tag_psref(ifp, tag, &psref);
+	if (mib == NULL) {
 		m_freem(m);
 		ifp->if_noproto++;
 		return;
 	}
 
+	ifv = mib->ifvm_ifvlan;
+	if ((ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
+	    (IFF_UP|IFF_RUNNING)) {
+		m_freem(m);
+		ifp->if_noproto++;
+		goto out;
+	}
+
 	/*
 	 * Now, remove the encapsulation header.  The original
 	 * header has already been fixed up above.
 	 */
 	if (mtag == NULL) {
-		memmove(mtod(m, char *) + ifv->ifv_encaplen,
+		memmove(mtod(m, char *) + mib->ifvm_encaplen,
 		    mtod(m, void *), sizeof(struct ether_header));
-		m_adj(m, ifv->ifv_encaplen);
+		m_adj(m, mib->ifvm_encaplen);
 	}
 
 	m_set_rcvif(m, &ifv->ifv_if);
@@ -940,6 +1550,8 @@ vlan_input(struct ifnet *ifp, struct mbu
 
 	m->m_flags &= ~M_PROMISC;
 	if_input(&ifv->ifv_if, m);
+out:
+	vlan_putref_linkmib(mib, &psref);
 }
 
 /*

Index: src/sys/net/if_vlanvar.h
diff -u src/sys/net/if_vlanvar.h:1.9 src/sys/net/if_vlanvar.h:1.9.80.1
--- src/sys/net/if_vlanvar.h:1.9	Mon Apr 28 20:24:09 2008
+++ src/sys/net/if_vlanvar.h	Wed Jun 21 17:39:24 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_vlanvar.h,v 1.9 2008/04/28 20:24:09 martin Exp $	*/
+/*	$NetBSD: if_vlanvar.h,v 1.9.80.1 2017/06/21 17:39:24 snj Exp $	*/
 
 /*-
  * Copyright (c) 2000 The NetBSD Foundation, Inc.
@@ -86,6 +86,37 @@ struct vlanreq {
 #ifdef _KERNEL
 void	vlan_input(struct ifnet *, struct mbuf *);
 void	vlan_ifdetach(struct ifnet *);
+
+/*
+ * Locking notes:
+ * + ifv_list.list is protected by ifv_list.lock (an adaptive mutex)
+ *     ifv_list.list is list of all ifvlans, and it is used to avoid
+ *     unload while busy.
+ * + ifv_hash.lists is protected by
+ *   - ifv_hash.lock (an adaptive mutex) for writer
+ *   - pserialize for reader
+ *     ifv_hash.lists is hashed list of all configured
+ *     vlan interface, and it is used to avoid unload while busy.
+ * + ifvlan->ifv_linkmib is protected by
+ *   - ifvlan->ifv_lock (an adaptive mutex) for writer
+ *   - ifv_linkmib->ifvm_psref for reader
+ *     ifvlan->ifv_linkmib is used for variant values while tagging
+ *     and untagging
+ *
+ * Locking order:
+ *     - ifv_list.lock => struct ifvlan->ifv_lock
+ *     - struct ifvlan->ifv_lock => ifv_hash.lock
+ * Other mutexes must not hold simultaneously
+ *
+ *   NOTICE
+ *     - ifvlan must not have a variant value while tagging and
+ *       untagging. Such variant values must be in ifvlan->ifv_mib
+ *     - ifvlan->ifv_mib is modified like read-copy-update.
+ *       So, once we dereference ifvlan->ifv_mib,
+ *       we must keep the pointer during the same context. If we
+ *       re-dereference ifvlan->ifv_mib, the ifv_mib may be other
+ *       one because of concurrent writer processing.
+ */
 #endif	/* _KERNEL */
 
 #endif	/* !_NET_IF_VLANVAR_H_ */

Reply via email to