git: f308d6d4846a - main - igbv: Recover disabled Hyper-V transmit queues

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Wed, 16 Sep 2026 21:05:51 UTC
The branch main has been updated by kbowling:

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

commit f308d6d4846a51c2d0839840d6eabbbe70309515
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-09-16 20:45:16 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-09-16 21:05:43 +0000

    igbv: Recover disabled Hyper-V transmit queues
    
    The Windows PF can disable a VF transmit queue while continuing to
    report carrier up.  Link polling alone then leaves the VF operationally
    up even though it cannot transmit.  The reproduced VLAN failure shows
    this state with PF driver 14.1.5.0 and an MDD indication in the host trace.
    
    Check queue zero from the admin path only while the Hyper-V VF is
    running with sanitized queues and a completed host handshake.  Report
    operational link down and invalidate the statistics baseline when the
    queue is disabled.  Request recovery through the normal iflib stop/init
    path only when a fresh, accessible STATUS read reports carrier up.
    Rate limit requests if the host continues to hold the queue disabled,
    and leave recovery pending while carrier is down.
    
    Document the recovery behavior and clarify why the Hyper-V reset retains
    the VF-local software reset before its host reset/MAC exchange.
    
    Sponsored by:   BBOX.io
---
 share/man/man4/em.4     |  8 +++++++-
 sys/dev/e1000/if_igbv.c | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/share/man/man4/em.4 b/share/man/man4/em.4
index 187930de087d..317e25926469 100644
--- a/share/man/man4/em.4
+++ b/share/man/man4/em.4
@@ -32,7 +32,7 @@
 .\"
 .\" * Other names and brands may be claimed as the property of others.
 .\"
-.Dd September 15, 2026
+.Dd September 16, 2026
 .Dt EM 4
 .Os
 .Sh NAME
@@ -112,6 +112,12 @@ the host and use the
 .Xr hn 4
 synthetic data path.
 .Pp
+If the host unexpectedly disables a running Hyper-V VF transmit queue,
+the driver reports link down.
+If the hardware reports carrier up, the driver requests recovery through
+the normal stop/init path.
+These recovery requests are rate-limited if the host keeps the queue disabled.
+.Pp
 The driver supports Transmit/Receive checksum offload.
 Jumbo Frames are supported except on 82542-based adapters and Hyper-V VFs.
 .Pp
diff --git a/sys/dev/e1000/if_igbv.c b/sys/dev/e1000/if_igbv.c
index 62b09973b301..eafae1250053 100644
--- a/sys/dev/e1000/if_igbv.c
+++ b/sys/dev/e1000/if_igbv.c
@@ -94,6 +94,7 @@ igbv_hv_reset_hw(struct e1000_hw *hw)
 	ctrl = E1000_READ_REG(hw, E1000_CTRL);
 	if (ctrl == 0xffffffff)
 		return (-E1000_ERR_RESET);
+	/* Reset VF-local state before the Hyper-V host reset/MAC exchange. */
 	E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_RST);
 	E1000_WRITE_FLUSH(hw);
 	for (i = 0; i < E1000_VF_INIT_TIMEOUT; i++) {
@@ -558,6 +559,40 @@ igbv_if_update_admin_status(if_ctx_t ctx)
 		return;
 	}
 
+	if (igbv_is_hyperv(sc)) {
+		u32 status, txdctl;
+
+		if (sc->vf_reset_pending)
+			return;
+		txdctl = E1000_READ_REG(hw, E1000_TXDCTL(0));
+		if (txdctl != 0xffffffff &&
+		    (txdctl & E1000_TXDCTL_QUEUE_ENABLE) == 0) {
+			/* Carrier can remain up after the PF disables queue DMA. */
+			sc->vf_stats_valid = false;
+			sc->link_speed = sc->link_duplex = 0;
+			if (sc->link_state != EM_LINK_STATE_DOWN) {
+				sc->link_state = EM_LINK_STATE_DOWN;
+				iflib_link_state_change(ctx, LINK_STATE_DOWN, 0);
+			}
+			/*
+			 * Do not reset while carrier is down.  Limit requests
+			 * if the host keeps the queue disabled with carrier up.
+			 */
+			status = E1000_READ_REG(hw, E1000_STATUS);
+			if (status != 0xffffffff &&
+			    (status & E1000_STATUS_LU) != 0 &&
+			    ratecheck(&sc->vf_last_queue_log,
+			    &igbv_queue_log_interval)) {
+				device_printf(dev,
+				    "Hyper-V VF queue disabled; requesting recovery\n");
+				sc->vf_reset_pending = true;
+				iflib_request_reset_if_up(ctx);
+				iflib_admin_intr_deferred(ctx);
+			}
+			return;
+		}
+	}
+
 	if (!sc->vf_reset_pending &&
 	    atomic_readandclear_32(&sc->promisc_pending) != 0)
 		(void)em_if_set_promisc_impl(ctx,