git: f310b83defeb - stable/15 - igc: Add VLAN hardware filtering

Kevin Bowling <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a825511.37fc4.d302c6__2666.78530476696$1786926394$gmane$org@gitrepo.freebsd.org>
The branch stable/15 has been updated by kbowling:

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

commit f310b83defebddcb0fab9fbb4d9629ea8d0a2f04
Author:     Kevin Bowling <[email protected]>
AuthorDate: 2026-08-03 10:57:39 +0000
Commit:     Kevin Bowling <[email protected]>
CommitDate: 2026-08-17 00:25:39 +0000

    igc: Add VLAN hardware filtering
    
    Borrow the e1000 VLAN filter table
    
    Ambiguous presence of the feature by Intel was settled by DPDK and
    emperical testing.
    
    Relnotes:       yes
    
    (cherry picked from commit 8f779f159e2198c85b4fcb8685989879a9330104)
---
 share/man/man4/igc.4 |   6 +--
 sys/dev/igc/if_igc.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++----
 sys/dev/igc/if_igc.h |   3 ++
 3 files changed, 113 insertions(+), 12 deletions(-)

diff --git a/share/man/man4/igc.4 b/share/man/man4/igc.4
index 3b92472e8355..521a36531208 100644
--- a/share/man/man4/igc.4
+++ b/share/man/man4/igc.4
@@ -3,7 +3,7 @@
 .\" Copyright 2021 Rubicon Communications, LLC (Netgate)
 .\" SPDX-License-Identifier: BSD-3-Clause
 .\"
-.Dd March 9, 2026
+.Dd August 3, 2026
 .Dt IGC 4
 .Os
 .Sh NAME
@@ -39,8 +39,8 @@ utility
 configures the adapter to receive and transmit Jumbo Frames.
 The maximum MTU size for Jumbo Frames is 9216 bytes.
 .Pp
-This driver version supports VLAN hardware insertion / extraction, and
-VLAN checksum offload.
+This driver version supports VLAN hardware insertion / extraction,
+VLAN checksum offload, and VLAN hardware filtering.
 For information on enabling VLANs, see
 .Xr ifconfig 8 .
 The
diff --git a/sys/dev/igc/if_igc.c b/sys/dev/igc/if_igc.c
index ec2f6a2fa7b9..d9e2128d33eb 100644
--- a/sys/dev/igc/if_igc.c
+++ b/sys/dev/igc/if_igc.c
@@ -109,6 +109,8 @@ static void	igc_if_media_status(if_ctx_t, struct ifmediareq *);
 static int	igc_if_media_change(if_ctx_t);
 static int	igc_if_mtu_set(if_ctx_t, uint32_t);
 static void	igc_if_timer(if_ctx_t, uint16_t);
+static void	igc_if_vlan_register(if_ctx_t, u16);
+static void	igc_if_vlan_unregister(if_ctx_t, u16);
 static void	igc_if_watchdog_reset(if_ctx_t);
 static bool	igc_if_needs_restart(if_ctx_t, enum iflib_restart_event);
 
@@ -134,6 +136,10 @@ static void	igc_if_debug(if_ctx_t);
 static void	igc_update_stats_counters(struct igc_softc *);
 static void	igc_add_hw_stats(struct igc_softc *);
 static int	igc_if_set_promisc(if_ctx_t, int);
+static bool	igc_if_vlan_filter_capable(if_ctx_t);
+static bool	igc_if_vlan_filter_used(if_ctx_t);
+static void	igc_if_vlan_filter_enable(struct igc_softc *);
+static void	igc_if_vlan_filter_disable(struct igc_softc *);
 static void	igc_setup_vlan_hw_support(if_ctx_t);
 static void	igc_fw_version(struct igc_softc *);
 static void	igc_sbuf_fw_version(struct igc_fw_version *, struct sbuf *);
