Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 30 Jul 2026 05:00:36 +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: a6bb3850e7c6 - main - igbv: Improve VF mailbox and status behavior
Message-ID:  <6a6ada74.310e6.1658b2df@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=a6bb3850e7c6ab6ce1356d52bc41368fe64e76fa

commit a6bb3850e7c6ab6ce1356d52bc41368fe64e76fa
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-07-29 03:24:04 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-07-30 04:55:27 +0000

    igbv: Improve VF mailbox and status behavior
    
    Treat VF media as fixed 1000baseT full duplex and report PF not ready
    and generated MAC fallback states during attach.
    
    After a successful reset handshake, reconcile a PF rejected MAC back
    into the ifnet. If the PF is unavailable, defer MAC, multicast, VLAN,
    LPE, and promiscuity replay until CTS is restored. Track a rejected
    VLAN removal separately so leaked traffic remains tagged until reset
    proves that the stale hardware filter is gone.
    
    Baseline VF counters at attach, collect the four loopback packet and
    octet counters with rollover-safe deltas, and account software RX
    checksum offload results. Preserve accumulated statistics across PF
    resets by rebasing the raw hardware counters, and sample them while
    physical link is down because VF loopback can remain active.
    
    Retain the 82576 VFMPRC hardware statistic, but do not read it on I350
    VFs because specification update errata 31 says it is unavailable.
    
    Clear PF owned flow control state and reset a link down VF when queued
    transmit descriptors must be flushed.
    
    Sponsored by:   BBOX.io
---
 sys/dev/e1000/if_em.c    | 212 +++++++++++++++++++++++++++++++++++++----------
 sys/dev/e1000/if_em.h    |  14 +++-
 sys/dev/e1000/if_igbv.c  | 115 +++++++++++++++++++++++++
 sys/dev/e1000/igb_txrx.c |  34 ++++++--
 4 files changed, 319 insertions(+), 56 deletions(-)

diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c
index 0e4671296339..9f9d3dc3024e 100644
--- a/sys/dev/e1000/if_em.c
+++ b/sys/dev/e1000/if_em.c
@@ -455,11 +455,11 @@ static int	igb_if_tx_queue_intr_enable(if_ctx_t, uint16_t);
 static void	em_if_multi_set(if_ctx_t);
 static void	em_if_update_admin_status(if_ctx_t);
 static void	em_if_debug(if_ctx_t);
-static void	em_update_stats_counters(struct e1000_softc *);
+static void	em_initialize_vf_stats(struct e1000_softc *);
+static void	em_rebase_vf_stats(struct e1000_softc *);
 static void	em_update_vf_stats_counters(struct e1000_softc *);
 static void	em_add_hw_stats(struct e1000_softc *);
 static int	em_if_set_promisc(if_ctx_t, int);
-static int	em_if_set_promisc_impl(if_ctx_t, int);
 static bool	em_if_defer_promisc(struct e1000_softc *);
 static bool	em_if_vlan_filter_capable(if_ctx_t);
 static bool	em_if_vlan_filter_used(if_ctx_t);
@@ -476,7 +476,6 @@ static int	em_sysctl_print_fw_version(SYSCTL_HANDLER_ARGS);
 static int	em_sysctl_debug_info(SYSCTL_HANDLER_ARGS);
 static int	em_get_rs(SYSCTL_HANDLER_ARGS);
 static void	em_print_debug_info(struct e1000_softc *);
-static int 	em_is_valid_ether_addr(u8 *);
 static void	em_newitr(struct e1000_softc *, struct em_rx_queue *,
     struct rx_ring *);
 static bool	em_automask_tso(if_ctx_t);
