Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 07 Aug 2026 01:47:43 +0000
From:      Kevin Bowling <kbowling@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: a81f97aecbfd - main - ixgbe: preserve VLAN ownership with SR-IOV
Message-ID:  <6a75393f.3bd1c.196cc903@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by kbowling:

URL: https://cgit.FreeBSD.org/src/commit/?id=a81f97aecbfda71fe0b423678732e863571793e2

commit a81f97aecbfda71fe0b423678732e863571793e2
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-07-31 12:47:03 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-07 01:36:29 +0000

    ixgbe: preserve VLAN ownership with SR-IOV
    
    The VF VLAN capability is checked but never granted, and no SR-IOV
    configuration property exposes the existing default-VLAN support. PF
    VLAN updates also replace VFTA registers from a PF-only shadow, erasing
    live VF filters.
    
    Expose access VLAN and trunk policy through the IOV schema. Track each
    VF VLAN as desired state, restore the administrative VLAN after reset,
    and use the native VLVF helper for incremental PF and VF ownership
    changes.
    
    Keep VLAN filtering enabled while SR-IOV is active. When PF hardware
    filtering is disabled, admit every VLAN to the PF without bypassing
    per-pool VF isolation. Reconstruct VLVF and the shared VFTA from PF and
    VF desired state after reset or a filtering-mode transition, and
    restore PF-only state on teardown.
    
    When the last VF leaves a VLAN still owned by the PF, free its VLVF
    slot while retaining the shared VFTA bit. This prevents a trunk VF from
    exhausting the 64-entry VLVF table by cycling VLAN memberships.
    
    Adapt the VLAN ownership model introduced for igb(4) in a2ed165f0049 to
    ixgbe's native VLVF machinery.
    
    Match Linux receive semantics by exposing a stripped VLAN tag only
    when that VID was registered by the VF.  A PF-assigned port VLAN is an
    administrative tag and must be delivered to the VF as untagged traffic;
    otherwise the stack dispatches it to a nonexistent VLAN interface and
    access-VLAN receive traffic is blackholed.
    
    MFC after:      1 week
    Relnotes:       yes
---
 share/man/man4/ix.4      |  24 ++++-
 sys/dev/ixgbe/if_ix.c    | 216 ++++++++++++++++++++++++++++++---------------
 sys/dev/ixgbe/if_sriov.c | 222 ++++++++++++++++++++++++++++++++++++++++++-----
 sys/dev/ixgbe/ix_txrx.c  |  18 +++-
 sys/dev/ixgbe/ixgbe.h    |   6 +-
 5 files changed, 392 insertions(+), 94 deletions(-)

diff --git a/share/man/man4/ix.4 b/share/man/man4/ix.4
index 39ed49aa8dfc..fb1d1032c0f1 100644
--- a/share/man/man4/ix.4
+++ b/share/man/man4/ix.4
@@ -29,7 +29,7 @@
 .\"
 .\" * Other names and brands may be claimed as the property of others.
 .\"
-.Dd November 10, 2025
+.Dd August 6, 2026
 .Dt IX 4
 .Os
 .Sh NAME
@@ -70,6 +70,27 @@ The maximum MTU size for Jumbo Frames is 9710.
 This driver version supports VLANs.
 For information on enabling VLANs, see
 .Xr ifconfig 8 .
+.Sh SR-IOV
+The driver-specific
+.Va vlan
+parameter in
+.Xr iovctl.conf 5
+accepts a VLAN identifier from 1 through 4095 or the keyword
+.Dq trunk ,
+which is the default.
+A numeric VLAN configures an access VF: the PF inserts the VLAN tag on
+transmit and strips it on receive.
+The stripped administrative tag is not presented to the VF network stack as
+.Dv M_VLANTAG ,
+so received packets are delivered directly through the VF interface.
+With
+.Dq trunk ,
+the VF may add and remove individual VLAN memberships; tags for VLANs
+registered by the VF are presented normally to its network stack.
+.Pp
+VLAN filtering remains enabled in the hardware while SR-IOV is active so that
+the PF can enforce VLAN ownership for each VF pool, including when hardware
+VLAN filtering has been disabled on the PF interface.
 .Sh HARDWARE
 The
 .Nm
@@ -220,6 +241,7 @@ issue to
 .Xr ng_ether 4 ,
 .Xr polling 4 ,
 .Xr vlan 4 ,
+.Xr iovctl.conf 5 ,
 .Xr ifconfig 8 ,
 .Xr sysctl 8
 .Sh HISTORY