@@ -221,6 +227,8 @@ static device_method_t igc_if_methods[] = {
 	DEVMETHOD(ifdi_promisc_set, igc_if_set_promisc),
 	DEVMETHOD(ifdi_timer, igc_if_timer),
 	DEVMETHOD(ifdi_watchdog_reset, igc_if_watchdog_reset),
+	DEVMETHOD(ifdi_vlan_register, igc_if_vlan_register),
+	DEVMETHOD(ifdi_vlan_unregister, igc_if_vlan_unregister),
 	DEVMETHOD(ifdi_get_counter, igc_if_get_counter),
 	DEVMETHOD(ifdi_rx_queue_intr_enable, igc_if_rx_queue_intr_enable),
 	DEVMETHOD(ifdi_tx_queue_intr_enable, igc_if_tx_queue_intr_enable),
@@ -448,8 +456,9 @@ igc_set_num_queues(if_ctx_t ctx)
 
 #define	IGC_CAPS							\
     IFCAP_HWCSUM | IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING |		\
-    IFCAP_VLAN_HWCSUM | IFCAP_WOL | IFCAP_TSO4 | IFCAP_LRO |		\
-    IFCAP_VLAN_HWTSO | IFCAP_JUMBO_MTU | IFCAP_HWCSUM_IPV6 | IFCAP_TSO6
+    IFCAP_VLAN_HWCSUM | IFCAP_VLAN_HWFILTER | IFCAP_WOL | IFCAP_TSO4 |	\
+    IFCAP_LRO | IFCAP_VLAN_HWTSO | IFCAP_JUMBO_MTU |			\
+    IFCAP_HWCSUM_IPV6 | IFCAP_TSO6
 
 /*********************************************************************
  *  Device initialization routine
@@ -1307,20 +1316,24 @@ igc_if_set_promisc(if_ctx_t ctx, int flags)
 
 	/* Don't disable if in MAX groups */
 	if (mcnt < MAX_NUM_MULTICAST_ADDRESSES)
-		reg_rctl &=  (~IGC_RCTL_MPE);
-	IGC_WRITE_REG(&sc->hw, IGC_RCTL, reg_rctl);
+		reg_rctl &= ~IGC_RCTL_MPE;
 
 	if (flags & IFF_PROMISC) {
 		reg_rctl |= (IGC_RCTL_UPE | IGC_RCTL_MPE);
 		/* Turn this on if you want to see bad packets */
 		if (igc_debug_sbp)
 			reg_rctl |= IGC_RCTL_SBP;
-		IGC_WRITE_REG(&sc->hw, IGC_RCTL, reg_rctl);
 	} else if (flags & IFF_ALLMULTI) {
 		reg_rctl |= IGC_RCTL_MPE;
 		reg_rctl &= ~IGC_RCTL_UPE;
-		IGC_WRITE_REG(&sc->hw, IGC_RCTL, reg_rctl);
 	}
+
+	if ((flags & IFF_PROMISC) || !igc_if_vlan_filter_used(ctx))
+		reg_rctl &= ~IGC_RCTL_VFE;
+	else
+		reg_rctl |= IGC_RCTL_VFE;
+	IGC_WRITE_REG(&sc->hw, IGC_RCTL, reg_rctl);
+
 	return (0);
 }
 
@@ -2416,16 +2429,87 @@ igc_initialize_receive_unit(if_ctx_t ctx)
 	return;
 }
 