@@ -692,10 +691,10 @@ static device_method_t igbv_if_methods[] = {
 	DEVMETHOD(ifdi_tx_queues_alloc, em_if_tx_queues_alloc),
 	DEVMETHOD(ifdi_rx_queues_alloc, em_if_rx_queues_alloc),
 	DEVMETHOD(ifdi_queues_free, em_if_queues_free),
-	DEVMETHOD(ifdi_update_admin_status, em_if_update_admin_status),
+	DEVMETHOD(ifdi_update_admin_status, igbv_if_update_admin_status),
 	DEVMETHOD(ifdi_multi_set, em_if_multi_set),
 	DEVMETHOD(ifdi_media_status, em_if_media_status),
-	DEVMETHOD(ifdi_media_change, em_if_media_change),
+	DEVMETHOD(ifdi_media_change, igbv_if_media_change),
 	DEVMETHOD(ifdi_mtu_set, em_if_mtu_set),
 	DEVMETHOD(ifdi_promisc_set, em_if_set_promisc),
 	DEVMETHOD(ifdi_timer, em_if_timer),
@@ -1528,7 +1527,9 @@ em_if_attach_pre(if_ctx_t ctx)
 	** important in reading the nvm and
 	** mac from that.
 	*/
-	e1000_reset_hw(hw);
+	if (e1000_reset_hw(hw) != E1000_SUCCESS && sc->vf_ifp)
+		device_printf(dev,
+		    "PF is not ready; VF mailbox initialization deferred\n");
 
 	/* Make sure a PF has a good EEPROM before we read from it. */
 	if (!sc->vf_ifp && e1000_validate_nvm_checksum(hw) < 0) {
@@ -1555,6 +1556,9 @@ em_if_attach_pre(if_ctx_t ctx)
 
 	if (!em_is_valid_ether_addr(hw->mac.addr)) {
 		if (sc->vf_ifp) {
+			device_printf(dev,
+			    "PF did not assign a MAC address; using a "
+			    "locally generated address\n");
 			ether_gen_addr(iflib_get_ifp(ctx),
 			    (struct ether_addr *)hw->mac.addr);
 		} else {
@@ -1617,13 +1621,16 @@ em_if_attach_post(if_ctx_t ctx)
 
 	/* Initialize statistics */
 	if (sc->vf_ifp)
-		sc->ustats.vf_stats = (struct e1000_vf_stats){};
+		em_initialize_vf_stats(sc);
 	else
 		sc->ustats.stats = (struct e1000_hw_stats){};
 
 	em_update_stats_counters(sc);
 	hw->mac.get_link_status = 1;
-	em_if_update_admin_status(ctx);
+	if (sc->vf_ifp)
+		igbv_if_update_admin_status(ctx);
+	else
+		em_if_update_admin_status(ctx);
 	em_add_hw_stats(sc);
 
 	/* Non-AMT based hardware can now take control from firmware */
@@ -1777,17 +1784,23 @@ em_if_init(if_ctx_t ctx)
 	if_softc_ctx_t scctx = sc->shared;
 	if_t ifp = iflib_get_ifp(ctx);
 	struct em_tx_queue *tx_que;
+	bool vf_mbx_ready;
 	int i;
 
 	INIT_DEBUGOUT("em_if_init: begin");
+	vf_mbx_ready = !sc->vf_ifp;
 	if (sc->vf_ifp)
 		sc->vf_reset_pending = true;
 
 	/* Get the latest mac address, User can use a LAA */
 	bcopy(if_getlladdr(ifp), sc->hw.mac.addr, ETHER_ADDR_LEN);
 
-	/* Put the address into the Receive Address Array */
-	e1000_rar_set(&sc->hw, sc->hw.mac.addr, 0);
+	/*
+	 * A VF restores its address only after its reset handshake establishes
+	 * CTS.  The PF path programs RAR[0] directly here.
+	 */
+	if (!sc->vf_ifp)
+		e1000_rar_set(&sc->hw, sc->hw.mac.addr, 0);
 
 	/*
 	 * With the 82571 adapter, RAR[0] may be overwritten
@@ -1803,15 +1816,22 @@ em_if_init(if_ctx_t ctx)
 
 	/* Initialize the hardware */
 	igb_iov_reset_prepare(sc);
-	if (sc->vf_ifp)
-		(void)igbv_reset(ctx);
-	else
+	if (sc->vf_ifp) {
+		vf_mbx_ready = igbv_reset(ctx);
+		em_rebase_vf_stats(sc);
+	} else {
 		em_reset(ctx);
+	}
+	if (sc->vf_ifp && vf_mbx_ready)
+		igbv_reconcile_mac(sc, ifp);
 	/* Re-arm a link-up transition deferred for this reset. */
 	if (sc->link_state == EM_LINK_STATE_DOWN_RESET_PENDING ||
 	    sc->link_state == EM_LINK_STATE_UP_RESET_PENDING)
 		sc->link_state = EM_LINK_STATE_DOWN;
-	em_if_update_admin_status(ctx);
+	if (sc->vf_ifp)
+		igbv_if_update_admin_status(ctx);
+	else
+		em_if_update_admin_status(ctx);
 
 	for (i = 0, tx_que = sc->tx_queues; i < sc->tx_num_queues;
 	    i++, tx_que++) {
@@ -1844,8 +1864,13 @@ em_if_init(if_ctx_t ctx)
 	else
 		em_initialize_transmit_unit(ctx);
 
-	/* Setup Multicast table */
-	em_if_multi_set(ctx);
+	/*
+	 * A failed VF reset has no CTS channel on which to restore mailbox
+	 * state.  The reset detector schedules another complete init, which
+	 * replays these interface-owned lists after the handshake succeeds.
+	 */
+	if (vf_mbx_ready)
+		em_if_multi_set(ctx);
 
 	sc->rx_mbuf_sz = iflib_get_rx_mbuf_sz(ctx);
 	if (sc->vf_ifp)
@@ -1853,11 +1878,13 @@ em_if_init(if_ctx_t ctx)
 	else
 		em_initialize_receive_unit(ctx);
 
-	/* Set up VLAN support and filter */
-	em_setup_vlan_hw_support(ctx);
+	if (vf_mbx_ready) {
+		/* Set up VLAN support and filter. */
+		em_setup_vlan_hw_support(ctx);
 
-	/* Don't lose promiscuous settings */
-	em_if_set_promisc_impl(ctx, if_getflags(ifp));
+		/* Don't lose promiscuous settings. */
+		em_if_set_promisc_impl(ctx, if_getflags(ifp));
+	}
 	atomic_readandclear_32(&sc->promisc_pending);
 
 	/* Restore PF/VF pool configuration after the global reset. */
@@ -2414,12 +2441,13 @@ em_if_defer_promisc(struct e1000_softc *sc)
 	return (true);
 }
 
-static int
+int
 em_if_set_promisc_impl(if_ctx_t ctx, int flags)
 {
 	struct e1000_softc *sc = iflib_get_softc(ctx);
 	if_t ifp = iflib_get_ifp(ctx);
 	enum e1000_promisc_type type;
+	s32 error;
 	u32 reg_rctl;
 	int mcnt = 0;
 
@@ -2430,7 +2458,8 @@ em_if_set_promisc_impl(if_ctx_t ctx, int flags)
 			type = e1000_promisc_multicast;
 		else
 			type = e1000_promisc_disabled;
-		if (e1000_promisc_set_vf(&sc->hw, type) != E1000_SUCCESS) {
+		error = e1000_promisc_set_vf(&sc->hw, type);
+		if (error != E1000_SUCCESS) {
 			device_printf(sc->dev,
 			    "VF promiscuous-mode request failed\n");
 			return (EPERM);
@@ -2579,6 +2608,8 @@ em_if_update_admin_status(if_ctx_t ctx)
 	u32 link_check, thstat, ctrl;
 	bool reset_requested = false;
 
+	KASSERT(!sc->vf_ifp, ("%s called for a VF", __func__));
+
 	if (atomic_readandclear_32(&sc->promisc_pending) != 0)
 		(void)em_if_set_promisc_impl(ctx,
 		    if_getflags(iflib_get_ifp(ctx)));
@@ -3902,6 +3933,14 @@ em_setup_interface(if_ctx_t ctx)
 	 * Specify the media types supported by this adapter and register
 	 * callbacks to update media and link information
 	 */
+	if (sc->vf_ifp) {
+		ifmedia_add(sc->media,
+		    IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL);
+		ifmedia_set(sc->media,
+		    IFM_ETHER | IFM_1000_T | IFM_FDX);
+		return (0);
+	}
+
 	if (sc->hw.phy.media_type == e1000_media_type_fiber ||
 	    sc->hw.phy.media_type == e1000_media_type_internal_serdes) {
 		u_char fiber_type = IFM_1000_SX;	/* default type */
@@ -4522,6 +4561,7 @@ em_if_vlan_register(if_ctx_t ctx, u16 vtag)
 	 * ready to accept it yet.
 	 */
 	sc->shadow_vfta[index] |= mask;
+	sc->vf_vfta_stale[index] &= ~mask;
 	if (!present)
 		++sc->num_vlans;
 	if (sc->vf_ifp &&
@@ -4552,11 +4592,12 @@ em_if_vlan_unregister(if_ctx_t ctx, u16 vtag)
 		device_printf(sc->dev,
 		    "VF VLAN %u remove request failed\n", vtag);
 		/*
-		 * The PF can still admit this VID.  Retain the bit so its
-		 * stripped frames remain tagged and are dropped by vlan(4)
-		 * after the child disappears, rather than reaching the parent.
+		 * Hardware might still admit this VID.  Preserve its receive
+		 * tag until a successful VF reset proves the stale filter gone.
 		 */
-		return;
+		sc->vf_vfta_stale[index] |= mask;
+	} else {
+		sc->vf_vfta_stale[index] &= ~mask;
 	}
 	sc->shadow_vfta[index] &= ~mask;
 	if (present)
@@ -4645,6 +4686,7 @@ em_setup_vlan_hw_support(if_ctx_t ctx)
 	struct e1000_softc *sc = iflib_get_softc(ctx);
 	struct e1000_hw *hw = &sc->hw;
 	if_t ifp = iflib_get_ifp(ctx);
+	s32 error;
 	u32 max_frame_size, reg;
 	u16 vid;
 
@@ -4661,7 +4703,12 @@ em_setup_vlan_hw_support(if_ctx_t ctx)
 			if ((sc->shadow_vfta[vid >> 5] &
 			    (1U << (vid & 0x1f))) == 0)
 				continue;
-			if (e1000_vfta_set_vf(hw, vid, true) != E1000_SUCCESS)
+			/*
+			 * Desired state remains in shadow_vfta for the next
+			 * replay if the PF mailbox is absent during reset.
+			 */
+			error = e1000_vfta_set_vf(hw, vid, true);
+			if (error != E1000_SUCCESS)
 				device_printf(sc->dev,
 				    "VF VLAN %u restore request failed\n",
 				    vid);
@@ -4890,16 +4937,13 @@ em_release_hw_control(struct e1000_softc *sc)
 	return;
 }
 
-static int
-em_is_valid_ether_addr(u8 *addr)
+bool
+em_is_valid_ether_addr(const u8 *addr)
 {
-	char zero_addr[6] = { 0, 0, 0, 0, 0, 0 };
-
-	if ((addr[0] & 1) || (!bcmp(addr, zero_addr, ETHER_ADDR_LEN))) {
-		return (false);
-	}
+	static const u8 zero_addr[ETHER_ADDR_LEN];
 
-	return (true);
+	return (!ETHER_IS_MULTICAST(addr) &&
+	    memcmp(addr, zero_addr, ETHER_ADDR_LEN) != 0);
 }
 
 static bool
@@ -5269,7 +5313,7 @@ em_disable_aspm(struct e1000_softc *sc)
  *  Update the board statistics counters.
  *
  **********************************************************************/
-static void
+void
 em_update_stats_counters(struct e1000_softc *sc)
 {
 	struct e1000_hw_stats *stats;
@@ -5381,15 +5425,60 @@ em_update_stats_counters(struct e1000_softc *sc)
 }
 
 static void
-em_update_vf_stats_counters(struct e1000_softc *sc)
+em_initialize_vf_stats(struct e1000_softc *sc)
 {
 	struct e1000_vf_stats *stats;
 
-	if (sc->link_speed == 0)
-		return;
+	stats = &sc->ustats.vf_stats;
+	*stats = (struct e1000_vf_stats){};
+	em_rebase_vf_stats(sc);
+}
+
+static void
+em_rebase_vf_stats(struct e1000_softc *sc)
+{
+	struct e1000_vf_stats *stats;
+
+	/*
+	 * A PF reset starts a new VF counter epoch.  Preserve the accumulated
+	 * totals while establishing a new raw baseline so the reset is not
+	 * mistaken for a 32-bit wrap.
+	 */
+	stats = &sc->ustats.vf_stats;
+#define INIT_VF_REG(reg, name) do {					\
+	stats->last_##name = E1000_READ_REG(&sc->hw, reg);		\
+} while (0)
+	INIT_VF_REG(E1000_VFGPRC, gprc);
+	INIT_VF_REG(E1000_VFGORC, gorc);
+	INIT_VF_REG(E1000_VFGPTC, gptc);
+	INIT_VF_REG(E1000_VFGOTC, gotc);
+	/*
+	 * I350 specification update erratum 31 says VFMPRC is not
+	 * accessible from VF memory.  The 0xf3c register remains valid on
+	 * 82576 VFs, but must not be read on vfadapt_i350.
+	 */
+	if (sc->hw.mac.type == e1000_vfadapt)
+		INIT_VF_REG(E1000_VFMPRC, mprc);
+	else
+		stats->last_mprc = 0;
+	INIT_VF_REG(E1000_VFGOTLBC, gotlbc);
+	INIT_VF_REG(E1000_VFGPTLBC, gptlbc);
+	INIT_VF_REG(E1000_VFGORLBC, gorlbc);
+	INIT_VF_REG(E1000_VFGPRLBC, gprlbc);
+#undef INIT_VF_REG
+}
+
+static void
+em_update_vf_stats_counters(struct e1000_softc *sc)
+{
+	struct e1000_vf_stats *stats;
 
 	stats = &sc->ustats.vf_stats;
 
+	/*
+	 * Internal VF loopback traffic can continue without physical link,
+	 * so sample the counters regardless of link state.
+	 */
 	UPDATE_VF_REG(E1000_VFGPRC,
 	    stats->last_gprc, stats->gprc);
 	UPDATE_VF_REG(E1000_VFGORC,
@@ -5398,8 +5487,17 @@ em_update_vf_stats_counters(struct e1000_softc *sc)
 	    stats->last_gptc, stats->gptc);
 	UPDATE_VF_REG(E1000_VFGOTC,
 	    stats->last_gotc, stats->gotc);
-	UPDATE_VF_REG(E1000_VFMPRC,
-	    stats->last_mprc, stats->mprc);
+	if (sc->hw.mac.type == e1000_vfadapt)
+		UPDATE_VF_REG(E1000_VFMPRC,
+		    stats->last_mprc, stats->mprc);
+	UPDATE_VF_REG(E1000_VFGOTLBC,
+	    stats->last_gotlbc, stats->gotlbc);
+	UPDATE_VF_REG(E1000_VFGPTLBC,
+	    stats->last_gptlbc, stats->gptlbc);
+	UPDATE_VF_REG(E1000_VFGORLBC,
+	    stats->last_gorlbc, stats->gorlbc);
+	UPDATE_VF_REG(E1000_VFGPRLBC,
+	    stats->last_gprlbc, stats->gprlbc);
 }
 
 static uint64_t
@@ -5688,9 +5786,35 @@ em_add_hw_stats(struct e1000_softc *sc)
 		SYSCTL_ADD_QUAD(ctx, stat_list, OID_AUTO, "good_octets_txd",
 		    CTLFLAG_RD, &vfstats->gotc,
 		    "Good Octets Transmitted");
-		SYSCTL_ADD_QUAD(ctx, stat_list, OID_AUTO, "mcast_pkts_recvd",
-		    CTLFLAG_RD, &vfstats->mprc,
-		    "Multicast Packets Received");
+		if (sc->hw.mac.type == e1000_vfadapt) {
+			SYSCTL_ADD_QUAD(ctx, stat_list, OID_AUTO,
+			    "mcast_pkts_recvd", CTLFLAG_RD, &vfstats->mprc,
+			    "Multicast Packets Received");
+		}
+		SYSCTL_ADD_QUAD(ctx, stat_list, OID_AUTO,
+		    "loopback_good_pkts_recvd",
+		    CTLFLAG_RD, &vfstats->gprlbc,
+		    "Good Loopback Packets Received");
+		SYSCTL_ADD_QUAD(ctx, stat_list, OID_AUTO,
+		    "loopback_good_pkts_txd",
+		    CTLFLAG_RD, &vfstats->gptlbc,
+		    "Good Loopback Packets Transmitted");
+		SYSCTL_ADD_QUAD(ctx, stat_list, OID_AUTO,
+		    "loopback_good_octets_recvd",
+		    CTLFLAG_RD, &vfstats->gorlbc,
+		    "Good Loopback Octets Received");
+		SYSCTL_ADD_QUAD(ctx, stat_list, OID_AUTO,
+		    "loopback_good_octets_txd",
+		    CTLFLAG_RD, &vfstats->gotlbc,
+		    "Good Loopback Octets Transmitted");
+		SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO,
+		    "rx_csum_offload_good",
+		    CTLFLAG_RD, &sc->rx_csum_good,
+		    "Receive Checksum Offload Successes");
+		SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO,
+		    "rx_csum_offload_errors",
+		    CTLFLAG_RD, &sc->rx_csum_errors,
+		    "Receive Checksum Offload Errors");
 		return;
 	}
 
diff --git a/sys/dev/e1000/if_em.h b/sys/dev/e1000/if_em.h
index e83a6c47baf3..dbc3e945f78b 100644
--- a/sys/dev/e1000/if_em.h
+++ b/sys/dev/e1000/if_em.h
@@ -389,11 +389,8 @@ struct igb_vf_mac_filter;
 #define UPDATE_VF_REG(reg, last, cur)		\
 do {						\
 	u32 new = E1000_READ_REG(&sc->hw, reg);	\
-	if (new < last)				\
-		cur += 0x100000000LL;		\
+	cur += (u32)(new - last);		\
 	last = new;				\
-	cur &= 0xFFFFFFFF00000000LL;		\
-	cur |= new;				\
 } while (0)
 
 struct e1000_softc;
@@ -586,6 +583,7 @@ struct e1000_softc {
 	** to repopulate it.
 	*/
 	u32			shadow_vfta[EM_VFTA_SIZE];
+	u32			vf_vfta_stale[EM_VFTA_SIZE];
 
 	/* Info about the interface */
 	enum em_link_state	link_state;
@@ -635,6 +633,8 @@ struct e1000_softc {
 	unsigned long		link_irq;
 	unsigned long		rx_overruns;
 	unsigned long		watchdog_events;
+	u64			rx_csum_good;
+	u64			rx_csum_errors;
 
 	union {
 		struct e1000_hw_stats	stats;		/* !sc->vf_ifp */
@@ -652,16 +652,22 @@ struct e1000_softc {
 int	em_if_attach_pre(if_ctx_t);
 int	em_if_attach_post(if_ctx_t);
 void	em_add_device_sysctls(struct e1000_softc *);
+int	em_if_set_promisc_impl(if_ctx_t, int);
+bool	em_is_valid_ether_addr(const u8 *);
 void	em_initialize_transmit_rings(if_ctx_t);
+void	em_update_stats_counters(struct e1000_softc *);
 void	igb_initialize_receive_rings(if_ctx_t, bool);
 
 int	igbv_get_regs(SYSCTL_HANDLER_ARGS);
 int	igbv_if_attach_pre(if_ctx_t);
 int	igbv_if_attach_post(if_ctx_t);
+int	igbv_if_media_change(if_ctx_t);
 void	igbv_if_intr_enable(if_ctx_t);
 void	igbv_if_intr_disable(if_ctx_t);
+void	igbv_if_update_admin_status(if_ctx_t);
 void	igbv_initialize_receive_unit(if_ctx_t);
 void	igbv_initialize_transmit_unit(if_ctx_t);
+void	igbv_reconcile_mac(struct e1000_softc *, if_t);
 bool	igbv_reset(if_ctx_t);
 
 /********************************************************************************
diff --git a/sys/dev/e1000/if_igbv.c b/sys/dev/e1000/if_igbv.c
index e084b89809be..78b07d204aef 100644
--- a/sys/dev/e1000/if_igbv.c
+++ b/sys/dev/e1000/if_igbv.c
@@ -30,6 +30,8 @@
 
 #include <sys/sbuf.h>
 
+static bool	igbv_tx_pending(struct e1000_softc *);
+
 int
 igbv_if_attach_pre(if_ctx_t ctx)
 {
@@ -76,6 +78,92 @@ igbv_if_attach_post(if_ctx_t ctx)
 	return (0);
 }
 
+int
+igbv_if_media_change(if_ctx_t ctx __unused)
+{
+
+	return (EOPNOTSUPP);
+}
+
+void
+igbv_if_update_admin_status(if_ctx_t ctx)
+{
+	struct e1000_softc *sc;
+	struct e1000_hw *hw;
+	device_t dev;
+	bool link_check;
+
+	sc = iflib_get_softc(ctx);
+	hw = &sc->hw;
+	dev = iflib_get_dev(ctx);
+	KASSERT(sc->vf_ifp, ("%s called for a PF", __func__));
+
+	if (!sc->vf_reset_pending &&
+	    atomic_readandclear_32(&sc->promisc_pending) != 0)
+		(void)em_if_set_promisc_impl(ctx,
+		    if_getflags(iflib_get_ifp(ctx)));
+
+	if (e1000_check_for_link(hw) != E1000_SUCCESS &&
+	    !sc->vf_reset_pending) {
+		sc->vf_reset_pending = true;
+		iflib_request_reset(ctx);
+		iflib_admin_intr_deferred(ctx);
+	}
+	link_check = !hw->mac.get_link_status;
+
+	if (link_check &&
+	    (sc->link_state == EM_LINK_STATE_DOWN ||
+	    sc->link_state == EM_LINK_STATE_DOWN_RESET_PENDING)) {
+		e1000_get_speed_and_duplex(hw, &sc->link_speed,
+		    &sc->link_duplex);
+		if (bootverbose)
+			device_printf(dev, "Link is up %d Mbps %s\n",
+			    sc->link_speed,
+			    sc->link_duplex == FULL_DUPLEX ?
+			    "Full Duplex" : "Half Duplex");
+		sc->link_state = EM_LINK_STATE_UP;
+		iflib_link_state_change(ctx, LINK_STATE_UP,
+		    IF_Mbps(sc->link_speed));
+	} else if (!link_check &&
+	    (sc->link_state == EM_LINK_STATE_UP ||
+	    sc->link_state == EM_LINK_STATE_UP_RESET_PENDING)) {
+		sc->link_speed = 0;
+		sc->link_duplex = 0;
+		sc->link_state = EM_LINK_STATE_DOWN;
+		iflib_link_state_change(ctx, LINK_STATE_DOWN, 0);
+	}
+
+	/*
+	 * A VF stops transmit DMA when its PF reports link down.  Reset if
+	 * descriptors remain queued so they cannot be sent stale when carrier
+	 * returns, matching the periodic check in Linux igbvf.
+	 */
+	if (!link_check && !sc->vf_reset_pending && igbv_tx_pending(sc)) {
+		sc->vf_reset_pending = true;
+		iflib_request_reset(ctx);
+		iflib_admin_intr_deferred(ctx);
+	}
+	/* em_if_init() establishes a new counter baseline after the reset. */
+	if (!sc->vf_reset_pending)
+		em_update_stats_counters(sc);
+}
+
+static bool
+igbv_tx_pending(struct e1000_softc *sc)
+{
+	struct tx_ring *txr;
+	u32 head, tail;
+
+	for (int i = 0; i < sc->tx_num_queues; i++) {
+		txr = &sc->tx_queues[i].txr;
+		head = E1000_READ_REG(&sc->hw, E1000_TDH(txr->me));
+		tail = E1000_READ_REG(&sc->hw, E1000_TDT(txr->me));
+		if (head != tail)
+			return (true);
+	}
+	return (false);
+}
+
 bool
 igbv_reset(if_ctx_t ctx)
 {
@@ -100,6 +188,7 @@ igbv_reset(if_ctx_t ctx)
 		e1000_check_for_link(hw);
 		return (false);
 	}
+	memset(sc->vf_vfta_stale, 0, sizeof(sc->vf_vfta_stale));
 	if (e1000_init_hw(hw) < 0) {
 		device_printf(sc->dev, "Hardware Initialization Failed\n");
 		return (false);
@@ -203,3 +292,29 @@ igbv_get_regs(SYSCTL_HANDLER_ARGS)
 	sbuf_delete(sb);
 	return (error);
 }
+
+void
+igbv_reconcile_mac(struct e1000_softc *sc, if_t ifp)
+{
+	u8 *lladdr;
+
+	if (!em_is_valid_ether_addr(sc->hw.mac.addr))
+		return;
+	lladdr = (u8 *)if_getlladdr(ifp);
+	if (memcmp(lladdr, sc->hw.mac.addr, ETHER_ADDR_LEN) == 0)
+		return;
+
+	device_printf(sc->dev,
+	    "PF rejected or replaced the requested MAC; using %6D\n",
+	    sc->hw.mac.addr, ":");
+	/*
+	 * if_setlladdr() would re-enter the driver's address-change path.
+	 * Initialization already holds the context lock, so update the
+	 * storage directly and issue the notification it would have sent.
+	 */
+	memcpy(lladdr, sc->hw.mac.addr, ETHER_ADDR_LEN);
+
+	CURVNET_SET_QUIET(if_getvnet(ifp));
+	EVENTHANDLER_INVOKE(iflladdr_event, ifp);
+	CURVNET_RESTORE();
+}
diff --git a/sys/dev/e1000/igb_txrx.c b/sys/dev/e1000/igb_txrx.c
index 2b6144a27d25..dd4d1e5b5974 100644
--- a/sys/dev/e1000/igb_txrx.c
+++ b/sys/dev/e1000/igb_txrx.c
@@ -56,7 +56,14 @@ static int igb_tx_ctx_setup(struct tx_ring *, if_pkt_info_t, uint32_t *,
 static int igb_tso_setup(struct tx_ring *, if_pkt_info_t, uint32_t *,
     uint32_t *);
 
-static void igb_rx_checksum(uint32_t, if_rxd_info_t, uint32_t);
+enum igb_rx_csum_status {
+	IGB_RX_CSUM_NONE,
+	IGB_RX_CSUM_GOOD,
+	IGB_RX_CSUM_ERROR,
+};
+
+static enum igb_rx_csum_status igb_rx_checksum(uint32_t, if_rxd_info_t,
+    uint32_t);
 static int igb_determine_rsstype(uint16_t);
 
 extern void igb_if_enable_intr(if_ctx_t);
@@ -76,6 +83,7 @@ struct if_txrx igb_txrx = {
 static bool
 igb_vf_vlan_registered(const struct e1000_softc *sc, u16 vtag)
 {
+	u32 vlans;
 	u16 vid;
 
 	/*
@@ -91,8 +99,8 @@ igb_vf_vlan_registered(const struct e1000_softc *sc, u16 vtag)
 	 * A trunk VF is an implicit member of VID 0, so retain priority-tag
 	 * metadata without requiring vlan(4) to register a VID-0 interface.
 	 */
-	return (vid == 0 ||
-	    (sc->shadow_vfta[vid >> 5] & (1U << (vid & 0x1f))) != 0);
+	vlans = sc->shadow_vfta[vid >> 5] | sc->vf_vfta_stale[vid >> 5];
+	return (vid == 0 || (vlans & (1U << (vid & 0x1f))) != 0);
 }
 
 /**********************************************************************
@@ -481,6 +489,7 @@ igb_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri)
 
 	uint16_t pkt_info, len;
 	uint32_t ptype, staterr;
+	enum igb_rx_csum_status csum_status;
 	int i, cidx;
 	bool eop;
 
@@ -530,8 +539,15 @@ igb_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri)
 	rxr->rx_bytes += ri->iri_len;
 	rxr->rx_packets++;
 
-	if ((scctx->isc_capenable & IFCAP_RXCSUM) != 0)
-		igb_rx_checksum(staterr, ri, ptype);
+	if ((scctx->isc_capenable & IFCAP_RXCSUM) != 0) {
+		csum_status = igb_rx_checksum(staterr, ri, ptype);
+		if (sc->vf_ifp) {
+			if (csum_status == IGB_RX_CSUM_GOOD)
+				sc->rx_csum_good++;
+			else if (csum_status == IGB_RX_CSUM_ERROR)
+				sc->rx_csum_errors++;
+		}
+	}
 
 	if (staterr & E1000_RXD_STAT_VP) {
 		if (((sc->hw.mac.type == e1000_i350) ||
@@ -560,19 +576,19 @@ igb_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri)
  *  doesn't spend time verifying the checksum.
  *
  *********************************************************************/
-static void
+static enum igb_rx_csum_status
 igb_rx_checksum(uint32_t staterr, if_rxd_info_t ri, uint32_t ptype)
 {
 	uint16_t status = (uint16_t)staterr;
 	uint8_t errors = (uint8_t)(staterr >> 24);
 
 	if (__predict_false(status & E1000_RXD_STAT_IXSM))
-		return;
+		return (IGB_RX_CSUM_NONE);
 
 	/* If there is a layer 3 or 4 error we are done */
 	if (__predict_false(errors &
 	    (E1000_RXD_ERR_IPE | E1000_RXD_ERR_TCPE)))
-		return;
+		return (IGB_RX_CSUM_ERROR);
 
 	/* IP Checksum Good */
 	if (status & E1000_RXD_STAT_IPCS)
@@ -592,6 +608,8 @@ igb_rx_checksum(uint32_t staterr, if_rxd_info_t ri, uint32_t ptype)
 			ri->iri_csum_data = htons(0xffff);
 		}
 	}
+
+	return (IGB_RX_CSUM_GOOD);
 }
 
 /********************************************************************


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a6ada74.310e6.1658b2df>