diff --git a/sys/dev/ixgbe/if_ix.c b/sys/dev/ixgbe/if_ix.c
index 74444c439891..e8aa81df229c 100644
--- a/sys/dev/ixgbe/if_ix.c
+++ b/sys/dev/ixgbe/if_ix.c
@@ -241,7 +241,6 @@ static void ixgbe_add_hw_stats(struct ixgbe_softc *);
 static int  ixgbe_set_flowcntl(struct ixgbe_softc *, int);
 static int  ixgbe_set_advertise(struct ixgbe_softc *, int);
 static int  ixgbe_get_default_advertise(struct ixgbe_softc *);
-static void ixgbe_setup_vlan_hw_support(if_ctx_t);
 static void ixgbe_config_gpie(struct ixgbe_softc *);
 static void ixgbe_config_delay_values(struct ixgbe_softc *);
 
@@ -2321,12 +2320,21 @@ static void
 ixgbe_if_vlan_register(if_ctx_t ctx, u16 vtag)
 {
 	struct ixgbe_softc *sc = iflib_get_softc(ctx);
-	u16 index, bit;
+	bool present;
+	u16 index;
+	u32 mask;
 
 	index = (vtag >> 5) & 0x7F;
-	bit = vtag & 0x1F;
-	sc->shadow_vfta[index] |= (1 << bit);
-	++sc->num_vlans;
+	mask = 1U << (vtag & 0x1F);
+	present = (sc->shadow_vfta[index] & mask) != 0;
+	sc->shadow_vfta[index] |= mask;
+	if (!present)
+		++sc->num_vlans;
+#ifdef PCI_IOV
+	if ((sc->feat_en & IXGBE_FEATURE_SRIOV) != 0 &&
+	    sc->iov_vfta_valid && !sc->iov_vlan_promisc)
+		(void)ixgbe_set_vfta(&sc->hw, vtag, sc->pool, true, true);
+#endif
 	ixgbe_setup_vlan_hw_support(ctx);
 } /* ixgbe_if_vlan_register */
 
@@ -2339,96 +2347,167 @@ static void
 ixgbe_if_vlan_unregister(if_ctx_t ctx, u16 vtag)
 {
 	struct ixgbe_softc *sc = iflib_get_softc(ctx);
-	u16 index, bit;
+	bool present;
+	u16 index;
+	u32 mask;
 
 	index = (vtag >> 5) & 0x7F;
-	bit = vtag & 0x1F;
-	sc->shadow_vfta[index] &= ~(1 << bit);
-	--sc->num_vlans;
-	/* Re-init to load the changes */
+	mask = 1U << (vtag & 0x1F);
+	present = (sc->shadow_vfta[index] & mask) != 0;
+	sc->shadow_vfta[index] &= ~mask;
+	if (present)
+		--sc->num_vlans;
+#ifdef PCI_IOV
+	if ((sc->feat_en & IXGBE_FEATURE_SRIOV) != 0 &&
+	    sc->iov_vfta_valid && !sc->iov_vlan_promisc)
+		(void)ixgbe_set_vfta(&sc->hw, vtag, sc->pool, false, true);
+#endif
 	ixgbe_setup_vlan_hw_support(ctx);
 } /* ixgbe_if_vlan_unregister */
 
