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.
This commit is contained in:
msaitoh 2019-07-17 03:26:24 +00:00
parent cb1ccde89c
commit eea2ee1212
7 changed files with 256 additions and 89 deletions

View File

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

View File

@ -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 ixgbe_hw *, u8 **, u32 *);
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 dev, void *aux)
/* 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 ifnet *ifp, u16 vtag)
*
* 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 adapter *adapter)
struct rx_ring *rxr;
int i;
u32 ctrl;
struct vlanid_list *vlanidp;
bool hwtagging;
/*
@ -2391,14 +2417,21 @@ ixgbe_setup_vlan_hw_support(struct adapter *adapter)
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++)
if (adapter->shadow_vfta[i] != 0)
IXGBE_WRITE_REG(hw, IXGBE_VFTA(i),
adapter->shadow_vfta[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++)
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 *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;
@ -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);

View File

@ -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;

View File

@ -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 adapter *);
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, void *aux)
}
/* 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 adapter *adapter)
| 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 *adapter)
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 *adapter)
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 *adapter)
* 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 ifnet *ifp, u16 vtag)
* 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);

View File

@ -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_min[ETHER_ADDR_LEN];
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 *step, const struct ethercom *ec)
* 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)

View File

@ -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 uint8_t *lla)
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, ether_cb_t cb)
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)
{

View File

@ -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 ifnet *p, uint16_t tag)
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 ifnet *p, uint16_t tag)
}
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, struct ifvlan_linkmib *nmib)
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, void *data)
error = ENOENT;
break;
}
error = vlan_config(ifv, pr, vlr.vlr_tag);
if (error != 0)
break;