Module Name:    src
Committed By:   msaitoh
Date:           Wed Jul 17 03:26:24 UTC 2019

Modified Files:
        src/sbin/ifconfig: ether.c
        src/sys/dev/pci/ixgbe: ixgbe.c ixgbe.h ixv.c
        src/sys/net: if_ether.h if_ethersubr.c if_vlan.c

Log Message:
 Implement VLAN hardware filter function(ETHERCAP_VLAN_HWFILTER).
First proposed by jmcneill in 2017 and modified by me.

How to use:

 - Set callback function:

        ether_set_vlan_cb(struct ethercom *, ether_vlancb_t)

 - Callback. This function is called when a vlan is attached/detached to the
   parent interface:

        int (*ether_vlancb_t)(struct ethercom *ec, uint16_t vlanid, bool set);

 - ifconfig(8)

        ifconfig ixg0 [-]vlan-hwfilter

 Note that ETHERCAP_VLAN_HWFILTER is set by default on ixg(4) because
the PF driver usually enable "all block" filter by default.


To generate a diff of this commit:
cvs rdiff -u -r1.5 -r1.6 src/sbin/ifconfig/ether.c
cvs rdiff -u -r1.192 -r1.193 src/sys/dev/pci/ixgbe/ixgbe.c
cvs rdiff -u -r1.55 -r1.56 src/sys/dev/pci/ixgbe/ixgbe.h
cvs rdiff -u -r1.119 -r1.120 src/sys/dev/pci/ixgbe/ixv.c
cvs rdiff -u -r1.80 -r1.81 src/sys/net/if_ether.h
cvs rdiff -u -r1.275 -r1.276 src/sys/net/if_ethersubr.c
cvs rdiff -u -r1.140 -r1.141 src/sys/net/if_vlan.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sbin/ifconfig/ether.c
diff -u src/sbin/ifconfig/ether.c:1.5 src/sbin/ifconfig/ether.c:1.6
--- src/sbin/ifconfig/ether.c:1.5	Wed Jul 17 03:09:16 2019
+++ src/sbin/ifconfig/ether.c	Wed Jul 17 03:26:24 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: ether.c,v 1.5 2019/07/17 03:09:16 msaitoh Exp $	*/
+/*	$NetBSD: ether.c,v 1.6 2019/07/17 03:26:24 msaitoh Exp $	*/
 
 /*
  * Copyright (c) 1983, 1993
@@ -31,7 +31,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: ether.c,v 1.5 2019/07/17 03:09:16 msaitoh Exp $");
+__RCSID("$NetBSD: ether.c,v 1.6 2019/07/17 03:26:24 msaitoh Exp $");
 #endif /* not lint */
 
 #include <sys/param.h> 
@@ -64,9 +64,7 @@ static cmdloop_branch_t branch;
 #define MAX_PRINT_LEN 55
 
 static const struct kwinst ethercapskw[] = {
-#if 0 /* notyet */
 	IFKW("vlan-hwfilter",	ETHERCAP_VLAN_HWFILTER),
-#endif
 	IFKW("vlan-hwtagging",	ETHERCAP_VLAN_HWTAGGING),
 	IFKW("eee",		ETHERCAP_EEE)
 };

Index: src/sys/dev/pci/ixgbe/ixgbe.c
diff -u src/sys/dev/pci/ixgbe/ixgbe.c:1.192 src/sys/dev/pci/ixgbe/ixgbe.c:1.193
--- src/sys/dev/pci/ixgbe/ixgbe.c:1.192	Thu Jul  4 09:02:24 2019
+++ src/sys/dev/pci/ixgbe/ixgbe.c	Wed Jul 17 03:26:24 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: ixgbe.c,v 1.192 2019/07/04 09:02:24 msaitoh Exp $ */
+/* $NetBSD: ixgbe.c,v 1.193 2019/07/17 03:26:24 msaitoh Exp $ */
 
 /******************************************************************************
 
@@ -220,10 +220,9 @@ static u8 *	ixgbe_mc_array_itr(struct ix
 static void	ixgbe_eitr_write(struct adapter *, uint32_t, uint32_t);
 
 static void	ixgbe_setup_vlan_hw_support(struct adapter *);
-#if 0
-static void	ixgbe_register_vlan(void *, struct ifnet *, u16);
-static void	ixgbe_unregister_vlan(void *, struct ifnet *, u16);
-#endif
+static int	ixgbe_vlan_cb(struct ethercom *, uint16_t, bool);
+static int	ixgbe_register_vlan(void *, struct ifnet *, u16);
+static int	ixgbe_unregister_vlan(void *, struct ifnet *, u16);
 
 static void	ixgbe_add_device_sysctls(struct adapter *);
 static void	ixgbe_add_hw_stats(struct adapter *);
@@ -905,6 +904,9 @@ ixgbe_attach(device_t parent, device_t d
 	/* Enable WoL (if supported) */
 	ixgbe_check_wol_support(adapter);
 