+#ifdef PCI_IOV
+static bool
+ixgbe_iov_pf_owns_vlan(const struct ixgbe_softc *sc, u16 vlan)
+{
+
+	return ((sc->shadow_vfta[vlan >> 5] &
+	    (1U << (vlan & 0x1f))) != 0);
+}
+
+/*
+ * start_hw clears both VFTA and VLVF.  Reconstruct the shared tables from
+ * PF and VF desired state after every reset or filtering-mode transition.
+ * Allocate VF entries first so PF-only VLANs cannot exhaust VLVF.
+ */
+static void
+ixgbe_iov_vlan_rebuild(struct ixgbe_softc *sc, bool promisc)
+{
+	struct ixgbe_hw *hw;
+	struct ixgbe_vf *vf;
+	u32 vfta[IXGBE_VFTA_SIZE];
+	u32 bits, vlan, vlvf;
+	int bit, failures, i, word;
+
+	hw = &sc->hw;
+	bcopy(sc->shadow_vfta, vfta, sizeof(vfta));
+	(void)ixgbe_clear_vfta(hw);
+	failures = 0;
+	for (i = 0; i < sc->num_vfs; i++) {
+		vf = &sc->vfs[i];
+		if ((vf->flags & IXGBE_VF_ACTIVE) == 0)
+			continue;
+		for (word = 0; word < IXGBE_VFTA_SIZE; word++) {
+			bits = vf->vlans[word];
+			while (bits != 0) {
+				bit = ffs(bits) - 1;
+				vlan = word * 32 + bit;
+				if (ixgbe_set_vfta(hw, vlan, vf->pool, true,
+				    false) == IXGBE_SUCCESS)
+					vfta[word] |= 1U << bit;
+				else
+					failures++;
+				bits &= ~(1U << bit);
+			}
+		}
+	}
+
+	/* Add the PF to shared entries, or every entry in promiscuous mode. */
+	for (i = 1; i < IXGBE_VLVF_ENTRIES; i++) {
+		vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(i));
+		if ((vlvf & IXGBE_VLVF_VIEN) == 0)
+			continue;
+		vlan = vlvf & IXGBE_VLVF_VLANID_MASK;
+		if (promisc || ixgbe_iov_pf_owns_vlan(sc, vlan))
+			(void)ixgbe_set_vfta(hw, vlan, sc->pool, true, true);
+		vfta[vlan >> 5] |= 1U << (vlan & 0x1f);
+	}
+	if (promisc)
+		for (i = 0; i < IXGBE_VFTA_SIZE; i++)
+			vfta[i] = UINT32_MAX;
+	for (i = 0; i < IXGBE_VFTA_SIZE; i++)
+		IXGBE_WRITE_REG(hw, IXGBE_VFTA(i), vfta[i]);
+	if (failures != 0)
+		device_printf(sc->dev,
+		    "VF VLAN restore failed for %d memberships\n", failures);
+}
+
+static void
+ixgbe_iov_vlan_sync(struct ixgbe_softc *sc, bool promisc)
+{
+
+	if (sc->iov_vfta_valid && sc->iov_vlan_promisc == promisc)
+		return;
+	ixgbe_iov_vlan_rebuild(sc, promisc);
+	sc->iov_vlan_promisc = promisc;
+	sc->iov_vfta_valid = true;
+}
+#endif
+
 /************************************************************************
  * ixgbe_setup_vlan_hw_support
  ************************************************************************/
-static void
+void
 ixgbe_setup_vlan_hw_support(if_ctx_t ctx)
 {
 	if_t ifp = iflib_get_ifp(ctx);
 	struct ixgbe_softc *sc = iflib_get_softc(ctx);
 	struct ixgbe_hw *hw = &sc->hw;
 	struct rx_ring *rxr;
+	bool strip;
 	int i;
 	u32 ctrl;
 
-
-	/*
-	 * We get here thru init_locked, meaning
-	 * a soft reset, this has already cleared
-	 * the VFTA and other state, so if there
-	 * have been no vlan's registered do nothing.
-	 */
-	if (sc->num_vlans == 0 ||
-	    (if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING) == 0) {
-		/* Clear the vlan hw flag */
-		for (i = 0; i < sc->num_rx_queues; i++) {
-			rxr = &sc->rx_queues[i].rxr;
-			/* On 82599 the VLAN enable is per/queue in RXDCTL */
-			if (hw->mac.type != ixgbe_mac_82598EB) {
-				ctrl = IXGBE_READ_REG(hw,
-				    IXGBE_RXDCTL(rxr->me));
+	strip = sc->num_vlans != 0 &&
+	    (if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING) != 0;
+	for (i = 0; i < sc->num_rx_queues; i++) {
+		rxr = &sc->rx_queues[i].rxr;
+		/* On 82599 and newer VLAN stripping is per receive queue. */
+		if (hw->mac.type != ixgbe_mac_82598EB) {
+			ctrl = IXGBE_READ_REG(hw, IXGBE_RXDCTL(rxr->me));
+			if (strip)
+				ctrl |= IXGBE_RXDCTL_VME;
+			else
 				ctrl &= ~IXGBE_RXDCTL_VME;
-				IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(rxr->me),
-				    ctrl);
-			}
-			rxr->vtag_strip = false;
+			IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(rxr->me), ctrl);
 		}
-		ctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
-		/* Enable the Filter Table if enabled */
-		ctrl |= IXGBE_VLNCTRL_CFIEN;
-		ctrl &= ~IXGBE_VLNCTRL_VFE;
-		if (hw->mac.type == ixgbe_mac_82598EB)
+		rxr->vtag_strip = strip;
+	}
+
+	ctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
+	if (hw->mac.type == ixgbe_mac_82598EB) {
+		if (strip)
+			ctrl |= IXGBE_VLNCTRL_VME;
+		else
 			ctrl &= ~IXGBE_VLNCTRL_VME;
+	}
+
+#ifdef PCI_IOV
+	if ((sc->feat_en & IXGBE_FEATURE_SRIOV) != 0) {
+		/*
+		 * VFE must remain enabled to enforce per-pool VLAN ownership.
+		 */
+		ctrl &= ~IXGBE_VLNCTRL_CFIEN;
+		ctrl |= IXGBE_VLNCTRL_VFE;
 		IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, ctrl);
+		ixgbe_iov_vlan_sync(sc,
+		    (if_getcapenable(ifp) & IFCAP_VLAN_HWFILTER) == 0);
 		return;
 	}
