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>