+static void
+igc_if_vlan_register(if_ctx_t ctx, u16 vtag)
+{
+	struct igc_softc *sc = iflib_get_softc(ctx);
+	u32 index, mask;
+
+	index = (vtag >> 5) & 0x7f;
+	mask = 1U << (vtag & 0x1f);
+	if ((sc->shadow_vfta[index] & mask) != 0)
+		return;
+	sc->shadow_vfta[index] |= mask;
+	igc_write_vfta(&sc->hw, index, sc->shadow_vfta[index]);
+}
+
+static void
+igc_if_vlan_unregister(if_ctx_t ctx, u16 vtag)
+{
+	struct igc_softc *sc = iflib_get_softc(ctx);
+	u32 index, mask;
+
+	index = (vtag >> 5) & 0x7f;
+	mask = 1U << (vtag & 0x1f);
+	if ((sc->shadow_vfta[index] & mask) == 0)
+		return;
+	sc->shadow_vfta[index] &= ~mask;
+	igc_write_vfta(&sc->hw, index, sc->shadow_vfta[index]);
+}
+
+static bool
+igc_if_vlan_filter_capable(if_ctx_t ctx)
+{
+	if_t ifp = iflib_get_ifp(ctx);
+
+	return ((if_getcapenable(ifp) & IFCAP_VLAN_HWFILTER) != 0 &&
+	    !igc_disable_crc_stripping);
+}
+
+static bool
+igc_if_vlan_filter_used(if_ctx_t ctx)
+{
+	struct igc_softc *sc = iflib_get_softc(ctx);
+
+	if (!igc_if_vlan_filter_capable(ctx))
+		return (false);
+
+	for (int i = 0; i < IGC_VFTA_SIZE; i++)
+		if (sc->shadow_vfta[i] != 0)
+			return (true);
+
+	return (false);
+}
+
+static void
+igc_if_vlan_filter_enable(struct igc_softc *sc)
+{
+	u32 reg;
+
+	reg = IGC_READ_REG(&sc->hw, IGC_RCTL);
+	reg &= ~IGC_RCTL_CFIEN;
+	reg |= IGC_RCTL_VFE;
+	IGC_WRITE_REG(&sc->hw, IGC_RCTL, reg);
+}
+
+static void
+igc_if_vlan_filter_disable(struct igc_softc *sc)
+{
+	u32 reg;
+
+	reg = IGC_READ_REG(&sc->hw, IGC_RCTL);
+	reg &= ~(IGC_RCTL_VFE | IGC_RCTL_CFIEN);
+	IGC_WRITE_REG(&sc->hw, IGC_RCTL, reg);
+}
+
 static void
 igc_setup_vlan_hw_support(if_ctx_t ctx)
 {
 	struct igc_softc *sc = iflib_get_softc(ctx);
 	struct igc_hw *hw = &sc->hw;
-	struct ifnet *ifp = iflib_get_ifp(ctx);
+	if_t ifp = iflib_get_ifp(ctx);
 	u32 reg;
 
-	/* igc hardware doesn't seem to implement VFTA for HWFILTER */
-
 	if (if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING &&
 	    !igc_disable_crc_stripping) {
 		reg = IGC_READ_REG(hw, IGC_CTRL);
@@ -2436,6 +2520,20 @@ igc_setup_vlan_hw_support(if_ctx_t ctx)
 		reg &= ~IGC_CTRL_VME;
 		IGC_WRITE_REG(hw, IGC_CTRL, reg);
 	}
+
+	if (!igc_if_vlan_filter_capable(ctx)) {
+		igc_if_vlan_filter_disable(sc);
+		return;
+	}
+
+	/* Always admit priority-tagged frames. */
+	sc->shadow_vfta[0] |= 1U;
+
+	/* A reset may clear the VFTA, so restore the complete desired table. */
+	for (int i = 0; i < IGC_VFTA_SIZE; i++)
+		igc_write_vfta(hw, i, sc->shadow_vfta[i]);
+
+	igc_if_vlan_filter_enable(sc);
 }
 
 static void
diff --git a/sys/dev/igc/if_igc.h b/sys/dev/igc/if_igc.h
index a090eeb12247..d9834212fee8 100644
--- a/sys/dev/igc/if_igc.h
+++ b/sys/dev/igc/if_igc.h
@@ -387,6 +387,9 @@ struct igc_softc {
 	/* Multicast array memory */
 	u8		*mta;
 
+	/* Retained across resets to restore the hardware VLAN filter table. */
+	u32		shadow_vfta[IGC_VFTA_SIZE];
+
 	/* Info about the interface */
 	u16		link_active;
 	u16		fc;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.