+#endif
 
-	/* Setup the queues for vlans */
-	if (if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING) {
-		for (i = 0; i < sc->num_rx_queues; i++) {
-			rxr = &sc->rx_queues[i].rxr;
-			/* On 82599 the VLAN enable is per/queue in RXDCTL */
-			if (hw->mac.type != ixgbe_mac_82598EB) {
-				ctrl = IXGBE_READ_REG(hw,
-				    IXGBE_RXDCTL(rxr->me));
-				ctrl |= IXGBE_RXDCTL_VME;
-				IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(rxr->me),
-				    ctrl);
-			}
-			rxr->vtag_strip = true;
-		}
+	if (!strip ||
+	    (if_getcapenable(ifp) & IFCAP_VLAN_HWFILTER) == 0) {
+		ctrl |= IXGBE_VLNCTRL_CFIEN;
+		ctrl &= ~IXGBE_VLNCTRL_VFE;
+		IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, ctrl);
+		return;
 	}
 
-	if ((if_getcapenable(ifp) & IFCAP_VLAN_HWFILTER) == 0)
-		return;
-	/*
-	 * A soft reset zero's out the VFTA, so
-	 * we need to repopulate it now.
-	 */
+	/* A soft reset clears VFTA, so restore the PF's desired bitmap. */
 	for (i = 0; i < IXGBE_VFTA_SIZE; i++)
-		if (sc->shadow_vfta[i] != 0)
-			IXGBE_WRITE_REG(hw, IXGBE_VFTA(i),
-			    sc->shadow_vfta[i]);
-
-	ctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
-	/* Enable the Filter Table if enabled */
-	if (if_getcapenable(ifp) & IFCAP_VLAN_HWFILTER) {
-		ctrl &= ~IXGBE_VLNCTRL_CFIEN;
-		ctrl |= IXGBE_VLNCTRL_VFE;
-	}
-	if (hw->mac.type == ixgbe_mac_82598EB)
-		ctrl |= IXGBE_VLNCTRL_VME;
+		IXGBE_WRITE_REG(hw, IXGBE_VFTA(i), sc->shadow_vfta[i]);
+	ctrl &= ~IXGBE_VLNCTRL_CFIEN;
+	ctrl |= IXGBE_VLNCTRL_VFE;
 	IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, ctrl);
 } /* ixgbe_setup_vlan_hw_support */
 
@@ -3839,6 +3918,7 @@ ixgbe_if_init(if_ctx_t ctx)
 
 	ixgbe_init_hw(hw);
 	sc->iov_mta_valid = false;
+	sc->iov_vfta_valid = false;
 
 	ixgbe_initialize_iov(sc);
 
diff --git a/sys/dev/ixgbe/if_sriov.c b/sys/dev/ixgbe/if_sriov.c
index 963f4392b2fd..318e0fe70759 100644
--- a/sys/dev/ixgbe/if_sriov.c
+++ b/sys/dev/ixgbe/if_sriov.c
@@ -36,6 +36,7 @@
 
 #ifdef PCI_IOV
 
+#include <sys/iov.h>
 #include <sys/ktr.h>
 
 MALLOC_DEFINE(M_IXGBE_SRIOV, "ix_sriov", "ix SR-IOV allocations");
@@ -66,6 +67,8 @@ ixgbe_define_iov_schemas(device_t dev, int *error)
 	    IOV_SCHEMA_HASDEFAULT, false);
 	pci_iov_schema_add_bool(vf_schema, "allow-promisc",
 	    IOV_SCHEMA_HASDEFAULT, false);