+	/* Register for VLAN events */
+	ether_set_vlan_cb(&adapter->osdep.ec, ixgbe_vlan_cb);
+
 	/* Verify adapter fan is still functional (if applicable) */
 	if (adapter->feat_en & IXGBE_FEATURE_FAN_FAIL) {
 		u32 esdp = IXGBE_READ_REG(hw, IXGBE_ESDP);
@@ -2299,7 +2301,20 @@ ixgbe_sysctl_rdt_handler(SYSCTLFN_ARGS)
 	return sysctl_lookup(SYSCTLFN_CALL(&node));
 } /* ixgbe_sysctl_rdt_handler */
 
-#if 0	/* XXX Badly need to overhaul vlan(4) on NetBSD. */
+static int
+ixgbe_vlan_cb(struct ethercom *ec, uint16_t vid, bool set)
+{
+	struct ifnet *ifp = &ec->ec_if;
+	int rv;
+
+	if (set)
+		rv = ixgbe_register_vlan(ifp->if_softc, ifp, vid);
+	else
+		rv = ixgbe_unregister_vlan(ifp->if_softc, ifp, vid);
+
+	return rv;
+}
+
 /************************************************************************
  * ixgbe_register_vlan
  *
@@ -2308,24 +2323,30 @@ ixgbe_sysctl_rdt_handler(SYSCTLFN_ARGS)
  *   just creates the entry in the soft version of the
  *   VFTA, init will repopulate the real table.
  ************************************************************************/
-static void
+static int
 ixgbe_register_vlan(void *arg, struct ifnet *ifp, u16 vtag)
 {
 	struct adapter	*adapter = ifp->if_softc;
 	u16		index, bit;
+	int		error;
 
 	if (ifp->if_softc != arg)   /* Not our event */
-		return;
+		return EINVAL;
 
 	if ((vtag == 0) || (vtag > 4095))	/* Invalid */
-		return;
+		return EINVAL;
 
 	IXGBE_CORE_LOCK(adapter);
 	index = (vtag >> 5) & 0x7F;
 	bit = vtag & 0x1F;
 	adapter->shadow_vfta[index] |= (1 << bit);
-	ixgbe_setup_vlan_hw_support(adapter);
+	error = adapter->hw.mac.ops.set_vfta(&adapter->hw, vtag, 0, true,
+	    true);
 	IXGBE_CORE_UNLOCK(adapter);
+	if (error != 0)
+		error = EACCES;
+
+	return error;
 } /* ixgbe_register_vlan */
 
 /************************************************************************
@@ -2333,27 +2354,31 @@ ixgbe_register_vlan(void *arg, struct if
  *
  *   Run via vlan unconfig EVENT, remove our entry in the soft vfta.
  ************************************************************************/
-static void
+static int
 ixgbe_unregister_vlan(void *arg, struct ifnet *ifp, u16 vtag)
 {
 	struct adapter	*adapter = ifp->if_softc;
 	u16		index, bit;
+	int		error;
 
 	if (ifp->if_softc != arg)
-		return;
+		return EINVAL;
 
 	if ((vtag == 0) || (vtag > 4095))	/* Invalid */
-		return;
+		return EINVAL;
 
 	IXGBE_CORE_LOCK(adapter);
 	index = (vtag >> 5) & 0x7F;
 	bit = vtag & 0x1F;
 	adapter->shadow_vfta[index] &= ~(1 << bit);
-	/* Re-init to load the changes */
-	ixgbe_setup_vlan_hw_support(adapter);
+	error = adapter->hw.mac.ops.set_vfta(&adapter->hw, vtag, 0, false,
+	    true);
 	IXGBE_CORE_UNLOCK(adapter);
+	if (error != 0)
+		error = EACCES;
+
+	return error;
 } /* ixgbe_unregister_vlan */
-#endif
 
 static void
 ixgbe_setup_vlan_hw_support(struct adapter *adapter)