+	pci_iov_schema_add_vlan(vf_schema, "vlan", IOV_SCHEMA_HASDEFAULT,
+	    VF_VLAN_TRUNK);
 	*error = pci_iov_attach(dev, pf_schema, vf_schema);
 	if (*error != 0) {
 		device_printf(dev,
@@ -249,16 +252,105 @@ ixgbe_ping_all_vfs(struct ixgbe_softc *sc)
 } /* ixgbe_ping_all_vfs */
 
 
+static bool
+ixgbe_pf_owns_vlan(struct ixgbe_softc *sc, uint16_t tag)
+{
+	if_t ifp;
+
+	ifp = iflib_get_ifp(sc->ctx);
+	if ((if_getcapenable(ifp) & IFCAP_VLAN_HWFILTER) == 0)
+		return (true);
+	return ((sc->shadow_vfta[tag >> 5] &
+	    (1U << (tag & 0x1f))) != 0);
+}
+
+static bool
+ixgbe_vf_owns_vlan(const struct ixgbe_vf *vf, uint16_t tag)
+{
+
+	return ((vf->vlans[tag >> 5] & (1U << (tag & 0x1f))) != 0);
+}
+
 static void
-ixgbe_vf_set_default_vlan(struct ixgbe_softc *sc, struct ixgbe_vf *vf,
-    uint16_t tag)
+ixgbe_vf_vlan_release_pf_only(struct ixgbe_softc *sc, uint16_t tag)
 {
 	struct ixgbe_hw *hw;
-	uint32_t vmolr, vmvir;
+	uint32_t bits[2];
+	s32 slot;
 
 	hw = &sc->hw;
+	slot = ixgbe_find_vlvf_slot(hw, tag, true);
+	if (slot <= 0)
+		return;
+	bits[0] = IXGBE_READ_REG(hw, IXGBE_VLVFB(slot * 2));
+	bits[1] = IXGBE_READ_REG(hw, IXGBE_VLVFB(slot * 2 + 1));
+	bits[sc->pool / 32] &= ~(1U << (sc->pool % 32));
+	if (bits[0] != 0 || bits[1] != 0)
+		return;
+
+	/* The VFTA bit still admits this VLAN to the PF's default pool. */
+	IXGBE_WRITE_REG(hw, IXGBE_VLVF(slot), 0);
+	IXGBE_WRITE_REG(hw, IXGBE_VLVFB(slot * 2), 0);
+	IXGBE_WRITE_REG(hw, IXGBE_VLVFB(slot * 2 + 1), 0);
+}
 
-	vf->vlan_tag = tag;
+static s32
+ixgbe_vf_vlan_hw_update(struct ixgbe_softc *sc, struct ixgbe_vf *vf,
+    uint16_t tag, bool enable)
+{
+	struct ixgbe_hw *hw;
+	s32 error;
+
+	hw = &sc->hw;
+	if (!enable &&
+	    ixgbe_find_vlvf_slot(hw, tag, true) < IXGBE_SUCCESS)
+		return (IXGBE_SUCCESS);
+	/*
+	 * Allocate the VLVF entry with the PF first when it also owns this
+	 * VLAN.  This guarantees that adding the VF cannot hide the VLAN from
+	 * the PF when the shared VFTA bit becomes pool-selective.
+	 */
+	if (enable && ixgbe_pf_owns_vlan(sc, tag)) {
+		error = ixgbe_set_vfta(hw, tag, sc->pool, true, false);
+		if (error != IXGBE_SUCCESS)
+			return (error);
+	}
+
+	error = ixgbe_set_vfta(hw, tag, vf->pool, enable, false);
+	if (error != IXGBE_SUCCESS)
+		return (error);
+
+	/* Free a PF-only VLVF slot without removing the PF's VFTA bit. */
+	if (!enable && ixgbe_pf_owns_vlan(sc, tag))
+		ixgbe_vf_vlan_release_pf_only(sc, tag);
+	return (IXGBE_SUCCESS);
+}
+
+static void
+ixgbe_vf_vlan_record(struct ixgbe_vf *vf, uint16_t tag, bool enable)
+{
+	u32 mask;
+
+	mask = 1U << (tag & 0x1f);
+	if (enable) {
+		vf->vlans[tag >> 5] |= mask;
+		vf->num_vlans++;
+	} else {
+		vf->vlans[tag >> 5] &= ~mask;
+		vf->num_vlans--;
+	}
+}
+
+static void
+ixgbe_vf_configure_default_vlan(struct ixgbe_softc *sc,
+    struct ixgbe_vf *vf)
+{
+	struct ixgbe_hw *hw;
+	uint32_t vmolr, vmvir;
+	uint16_t tag;
+
+	hw = &sc->hw;
+	tag = vf->default_vlan;
 
 	vmolr = IXGBE_READ_REG(hw, IXGBE_VMOLR(vf->pool));
 
@@ -286,7 +378,45 @@ ixgbe_vf_set_default_vlan(struct ixgbe_softc *sc, struct ixgbe_vf *vf,
 	}
 	IXGBE_WRITE_REG(hw, IXGBE_VMOLR(vf->pool), vmolr);
 	IXGBE_WRITE_REG(hw, IXGBE_VMVIR(vf->pool), vmvir);
-} /* ixgbe_vf_set_default_vlan */
+} /* ixgbe_vf_configure_default_vlan */
+
+static void
+ixgbe_vf_clear_vlans(struct ixgbe_softc *sc, struct ixgbe_vf *vf,
+    bool clear_hw)
+{
+	uint32_t bits;
+	int bit, index;
+
+	for (index = 0; index < IXGBE_VFTA_SIZE; index++) {
+		bits = vf->vlans[index];
+		while (bits != 0) {
+			bit = ffs(bits) - 1;
+			if (clear_hw)
+				(void)ixgbe_vf_vlan_hw_update(sc, vf,
+				    index * 32 + bit, false);
+			bits &= ~(1U << bit);
+		}
+	}
+	bzero(vf->vlans, sizeof(vf->vlans));
+	vf->num_vlans = 0;
+}
+
+static s32
+ixgbe_vf_reset_vlan(struct ixgbe_softc *sc, struct ixgbe_vf *vf,
+    bool clear_hw)
+{
+	s32 error;
+
+	ixgbe_vf_clear_vlans(sc, vf, clear_hw);
+	error = IXGBE_SUCCESS;
+	if (vf->default_vlan != 0) {
+		error = ixgbe_vf_vlan_hw_update(sc, vf, vf->default_vlan, true);
+		if (error == IXGBE_SUCCESS)
+			ixgbe_vf_vlan_record(vf, vf->default_vlan, true);
+	}
+	ixgbe_vf_configure_default_vlan(sc, vf);
+	return (error);
+}
 
 static boolean_t
 ixgbe_vf_frame_size_compatible(struct ixgbe_softc *sc, struct ixgbe_vf *vf)
@@ -338,8 +468,13 @@ static void
 ixgbe_process_vf_reset(struct ixgbe_softc *sc, struct ixgbe_vf *vf)
 {
 	bool rebuild_mta;
+	s32 error;
 
-	ixgbe_vf_set_default_vlan(sc, vf, vf->default_vlan);
+	error = ixgbe_vf_reset_vlan(sc, vf, true);
+	if (error != IXGBE_SUCCESS)
+		device_printf(sc->dev,
+		    "VF %u default VLAN restore failed: %d\n",
+		    vf->pool, error);
 
 	rebuild_mta = vf->num_mc_hashes != 0;
 	vf->num_mc_hashes = 0;
@@ -497,26 +632,37 @@ ixgbe_vf_set_mc_addr(struct ixgbe_softc *sc, struct ixgbe_vf *vf, u32 *msg)
 static void
 ixgbe_vf_set_vlan(struct ixgbe_softc *sc, struct ixgbe_vf *vf, uint32_t *msg)
 {
-	struct ixgbe_hw *hw;
-	int enable;
+	bool enable, present;
+	s32 error;
 	uint16_t tag;
 
-	hw = &sc->hw;
-	enable = IXGBE_VT_MSGINFO(msg[0]);
+	enable = IXGBE_VT_MSGINFO(msg[0]) != 0;
 	tag = msg[1] & IXGBE_VLVF_VLANID_MASK;
 
-	if (!(vf->flags & IXGBE_VF_CAP_VLAN)) {
+	if (!(vf->flags & IXGBE_VF_CAP_VLAN) || vf->default_vlan != 0 ||
+	    (msg[1] & ~IXGBE_VLVF_VLANID_MASK) != 0) {
 		ixgbe_send_vf_failure(sc, vf, msg[0]);
 		return;
 	}
 
 	/* It is illegal to enable vlan tag 0. */
-	if (tag == 0 && enable != 0) {
+	if (tag == 0 && enable) {
 		ixgbe_send_vf_failure(sc, vf, msg[0]);
 		return;
 	}
 
-	ixgbe_set_vfta(hw, tag, vf->pool, enable, false);
+	present = ixgbe_vf_owns_vlan(vf, tag);
+	if (enable == present) {
+		ixgbe_send_vf_success(sc, vf, msg[0]);
+		return;
+	}
+
+	error = ixgbe_vf_vlan_hw_update(sc, vf, tag, enable);
+	if (error != IXGBE_SUCCESS) {
+		ixgbe_send_vf_failure(sc, vf, msg[0]);
+		return;
+	}
+	ixgbe_vf_vlan_record(vf, tag, enable);
 	ixgbe_send_vf_success(sc, vf, msg[0]);
 } /* ixgbe_vf_set_vlan */
 
@@ -811,25 +957,32 @@ ixgbe_if_iov_uninit(if_ctx_t ctx)
 	free(sc->vfs, M_IXGBE_SRIOV);
 	sc->vfs = NULL;
 	sc->feat_en &= ~IXGBE_FEATURE_SRIOV;
+	sc->iov_vfta_valid = false;
+	sc->iov_vlan_promisc = false;
+	(void)ixgbe_clear_vfta(hw);
+	ixgbe_setup_vlan_hw_support(ctx);
 } /* ixgbe_if_iov_uninit */
 
-static void
+static s32
 ixgbe_init_vf(struct ixgbe_softc *sc, struct ixgbe_vf *vf)
 {
 	struct ixgbe_hw *hw;
 	uint32_t vf_index, pfmbimr;
+	s32 error;
 
 	hw = &sc->hw;
 
 	if (!(vf->flags & IXGBE_VF_ACTIVE))
-		return;
+		return (IXGBE_SUCCESS);
 
 	vf_index = IXGBE_VF_INDEX(vf->pool);
 	pfmbimr = IXGBE_READ_REG(hw, IXGBE_PFMBIMR(vf_index));
 	pfmbimr |= IXGBE_VF_BIT(vf->pool);
 	IXGBE_WRITE_REG(hw, IXGBE_PFMBIMR(vf_index), pfmbimr);
 
-	ixgbe_vf_set_default_vlan(sc, vf, vf->vlan_tag);
+	error = ixgbe_vf_reset_vlan(sc, vf, false);
+	if (error != IXGBE_SUCCESS)
+		return (error);
 	vf->num_mc_hashes = 0;
 	bzero(vf->mc_hash, sizeof(vf->mc_hash));
 
@@ -843,6 +996,7 @@ ixgbe_init_vf(struct ixgbe_softc *sc, struct ixgbe_vf *vf)
 	ixgbe_vf_enable_receive(sc, vf);
 
 	ixgbe_send_vf_msg(&sc->hw, vf, IXGBE_PF_CONTROL_MSG);
+	return (IXGBE_SUCCESS);
 } /* ixgbe_init_vf */
 
 void
@@ -900,8 +1054,11 @@ ixgbe_initialize_iov(struct ixgbe_softc *sc)
 	vt_ctl |= (sc->pool << IXGBE_VT_CTL_POOL_SHIFT);
 	IXGBE_WRITE_REG(hw, IXGBE_VT_CTL, vt_ctl);
 
-	for (i = 0; i < sc->num_vfs; i++)
-		ixgbe_init_vf(sc, &sc->vfs[i]);
+	for (i = 0; i < sc->num_vfs; i++) {
+		if (ixgbe_init_vf(sc, &sc->vfs[i]) != IXGBE_SUCCESS)
+			device_printf(sc->dev,
+			    "VF %d default VLAN restore failed\n", i);
+	}
 } /* ixgbe_initialize_iov */
 
 
@@ -924,6 +1081,9 @@ ixgbe_if_iov_vf_add(if_ctx_t ctx, u16 vfnum, const nvlist_t *config)
 	struct ixgbe_softc *sc;
 	struct ixgbe_vf *vf;
 	const void *mac;
+	uint64_t configured_vlan;
+	uint16_t vlan;
+	s32 error;
 
 	sc = iflib_get_softc(ctx);
 
@@ -931,11 +1091,23 @@ ixgbe_if_iov_vf_add(if_ctx_t ctx, u16 vfnum, const nvlist_t *config)
 	    vfnum, sc->num_vfs));
 
 	vf = &sc->vfs[vfnum];