@@ -2363,6 +2388,7 @@ ixgbe_setup_vlan_hw_support(struct adapt
 	struct rx_ring	*rxr;
 	int		i;
 	u32		ctrl;
+	struct vlanid_list *vlanidp;
 	bool		hwtagging;
 
 	/*
@@ -2391,14 +2417,21 @@ ixgbe_setup_vlan_hw_support(struct adapt
 		rxr->vtag_strip = hwtagging ? TRUE : FALSE;
 	}
 
-	/*
-	 * A soft reset zero's out the VFTA, so
-	 * we need to repopulate it now.
-	 */
+	/* Cleanup shadow_vfta */
+	for (i = 0; i < IXGBE_VFTA_SIZE; i++)
+		adapter->shadow_vfta[i] = 0;
+	/* Generate shadow_vfta from ec_vids */
+	mutex_enter(ec->ec_lock);
+	SIMPLEQ_FOREACH(vlanidp, &ec->ec_vids, vid_list) {
+		uint32_t idx;
+
+		idx = vlanidp->vid / 32;
+		KASSERT(idx < IXGBE_VFTA_SIZE);
+		adapter->shadow_vfta[idx] |= 1 << vlanidp->vid % 32;
+	}
+	mutex_exit(ec->ec_lock);
 	for (i = 0; i < IXGBE_VFTA_SIZE; i++)
-		if (adapter->shadow_vfta[i] != 0)
-			IXGBE_WRITE_REG(hw, IXGBE_VFTA(i),
-			    adapter->shadow_vfta[i]);
+		IXGBE_WRITE_REG(hw, IXGBE_VFTA(i), adapter->shadow_vfta[i]);
 
 	ctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
 	/* Enable the Filter Table if enabled */
@@ -4107,6 +4140,7 @@ ixgbe_init_locked(struct adapter *adapte
 
 	/* Update saved flags. See ixgbe_ifflags_cb() */
 	adapter->if_flags = ifp->if_flags;
+	adapter->ec_capenable = adapter->osdep.ec.ec_capenable;
 
 	/* Now inform the stack we're ready */
 	ifp->if_flags |= IFF_RUNNING;
@@ -6151,8 +6185,23 @@ ixgbe_ifflags_cb(struct ethercom *ec)
 	} else if ((change & IFF_PROMISC) != 0)
 		ixgbe_set_promisc(adapter);
 
+	/* Check for ec_capenable. */
+	change = ec->ec_capenable ^ adapter->ec_capenable;
+	adapter->ec_capenable = ec->ec_capenable;
+	if ((change & ~(ETHERCAP_VLAN_MTU | ETHERCAP_VLAN_HWTAGGING
+	    | ETHERCAP_VLAN_HWFILTER)) != 0) {
+		rv = ENETRESET;
+		goto out;
+	}
+
+	/*
+	 * Special handling is not required for ETHERCAP_VLAN_MTU.
+	 * MAXFRS(MHADD) does not include the 4bytes of the VLAN header.
+	 */
+
 	/* Set up VLAN support and filter */
-	ixgbe_setup_vlan_hw_support(adapter);
+	if ((change & (ETHERCAP_VLAN_HWTAGGING | ETHERCAP_VLAN_HWFILTER)) != 0)
+		ixgbe_setup_vlan_hw_support(adapter);
 
 out:
 	IXGBE_CORE_UNLOCK(adapter);

Index: src/sys/dev/pci/ixgbe/ixgbe.h
diff -u src/sys/dev/pci/ixgbe/ixgbe.h:1.55 src/sys/dev/pci/ixgbe/ixgbe.h:1.56
--- src/sys/dev/pci/ixgbe/ixgbe.h:1.55	Thu Jun 27 05:55:40 2019
+++ src/sys/dev/pci/ixgbe/ixgbe.h	Wed Jul 17 03:26:24 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: ixgbe.h,v 1.55 2019/06/27 05:55:40 msaitoh Exp $ */
+/* $NetBSD: ixgbe.h,v 1.56 2019/07/17 03:26:24 msaitoh Exp $ */
 
 /******************************************************************************
   SPDX-License-Identifier: BSD-3-Clause
@@ -475,7 +475,8 @@ struct adapter {
 
 	struct ifmedia		media;
 	callout_t		timer;
-	int			if_flags;
+	int			if_flags;	/* saved ifp->if_flags */
+	int			ec_capenable;	/* saved ec->ec_capenable */
 
 	kmutex_t		core_mtx;
 

Index: src/sys/dev/pci/ixgbe/ixv.c
diff -u src/sys/dev/pci/ixgbe/ixv.c:1.119 src/sys/dev/pci/ixgbe/ixv.c:1.120
--- src/sys/dev/pci/ixgbe/ixv.c:1.119	Wed Jul 17 03:09:16 2019
+++ src/sys/dev/pci/ixgbe/ixv.c	Wed Jul 17 03:26:24 2019
@@ -1,4 +1,4 @@
-/*$NetBSD: ixv.c,v 1.119 2019/07/17 03:09:16 msaitoh Exp $*/
+/*$NetBSD: ixv.c,v 1.120 2019/07/17 03:26:24 msaitoh Exp $*/
 
 /******************************************************************************
 
@@ -120,11 +120,10 @@ static void	ixv_configure_ivars(struct a
 static u8 *	ixv_mc_array_itr(struct ixgbe_hw *, u8 **, u32 *);
 static void	ixv_eitr_write(struct adapter *, uint32_t, uint32_t);
 
-static void	ixv_setup_vlan_support(struct adapter *);
-#if 0
-static void	ixv_register_vlan(void *, struct ifnet *, u16);
-static void	ixv_unregister_vlan(void *, struct ifnet *, u16);
-#endif
+static int	ixv_setup_vlan_support(struct adapter *);
+static int	ixv_vlan_cb(struct ethercom *, uint16_t, bool);
+static int	ixv_register_vlan(void *, struct ifnet *, u16);
+static int	ixv_unregister_vlan(void *, struct ifnet *, u16);
 
 static void	ixv_add_device_sysctls(struct adapter *);
 static void	ixv_save_stats(struct adapter *);
@@ -471,12 +470,7 @@ ixv_attach(device_t parent, device_t dev
 	}
 
 	/* Register for VLAN events */
-#if 0 /* XXX delete after write? */
-	adapter->vlan_attach = EVENTHANDLER_REGISTER(vlan_config,
-	    ixv_register_vlan, adapter, EVENTHANDLER_PRI_FIRST);
-	adapter->vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig,
-	    ixv_unregister_vlan, adapter, EVENTHANDLER_PRI_FIRST);
-#endif
+	ether_set_vlan_cb(&adapter->osdep.ec, ixv_vlan_cb);
 
 	/* Sysctls for limiting the amount of work done in the taskqueues */
 	ixv_set_sysctl_value(adapter, "rx_processing_limit",
@@ -618,14 +612,6 @@ ixv_detach(device_t dev, int flags)
 	/* Drain the Mailbox(link) queue */
 	softint_disestablish(adapter->link_si);
 
-	/* Unregister VLAN events */
-#if 0 /* XXX msaitoh delete after write? */
-	if (adapter->vlan_attach != NULL)
-		EVENTHANDLER_DEREGISTER(vlan_config, adapter->vlan_attach);
-	if (adapter->vlan_detach != NULL)
-		EVENTHANDLER_DEREGISTER(vlan_unconfig, adapter->vlan_detach);
-#endif
-
 	ether_ifdetach(adapter->ifp);
 	callout_halt(&adapter->timer, NULL);
 
@@ -823,6 +809,7 @@ ixv_init_locked(struct adapter *adapter)
 
 	/* Update saved flags. See ixgbe_ifflags_cb() */
 	adapter->if_flags = ifp->if_flags;
+	adapter->ec_capenable = adapter->osdep.ec.ec_capenable;
 
 	/* Now inform the stack we're ready */
 	ifp->if_flags |= IFF_RUNNING;
@@ -1557,7 +1544,8 @@ ixv_setup_interface(device_t dev, struct
 			     |	IFCAP_TSOv6;
 	ifp->if_capenable = 0;
 
-	ec->ec_capabilities |= ETHERCAP_VLAN_HWTAGGING
+	ec->ec_capabilities |= ETHERCAP_VLAN_HWFILTER
+			    |  ETHERCAP_VLAN_HWTAGGING
 			    |  ETHERCAP_VLAN_HWCSUM
 			    |  ETHERCAP_JUMBO_MTU
 			    |  ETHERCAP_VLAN_MTU;
@@ -1954,19 +1942,23 @@ ixv_sysctl_rdt_handler(SYSCTLFN_ARGS)
 /************************************************************************
  * ixv_setup_vlan_support
  ************************************************************************/
-static void
+static int
 ixv_setup_vlan_support(struct adapter *adapter)
 {
 	struct ethercom *ec = &adapter->osdep.ec;
 	struct ixgbe_hw *hw = &adapter->hw;
 	struct rx_ring	*rxr;
 	u32		ctrl, vid, vfta, retry;
+	struct vlanid_list *vlanidp;
+	int rv, error = 0;
+	bool usevlan;
 	bool		hwtagging;
 
 	/*
 	 *  This function is called from both if_init and ifflags_cb()
 	 * on NetBSD.
 	 */
+	usevlan = VLAN_ATTACHED(ec);
 
 	/* Enable HW tagging only if any vlan is attached */
 	hwtagging = (ec->ec_capenable & ETHERCAP_VLAN_HWTAGGING)
@@ -1988,11 +1980,23 @@ ixv_setup_vlan_support(struct adapter *a
 		rxr->vtag_strip = hwtagging ? TRUE : FALSE;
 	}
 
-	/* XXX dirty hack. Enable all VIDs if any VLAN is attached */
-	for (int i = 0; i < IXGBE_VFTA_SIZE; i++)
-		adapter->shadow_vfta[i]
-		    = VLAN_ATTACHED(&adapter->osdep.ec) ? 0xffffffff : 0;
+	if (!usevlan)
+		return 0;
 
+	/* Cleanup shadow_vfta */
+	for (int i = 0; i < IXGBE_VFTA_SIZE; i++)
+		adapter->shadow_vfta[i] = 0;
+	/* Generate shadow_vfta from ec_vids */
+	mutex_enter(ec->ec_lock);
+	SIMPLEQ_FOREACH(vlanidp, &ec->ec_vids, vid_list) {
+		uint32_t idx;
+
+		idx = vlanidp->vid / 32;
+		KASSERT(idx < IXGBE_VFTA_SIZE);
+		adapter->shadow_vfta[idx] |= 1 << vlanidp->vid % 32;
+	}
+	mutex_exit(ec->ec_lock);
+	
 	/*
 	 * A soft reset zero's out the VFTA, so
 	 * we need to repopulate it now.
@@ -2011,16 +2015,41 @@ ixv_setup_vlan_support(struct adapter *a
 			if ((vfta & (1 << j)) == 0)
 				continue;
 			vid = (i * 32) + j;
+			
 			/* Call the shared code mailbox routine */
-			while (hw->mac.ops.set_vfta(hw, vid, 0, TRUE, FALSE)) {
-				if (++retry > 5)
+			while ((rv = hw->mac.ops.set_vfta(hw, vid, 0, TRUE,
+			    FALSE)) != 0) {
+				if (++retry > 5) {
+					device_printf(adapter->dev,
+					    "%s: max retry exceeded\n",
+						__func__);
 					break;
+				}
+			}
+			if (rv != 0) {
+				device_printf(adapter->dev,
+				    "failed to set vlan %d\n", vid);
+				error = EACCES;
 			}
 		}
 	}
+	return error;
 } /* ixv_setup_vlan_support */
 
-#if 0	/* XXX Badly need to overhaul vlan(4) on NetBSD. */
+static int
+ixv_vlan_cb(struct ethercom *ec, uint16_t vid, bool set)
+{
+	struct ifnet *ifp = &ec->ec_if;
+	int rv;
+
+	if (set)
+		rv = ixv_register_vlan(ifp->if_softc, ifp, vid);
+	else
+		rv = ixv_unregister_vlan(ifp->if_softc, ifp, vid);
+
+	return rv;
+}
+
 /************************************************************************
  * ixv_register_vlan
  *
@@ -2029,25 +2058,32 @@ ixv_setup_vlan_support(struct adapter *a
  *   creates the entry in the soft version of the VFTA, init
  *   will repopulate the real table.
  ************************************************************************/
-static void
+static int
 ixv_register_vlan(void *arg, struct ifnet *ifp, u16 vtag)
 {
 	struct adapter	*adapter = ifp->if_softc;
+	struct ixgbe_hw *hw = &adapter->hw;
 	u16		index, bit;
+	int error;
 
 	if (ifp->if_softc != arg) /* Not our event */
-		return;
+		return EINVAL;
 
 	if ((vtag == 0) || (vtag > 4095)) /* Invalid */
-		return;
-
+		return EINVAL;
 	IXGBE_CORE_LOCK(adapter);
 	index = (vtag >> 5) & 0x7F;
 	bit = vtag & 0x1F;
 	adapter->shadow_vfta[index] |= (1 << bit);
-	/* Re-init to load the changes */
-	ixv_init_locked(adapter);
+	error = hw->mac.ops.set_vfta(hw, vtag, 0, true, false);
 	IXGBE_CORE_UNLOCK(adapter);
+
+	if (error != 0) {
+		device_printf(adapter->dev, "failed to register vlan %hu\n",
+		    vtag);
+		error = EACCES;
+	}
+	return error;
 } /* ixv_register_vlan */
 
 /************************************************************************
@@ -2056,27 +2092,34 @@ ixv_register_vlan(void *arg, struct ifne
  *   Run via a vlan unconfig EVENT, remove our entry
  *   in the soft vfta.
  ************************************************************************/
-static void
+static int
 ixv_unregister_vlan(void *arg, struct ifnet *ifp, u16 vtag)
 {
 	struct adapter	*adapter = ifp->if_softc;
+	struct ixgbe_hw *hw = &adapter->hw;
 	u16		index, bit;
+	int 		error;
 
 	if (ifp->if_softc !=  arg)
-		return;
+		return EINVAL;
 
 	if ((vtag == 0) || (vtag > 4095))  /* Invalid */
-		return;
+		return EINVAL;
 
 	IXGBE_CORE_LOCK(adapter);
 	index = (vtag >> 5) & 0x7F;
 	bit = vtag & 0x1F;
 	adapter->shadow_vfta[index] &= ~(1 << bit);
-	/* Re-init to load the changes */
-	ixv_init_locked(adapter);
+	error = hw->mac.ops.set_vfta(hw, vtag, 0, false, false);
 	IXGBE_CORE_UNLOCK(adapter);
+
+	if (error != 0) {
+		device_printf(adapter->dev, "failed to unregister vlan %hu\n",
+		    vtag);
+		error = EIO;
+	}
+	return error;
 } /* ixv_unregister_vlan */
-#endif
 
 /************************************************************************
  * ixv_enable_intr
@@ -2742,8 +2785,23 @@ ixv_ifflags_cb(struct ethercom *ec)
 		goto out;
 	}
 
+	/* Check for ec_capenable. */
+	change = ec->ec_capenable ^ adapter->ec_capenable;
+	adapter->ec_capenable = ec->ec_capenable;
+	if ((change & ~(ETHERCAP_VLAN_MTU | ETHERCAP_VLAN_HWTAGGING
+	    | ETHERCAP_VLAN_HWFILTER)) != 0) {
+		rv = ENETRESET;
+		goto out;
+	}
+
+	/*
+	 * Special handling is not required for ETHERCAP_VLAN_MTU.
+	 * PF's MAXFRS(MHADD) does not include the 4bytes of the VLAN header.
+	 */
+
 	/* Set up VLAN support and filter */
-	ixv_setup_vlan_support(adapter);
+	if ((change & (ETHERCAP_VLAN_HWTAGGING | ETHERCAP_VLAN_HWFILTER)) != 0)
+		rv = ixv_setup_vlan_support(adapter);
 
 out:
 	IXGBE_CORE_UNLOCK(adapter);

Index: src/sys/net/if_ether.h
diff -u src/sys/net/if_ether.h:1.80 src/sys/net/if_ether.h:1.81
--- src/sys/net/if_ether.h:1.80	Wed Jul 17 03:09:16 2019
+++ src/sys/net/if_ether.h	Wed Jul 17 03:26:24 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_ether.h,v 1.80 2019/07/17 03:09:16 msaitoh Exp $	*/
+/*	$NetBSD: if_ether.h,v 1.81 2019/07/17 03:26:24 msaitoh Exp $	*/
 
 /*
  * Copyright (c) 1982, 1986, 1993
@@ -163,6 +163,7 @@ struct mii_data;
 struct ethercom;
 
 typedef int (*ether_cb_t)(struct ethercom *);
+typedef int (*ether_vlancb_t)(struct ethercom *, uint16_t, bool);
 
 /*
  * Structure shared between the ethernet driver modules and
@@ -181,6 +182,7 @@ struct ethercom {
 						   capabilities to enable */
 
 	int	ec_nvlans;			/* # VLANs on this interface */
+	SIMPLEQ_HEAD(, vlanid_list) ec_vids;	/* list of VLAN IDs */
 	/* The device handle for the MII bus child device. */
 	struct mii_data				*ec_mii;
 	struct ifmedia				*ec_ifmedia;
@@ -190,6 +192,12 @@ struct ethercom {
 	 * ec_if.if_init, 0 on success, not 0 on failure.
 	 */
 	ether_cb_t				ec_ifflags_cb;
+	/*
+	 * Called whenever a vlan interface is configured or unconfigured.
+	 * Args include the vlan tag and a flag indicating whether the tag is
+	 * being added or removed.
+	 */
+	ether_vlancb_t				ec_vlan_cb;
 	kmutex_t				*ec_lock;
 	/* Flags used only by the kernel */
 	int					ec_flags;
@@ -241,6 +249,7 @@ extern const uint8_t ether_ipmulticast_m
 extern const uint8_t ether_ipmulticast_max[ETHER_ADDR_LEN];
 
 void	ether_set_ifflags_cb(struct ethercom *, ether_cb_t);
+void	ether_set_vlan_cb(struct ethercom *, ether_vlancb_t);
 int	ether_ioctl(struct ifnet *, u_long, void *);
 int	ether_addmulti(const struct sockaddr *, struct ethercom *);
 int	ether_delmulti(const struct sockaddr *, struct ethercom *);
@@ -336,6 +345,12 @@ ether_first_multi(struct ether_multistep
  * Ethernet 802.1Q VLAN structures.
  */
 
+/* for ethercom */
+struct vlanid_list {
+	uint16_t vid;
+	SIMPLEQ_ENTRY(vlanid_list) vid_list;
+};
+
 /* add VLAN tag to input/received packet */
 static __inline void
 vlan_set_tag(struct mbuf *m, uint16_t vlantag)

Index: src/sys/net/if_ethersubr.c
diff -u src/sys/net/if_ethersubr.c:1.275 src/sys/net/if_ethersubr.c:1.276
--- src/sys/net/if_ethersubr.c:1.275	Wed May 29 10:07:30 2019
+++ src/sys/net/if_ethersubr.c	Wed Jul 17 03:26:24 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_ethersubr.c,v 1.275 2019/05/29 10:07:30 msaitoh Exp $	*/
+/*	$NetBSD: if_ethersubr.c,v 1.276 2019/07/17 03:26:24 msaitoh Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -61,7 +61,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_ethersubr.c,v 1.275 2019/05/29 10:07:30 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_ethersubr.c,v 1.276 2019/07/17 03:26:24 msaitoh Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -996,6 +996,7 @@ ether_ifattach(struct ifnet *ifp, const 
 		if_set_sadl(ifp, lla, ETHER_ADDR_LEN, !ETHER_IS_LOCAL(lla));
 
 	LIST_INIT(&ec->ec_multiaddrs);
+	SIMPLEQ_INIT(&ec->ec_vids);
 	ec->ec_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
 	ec->ec_flags = 0;
 	ifp->if_broadcastaddr = etherbroadcastaddr;
@@ -1042,6 +1043,7 @@ ether_ifdetach(struct ifnet *ifp)
 #endif
 
 	ETHER_LOCK(ec);
+	KASSERT(ec->ec_nvlans == 0);
 	while ((enm = LIST_FIRST(&ec->ec_multiaddrs)) != NULL) {
 		LIST_REMOVE(enm, enm_list);
 		kmem_free(enm, sizeof(*enm));
@@ -1372,6 +1374,13 @@ ether_set_ifflags_cb(struct ethercom *ec
 	ec->ec_ifflags_cb = cb;
 }
 
+void
+ether_set_vlan_cb(struct ethercom *ec, ether_vlancb_t cb)
+{
+
+	ec->ec_vlan_cb = cb;
+}
+
 static int
 ether_ioctl_reinit(struct ethercom *ec)
 {

Index: src/sys/net/if_vlan.c
diff -u src/sys/net/if_vlan.c:1.140 src/sys/net/if_vlan.c:1.141
--- src/sys/net/if_vlan.c:1.140	Wed Jul 17 03:09:16 2019
+++ src/sys/net/if_vlan.c	Wed Jul 17 03:26:24 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_vlan.c,v 1.140 2019/07/17 03:09:16 msaitoh Exp $	*/
+/*	$NetBSD: if_vlan.c,v 1.141 2019/07/17 03:26:24 msaitoh 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.140 2019/07/17 03:09:16 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_vlan.c,v 1.141 2019/07/17 03:26:24 msaitoh Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -130,15 +130,10 @@ struct vlan_mc_entry {
 	 * used since multiple sockaddr may mapped into the same
 	 * ether_multi (e.g., AF_UNSPEC).
 	 */
-	union {
-		struct ether_multi	*mcu_enm;
-	} mc_u;
+	struct ether_multi	*mc_enm;
 	struct sockaddr_storage		mc_addr;
 };
 
-#define	mc_enm		mc_u.mcu_enm
-
-
 struct ifvlan_linkmib {
 	struct ifvlan *ifvm_ifvlan;
 	const struct vlan_multisw *ifvm_msw;
@@ -153,9 +148,7 @@ struct ifvlan_linkmib {
 };
 
 struct ifvlan {
-	union {
-		struct ethercom ifvu_ec;
-	} ifv_u;
+	struct ethercom ifv_ec;
 	struct ifvlan_linkmib *ifv_mib;	/*
 					 * reader must use vlan_getref_linkmib()
 					 * instead of direct dereference
@@ -171,8 +164,6 @@ struct ifvlan {
 
 #define	IFVF_PROMISC	0x01		/* promiscuous mode enabled */
 
-#define	ifv_ec		ifv_u.ifvu_ec
-
 #define	ifv_if		ifv_ec.ec_if
 
 #define	ifv_msw		ifv_mib.ifvm_msw
@@ -466,6 +457,8 @@ vlan_config(struct ifvlan *ifv, struct i
 	case IFT_ETHER:
 	    {
 		struct ethercom *ec = (void *)p;
+		struct vlanid_list *vidmem;
+
 		nmib->ifvm_msw = &vlan_ether_multisw;
 		nmib->ifvm_encaplen = ETHER_VLAN_ENCAP_LEN;
 		nmib->ifvm_mintu = ETHERMIN;
@@ -492,7 +485,36 @@ vlan_config(struct ifvlan *ifv, struct i
 			}
 			error = 0;
 		}
-
+		/*
+		 * Add a vid to the list even if it's not enabled in case
+		 * it's enabled later.
+		 */
+		if (ec->ec_capabilities & ETHERCAP_VLAN_HWFILTER) {
+			vidmem = kmem_alloc(sizeof(struct vlanid_list),
+			    KM_SLEEP);
+			if (vidmem == NULL){
+				ec->ec_nvlans--;
+				if (ec->ec_nvlans == 0)
+					(void)ether_disable_vlan_mtu(p);
+				error = ENOMEM;
+				goto done;
+			}
+			vidmem->vid = vid;
+			mutex_enter(ec->ec_lock);
+			SIMPLEQ_INSERT_TAIL(&ec->ec_vids, vidmem, vid_list);
+			mutex_exit(ec->ec_lock);
+		}
+		if (ec->ec_capenable & ETHERCAP_VLAN_HWFILTER) {
+			if (ec->ec_vlan_cb != NULL) {
+				error = (*ec->ec_vlan_cb)(ec, vid, true);
+				if (error) {
+					ec->ec_nvlans--;
+					if (ec->ec_nvlans == 0)
+						(void)ether_disable_vlan_mtu(p);
+					goto done;
+				}
+			}
+		}
 		/*
 		 * If the parent interface can do hardware-assisted
 		 * VLAN encapsulation, then propagate its hardware-
@@ -618,6 +640,20 @@ vlan_unconfig_locked(struct ifvlan *ifv,
 	case IFT_ETHER:
 	    {
 		struct ethercom *ec = (void *)p;
+		struct vlanid_list *vlanidp, *tmpp;
+		uint16_t vid = EVL_VLANOFTAG(nmib->ifvm_tag);
+
+		mutex_enter(ec->ec_lock);
+		SIMPLEQ_FOREACH_SAFE(vlanidp, &ec->ec_vids, vid_list, tmpp) {
+			if (vlanidp->vid == vid) {
+				SIMPLEQ_REMOVE(&ec->ec_vids, vlanidp,
+				    vlanid_list, vid_list);
+				kmem_free(vlanidp, sizeof(*vlanidp));
+			}
+		}
+		mutex_exit(ec->ec_lock);
+		if (ec->ec_vlan_cb != NULL)
+			(void)(*ec->ec_vlan_cb)(ec, vid, false);
 		if (--ec->ec_nvlans == 0) {
 			IFNET_LOCK(p);
 			(void)ether_disable_vlan_mtu(p);
@@ -999,6 +1035,7 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd
 			error = ENOENT;
 			break;
 		}
+		
 		error = vlan_config(ifv, pr, vlr.vlr_tag);
 		if (error != 0)
 			break;

Reply via email to