-	vf->pool= vfnum;
+	if (vf->flags & IXGBE_VF_ACTIVE)
+		return (EBUSY);
+
+	configured_vlan = nvlist_get_number(config, "vlan");
+	if (configured_vlan > VF_VLAN_TRUNK)
+		return (EINVAL);
+	vlan = configured_vlan;
+	if (vlan == 0)
+		return (ENOTSUP);
+	if (vlan == VF_VLAN_TRUNK)
+		vlan = 0;
+
+	vf->pool = vfnum;
 
 	/* RAR[0] is used by the PF so use vfnum + 1 for VF RAR. */
 	vf->rar_index = vfnum + 1;
-	vf->default_vlan = 0;
+	vf->default_vlan = vlan;
 	vf->maximum_frame_size = ETHER_MAX_LEN;
 	ixgbe_update_max_frame(sc, vf->maximum_frame_size);
 	if (nvlist_get_bool(config, "mac-anti-spoof"))
@@ -952,10 +1124,18 @@ ixgbe_if_iov_vf_add(if_ctx_t ctx, u16 vfnum, const nvlist_t *config)
 		 * we must allow the VF to choose one.
 		 */
 		vf->flags |= IXGBE_VF_CAP_MAC;
+	if (vf->default_vlan == 0)
+		vf->flags |= IXGBE_VF_CAP_VLAN;
 
 	vf->flags |= IXGBE_VF_ACTIVE;
 
-	ixgbe_init_vf(sc, vf);
+	error = ixgbe_init_vf(sc, vf);
+	if (error != IXGBE_SUCCESS) {
+		vf->flags &= ~IXGBE_VF_ACTIVE;
+		vf->default_vlan = 0;
+		ixgbe_vf_clear_vlans(sc, vf, true);
+		return (ENOSPC);
+	}
 
 	return (0);
 } /* ixgbe_if_iov_vf_add */
diff --git a/sys/dev/ixgbe/ix_txrx.c b/sys/dev/ixgbe/ix_txrx.c
index 3dda3270a2de..c95c3fc903ef 100644
--- a/sys/dev/ixgbe/ix_txrx.c
+++ b/sys/dev/ixgbe/ix_txrx.c
@@ -403,7 +403,7 @@ ixgbe_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri)
 	struct rx_ring *rxr = &que->rxr;
 	union ixgbe_adv_rx_desc *rxd;
 
-	uint16_t pkt_info, len, cidx, i;
+	uint16_t pkt_info, len, cidx, i, vid, vtag;
 	uint32_t ptype;
 	uint32_t staterr = 0;
 	bool eop;
@@ -463,8 +463,20 @@ ixgbe_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri)
 			ri->iri_rsstype = M_HASHTYPE_OPAQUE_HASH;
 	}
 	if ((rxr->vtag_strip) && (staterr & IXGBE_RXD_STAT_VP)) {
-		ri->iri_vtag = le16toh(rxd->wb.upper.vlan);
-		ri->iri_flags |= M_VLANTAG;
+		vtag = le16toh(rxd->wb.upper.vlan);
+		vid = EVL_VLANOFTAG(vtag);
+		/*
+		 * A PF-assigned port VLAN is stripped before a VF receives the
+		 * frame, but it is not one of the VLANs registered by the VF.
+		 * Do not expose that administrative tag to the VF's network
+		 * stack.  Locally registered trunk VLANs retain M_VLANTAG.
+		 */
+		if ((sc->feat_en & IXGBE_FEATURE_VF) == 0 ||
+		    (sc->shadow_vfta[vid >> 5] &
+		    (1U << (vid & 0x1f))) != 0) {
+			ri->iri_vtag = vtag;
+			ri->iri_flags |= M_VLANTAG;
+		}
 	}
 
 	ri->iri_nfrags = i;
diff --git a/sys/dev/ixgbe/ixgbe.h b/sys/dev/ixgbe/ixgbe.h
index ae246433348e..26513a0ba965 100644
--- a/sys/dev/ixgbe/ixgbe.h
+++ b/sys/dev/ixgbe/ixgbe.h
@@ -352,9 +352,10 @@ struct ixgbe_vf {
 	uint32_t	flags;
 	uint8_t		ether_addr[ETHER_ADDR_LEN];
 	uint16_t	mc_hash[IXGBE_MAX_VF_MC];
+	uint32_t	vlans[IXGBE_VFTA_SIZE];
 	uint16_t	num_mc_hashes;
+	uint16_t	num_vlans;
 	uint16_t	default_vlan;
-	uint16_t	vlan_tag;
 	uint16_t	api_ver;
 };
 
@@ -442,6 +443,8 @@ struct ixgbe_softc {
 	int			pool;
 	struct ixgbe_vf		*vfs;
 	bool			iov_mta_valid;
+	bool			iov_vfta_valid;
+	bool			iov_vlan_promisc;
 
 	/* Bypass */
 	struct ixgbe_bp_data	bypass;
@@ -607,6 +610,7 @@ void ixgbe_free_transmit_structures(struct ixgbe_softc *);
 int  ixgbe_setup_receive_structures(struct ixgbe_softc *);
 void ixgbe_free_receive_structures(struct ixgbe_softc *);
 int  ixgbe_get_regs(SYSCTL_HANDLER_ARGS);
+void ixgbe_setup_vlan_hw_support(if_ctx_t);
 
 void ixgbe_add_fw_logging_tunables(struct ixgbe_softc *sc,
 	struct sysctl_oid *parent);


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a75393f.3bd1c.196cc903>