git: 3a0d13751d21 - stable/15 - igb/igbv: MFC SR-IOV PF support and the VF driver split

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Sat, 31 Oct 2026 19:24:49 UTC
The branch stable/15 has been updated by kbowling:

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

commit 3a0d13751d211d13e225d6277b65f49ac1b1ce82
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-10-31 19:18:42 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-10-31 19:20:26 +0000

    igb/igbv: MFC SR-IOV PF support and the VF driver split
    
    Add SR-IOV PF support for Intel 82576 and I350 controllers and register
    virtual functions as igbv.  Bring over the shared mailbox and hardware
    queue mapping changes, separate VF policy, filtering and statistics,
    mailbox recovery, retained-queue sanitization, DMA fencing, and Hyper-V
    support and recovery.  Include the native VF-status provider for the
    netlink framework already on stable/15.
    
    Consolidate the series with the later e1000 and iflib fixes already
    merged to stable/15.  Complete the igb-specific pieces of the existing
    IOV restart support, including configuration while the PF is down.
    The resulting e1000 sources match main before the separate RSS hash-field
    policy change in 86bdf7a4aec5.  The reverted ioctl VF-status interface is
    not imported.
    
    Accept the VF interface rename from igbN to igbvN and document the
    configuration and device-tunable migration in UPDATING.  Preserve the
    if_em.ko implementation and if_igb.ko alias, add if_igbv.ko and manual
    aliases, and document the PF queue constraints and Hyper-V limitations.
    
    Relnotes:       yes
    Sponsored by:   BBOX.io
    
    (cherry picked from commit ac9a6d00a0146b3cda6d03b4d2c8895c2813d89a)
    (cherry picked from commit fd594981c55b3a4e316e72265065566a54d40d38)
    (cherry picked from commit e6b9ce25ec062b45038a37c88d193fa438f5ef58)
    (cherry picked from commit a2ed165f0049595f2e52ec545095240e362f27c7)
    (cherry picked from commit 316f6f2f7fa613d13316ea966a34ee5285b987ec)
    (cherry picked from commit a6bb3850e7c6ab6ce1356d52bc41368fe64e76fa)
    (cherry picked from commit 9332fd555588ef4f7913664f8bdb57febc828e76)
    (cherry picked from commit 350211ab1782a68190754dcbdbcc7c9169ce22cb)
    (cherry picked from commit 5ce6c94f98599e5ae0595ff5178a224145b6225f)
    (cherry picked from commit e8f3b96ba794521af9904a451da33cf119381da8)
    (cherry picked from commit fdce3830d9a66aa30cb40ad4ea984f376ee00095)
    (cherry picked from commit 5f02c8439786728392f250ff6fa772ce1787b74c)
    (cherry picked from commit 7cd6d2365b3121743dfd6012e259a3b05b0e7adb)
    (cherry picked from commit 8c8724705613c856836c3928e7240baae03e690c)
    (cherry picked from commit a77257d6005839f2dd540e98f1e9ecd70879a38d)
    (cherry picked from commit ac2be06e0c697f0c15c9a4fcb9503b2ddb14e135)
    (cherry picked from commit 176259efcb907bd73079e4da83344077271916a5)
    (cherry picked from commit 1c91c3ae1ec6c65e458f130a95d3119d83ca088e)
    (cherry picked from commit 75538a0f3275887d75f3585038d3de23baad9005)
    (cherry picked from commit 612f87c949a2aa3363258890bca2abaea99d3fd6)
    (cherry picked from commit 505e6bb93f80fa3a7799cd88b0d0cafcaa468491)
    (cherry picked from commit 037d45a0526f452d96f44059e88f735298a9063d)
    (cherry picked from commit 8d14fc8ba7f55c873e866f392c17113d514acce9)
    (cherry picked from commit f51ad3a266ec1ea71c24329dbffdb66a3708b214)
    (cherry picked from commit 89ce1aa07241abbcea1703b5f2b85b88460d584f)
    (cherry picked from commit f308d6d4846a51c2d0839840d6eabbbe70309515)
    (cherry picked from commit 100f7b8d39b273d0083b3f5a884fc00cc73169fd)
    (cherry picked from commit 2cf580c694f6f392531a63f01c3fb89c0244f89a)
---
 UPDATING                    |   13 +
 share/man/man4/Makefile     |    2 +
 share/man/man4/em.4         |  212 +++-
 sys/conf/files              |    4 +
 sys/dev/e1000/e1000_82575.h |    3 +-
 sys/dev/e1000/e1000_hw.h    |    3 +-
 sys/dev/e1000/e1000_mbx.c   |  107 +-
 sys/dev/e1000/e1000_mbx.h   |    5 +-
 sys/dev/e1000/e1000_osdep.h |  114 ++-
 sys/dev/e1000/e1000_regs.h  |    1 +
 sys/dev/e1000/e1000_vf.c    |  109 ++-
 sys/dev/e1000/e1000_vf.h    |    6 +-
 sys/dev/e1000/em_txrx.c     |    5 +
 sys/dev/e1000/if_em.c       | 1585 +++++++++++++++++++++++-------
 sys/dev/e1000/if_em.h       |  106 +-
 sys/dev/e1000/if_igb_iov.c  | 2268 +++++++++++++++++++++++++++++++++++++++++++
 sys/dev/e1000/if_igb_iov.h  |   61 ++
 sys/dev/e1000/if_igbv.c     |  973 +++++++++++++++++++
 sys/dev/e1000/igb_txrx.c    |   55 +-
 sys/modules/em/Makefile     |    7 +-
 20 files changed, 5192 insertions(+), 447 deletions(-)

diff --git a/UPDATING b/UPDATING
index 9116e396bb8f..777efc49b8c4 100644
--- a/UPDATING
+++ b/UPDATING
@@ -12,6 +12,19 @@ Items affecting the ports and packages system can be found in
 /usr/ports/UPDATING.  Please read that file before updating system packages
 and/or ports.
 
+20261031:
+	The Intel 82576 and I350 Virtual Function devices now use the igbv(4)
+	driver.  VF interfaces previously named igbN are now named igbvN.
+	Before booting the new kernel, update interface references in rc.conf,
+	firewall rules, and other network configuration, and change VF device
+	tunables from dev.igb.N to dev.igbv.N.  Systems with both PFs and VFs
+	should also check PF unit numbers after the driver split.
+
+	The drivers remain in if_em.ko; existing if_em_load and if_igb_load
+	settings still load them.  if_igbv_load is also supported.
+	The igb(4) PF driver now supports SR-IOV on these controllers; see
+	igb(4) for the PCI_IOV, MSI-X, and single-queue requirements.
+
 20261029:
 	struct if_rxd_info now carries a receive timestamp.  iflib drivers
 	must be rebuilt.  __FreeBSD_version has been bumped to 1501506.
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 66c4d6c8537f..a9d56d7496ae 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -716,6 +716,8 @@ MLINKS+=edsc.4 if_edsc.4
 MLINKS+=em.4 if_em.4 \
 	em.4 igb.4 \
 	em.4 if_igb.4 \
+	em.4 igbv.4 \
+	em.4 if_igbv.4 \
 	em.4 lem.4 \
 	em.4 if_lem.4
 MLINKS+=ena.4 if_ena.4
diff --git a/share/man/man4/em.4 b/share/man/man4/em.4
index 96d68eb4fcca..133f0d3bbac8 100644
--- a/share/man/man4/em.4
+++ b/share/man/man4/em.4
@@ -32,13 +32,14 @@
 .\"
 .\" * Other names and brands may be claimed as the property of others.
 .\"
-.Dd August 30, 2026
+.Dd September 16, 2026
 .Dt EM 4
 .Os
 .Sh NAME
 .Nm em ,
 .Nm lem ,
-.Nm igb
+.Nm igb ,
+.Nm igbv
 .Nd "Intel(R) PRO/1000 Gigabit Ethernet adapter driver"
 .Sh SYNOPSIS
 To compile this driver into the kernel,
@@ -49,6 +50,11 @@ kernel configuration file:
 .Cd "device em"
 .Ed
 .Pp
+PF SR-IOV support additionally requires:
+.Bd -ragged -offset indent
+.Cd "options PCI_IOV"
+.Ed
+.Pp
 Alternatively, to load the driver as a
 module at boot time, place the following line in
 .Xr loader.conf 5 :
@@ -78,12 +84,43 @@ The
 .Nm
 driver provides support for PCI Express Gigabit Ethernet adapters
 based on the Intel 82575, 82576, 82580, i210, i211, and i35x.
-These appear as
+Physical functions appear as
 .Nm igb
 interfaces to maintain compatibility with existing infrastructure.
+Virtual functions provided by 82576 and I350 physical functions appear as
+.Nm igbv
+interfaces.
 .Pp
-The driver supports Transmit/Receive checksum offload and Jumbo Frames
-on all but 82542-based adapters.
+On Hyper-V,
+.Nm igbv
+supports 82576 and I350 virtual functions using the host-assigned MAC address
+and a single transmit/receive queue pair.
+The VF can be used by
+.Xr hn 4
+as the accelerated data path for its synthetic interface.
+The host controls MAC, multicast, promiscuous-mode, and VLAN-filter policy;
+changing these on the VF does not reconfigure the host.
+Hyper-V VFs currently support an MTU of at most 1500 bytes.
+.Pp
+Only host-assigned access VLANs with untagged guest traffic are supported
+on Hyper-V; guest VLAN registration is ignored.
+With Windows I350 PF driver version 14.1.5.0, tagged VF transmissions can
+trigger host malicious driver detection and
+disable the VF's queues even when the Hyper-V port is configured for trunk mode.
+This limitation is also reproducible with Windows VF drivers.
+For guest VLAN trunks, disable SR-IOV for the virtual network adapter on
+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
 Physical functions on supported boards and ports advertise one or more of the
 .Cm wol_magic ,
@@ -96,7 +133,9 @@ The available modes depend on the controller and board.
 The NVM APME setting normally selects whether magic-packet wake is enabled by
 default without hiding modes that the hardware and board support.
 Wake support can be restricted to particular ports on multi-port adapters and
-is not available on virtual functions.
+is not available on
+.Nm igbv
+virtual functions.
 Waking the system from an ACPI sleep state also requires enabling the
 corresponding device wake source when it is exposed, for example:
 .Bd -literal -offset indent
@@ -186,6 +225,161 @@ is not specified, the driver defaults to
 .Pp
 For more information on configuring this device, see
 .Xr ifconfig 8 .
+.Sh SR-IOV
+When the kernel is built with
+.Cd PCI_IOV ,
+the
+.Nm igb
+PF driver supports SR-IOV on 82576 and I350 controllers.
+Up to seven VFs can be created per PF.
+PF SR-IOV is not featured on 82575, 82580, I210, I211, or I354 controllers.
+.Pp
+Passing a VF through to a guest requires an IOMMU to isolate its DMA.
+The packet filters described below do not constrain descriptor DMA;
+.Xr bhyve 8
+refuses to assign a passthrough device when it cannot create an IOMMU domain.
+.Pp
+The supported SR-IOV configuration uses one PF transmit and receive queue and
+requires MSI-X.
+I350 requires this layout; 82576 hardware can combine VMDq with per-pool RSS,
+but that mode is not implemented by this driver.
+The PF may be administratively down when VFs are created and remains down
+afterward.
+VF mailbox and datapath service begin when the PF is brought up, and the PF
+must remain up to provide those services.
+Creating or destroying VFs temporarily stops and restarts a running PF.
+Set the following per-device
+.Xr iflib 4
+loader tunables before the PF attaches:
+.Bd -literal -offset indent
+dev.igb.N.iflib.override_ntxqs=1
+dev.igb.N.iflib.override_nrxqs=1
+.Ed
+.Pp
+The following VF parameters are accepted in
+.Xr iovctl.conf 5 :
+.Bl -tag -width "mac-anti-spoof"
+.It Va mac-addr
+Assign a unicast MAC address to the VF.
+If omitted, the PF uses the stable address generator described by
+.Xr ether_gen_addr 9
+with a distinct name for each VF.
+.It Va mac-anti-spoof
+Enable source MAC anti-spoofing.
+The default is true.
+The check binds transmissions to MAC addresses currently assigned to that VF.
+It prevents impersonating the PF or a sibling VF, but a VF permitted to change
+its MAC can select an address belonging to an off-host station.
+.It Va allow-set-mac
+Permit the VF to replace its primary MAC address, whether assigned by
+.Va mac-addr
+or generated by the PF, and to install up to three auxiliary unicast
+addresses.
+Each auxiliary address expands both the VF's receive filters and the source
+addresses permitted by transmit anti-spoofing.
+The default is false.
+.It Va allow-promisc
+Permit explicit VF mailbox requests for unicast or multicast promiscuous mode
+within its VLAN membership.
+The default is false.
+VF promiscuous-mode changes are applied asynchronously.
+If the PF refuses a request, the VF logs the failure after
+.Xr ifconfig 8
+has returned.
+.It Va vlan
+Assign the VF an access VLAN from 1 through 4095.
+The PF inserts this tag on VF transmissions, admits only this VLAN to the VF,
+and strips the tag on reception so the VF operates on untagged traffic.
+VF mailbox requests to change VLAN membership are rejected while an access
+VLAN is assigned.
+The default value,
+.Dq trunk ,
+instead permits the VF to manage its VLAN memberships through the mailbox.
+VLAN 0 cannot be assigned as an access VLAN.
+.El
+.Pp
+I350 hides an assigned access VLAN in hardware.
+On 82576, transparent delivery depends on the VF driver ignoring stripped
+VLAN metadata for VLANs it did not request; the in-tree
+.Nm igbv
+VF driver does so.
+DPDK guests and
+.Fx
+guests with an older
+.Nm igb
+driver do not apply this workaround and can expose the assigned VLAN tag
+instead.
+.Pp
+The VF mailbox carries at most 30 multicast hashes.
+If the VF reports more, the PF automatically enables reception of all
+multicast within that VF's VLAN membership.
+This overflow fallback is independent of
+.Va allow-promisc .
+The multicast hash table is shared by the port, so hash collisions can also
+deliver matching multicast traffic to another multicast-enabled pool in the
+same VLAN.
+.Pp
+A VF with
+.Va allow-set-mac
+enabled can install at most three auxiliary unicast receive addresses.
+These filters come from a controller-wide pool; exhaustion makes later
+requests fail without replacing existing filters.
+.Pp
+The hardware provides 32 VLAN-to-pool filter entries for VLANs assigned to
+VFs, including PF-assigned access VLANs, and rejects a request that would
+exceed 32 distinct VF VLANs.
+A trunk VF's implicit VID-0 membership consumes one shared entry, leaving
+31 entries for nonzero VF VLANs while any trunk VF is active.
+.Pp
+Each VF can burst 64 VLAN-add requests and then receives eight additional
+additions per second.
+This permits two complete 31-VLAN replays while bounding repeated global
+filter-table rebuilds.
+VLAN removals and requests that do not change membership are not limited.
+A PF-wide reinitialization replenishes the burst; VF-initiated resets do not.
+The
+.Nm igbv
+VF driver retries failed VLAN additions at the PF's sustained rate for up to
+eight seconds.
+This heals transient rate-limit failures without polling indefinitely when
+the shared VLAN table is full.
+.Pp
+On 82576, a PF-only VLAN uses the default PF pool without consuming one of
+these entries; an entry shared by the PF and VFs includes all member pools.
+I350 can additionally place the PF in VLAN-promiscuous mode when its PF
+memberships do not fit.
+Because the entries are shared, VF allocations can force this PF fallback.
+DMA coalescing is disabled while I350 SR-IOV is active.
+.Pp
+The PF enables malicious-driver detection.
+Hardware stops a VF transmit queue that submits a malformed descriptor, such as
+a legacy descriptor in a virtualized mode, or a descriptor requesting VLAN
+insertion while an access VLAN is assigned.
+The PF logs
+.Ql malicious-driver event ,
+clears the offending VF's queue enables and PF-owned filter state, and sends a
+control message directing the guest to complete a new reset handshake.
+Recovery is unconditional; there is no tunable to disable it.
+A VF that submits further malformed descriptors is stopped and recovered again.
+.Pp
+Transmissions failing the anti-spoof checks are dropped by hardware without
+stopping the queue or resetting the VF.
+The PF logs
+.Ql spoof event detected
+for an offending VF at most once every two seconds.
+.Pp
+If a VF retains mailbox ownership while the PF is reading a request, the PF
+retries with a bounded backoff, then logs
+.Ql mailbox remained busy
+and revokes that VF's clear-to-send state.
+Requests from that VF are ignored until a hardware reset begins a new mailbox
+epoch.
+A guest that resets through the VF control register or through a function level
+reset recovers automatically; a guest that only sends the reset mailbox message
+does not.
+.Pp
+PF and VF statistics are sampled once per interface watchdog interval, so
+exported counters can trail the hardware by up to 500 milliseconds.
 .Sh HARDWARE
 The
 .Nm
@@ -350,13 +544,17 @@ issue to
 .Sh SEE ALSO
 .Xr altq 4 ,
 .Xr arp 4 ,
+.Xr hn 4 ,
 .Xr iflib 4 ,
 .Xr led 4 ,
 .Xr netintro 4 ,
 .Xr ng_ether 4 ,
 .Xr polling 4 ,
 .Xr vlan 4 ,
-.Xr ifconfig 8
+.Xr iovctl.conf 5 ,
+.Xr ifconfig 8 ,
+.Xr iovctl 8 ,
+.Xr ether_gen_addr 9
 .Sh HISTORY
 The
 .Nm
diff --git a/sys/conf/files b/sys/conf/files
index 72c6439d9bba..8551f999a2a7 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1633,6 +1633,10 @@ dev/efidev/efirt.c		optional efirt
 dev/efidev/efirtc.c		optional efirt efirtc
 dev/e1000/if_em.c		optional em \
 	compile-with "${NORMAL_C} -I$S/dev/e1000"
+dev/e1000/if_igbv.c		optional em \
+	compile-with "${NORMAL_C} -I$S/dev/e1000"
+dev/e1000/if_igb_iov.c		optional em \
+	compile-with "${NORMAL_C} -I$S/dev/e1000"
 dev/e1000/em_txrx.c		optional em \
 	compile-with "${NORMAL_C} -I$S/dev/e1000"
 dev/e1000/igb_txrx.c		optional em \
diff --git a/sys/dev/e1000/e1000_82575.h b/sys/dev/e1000/e1000_82575.h
index 69c195cc199d..430eed4202e3 100644
--- a/sys/dev/e1000/e1000_82575.h
+++ b/sys/dev/e1000/e1000_82575.h
@@ -403,9 +403,10 @@ enum e1000_promisc_type {
 	e1000_num_promisc_types
 };
 
-void e1000_vfta_set_vf(struct e1000_hw *, u16, bool);
+s32 e1000_vfta_set_vf(struct e1000_hw *, u16, bool);
 void e1000_rlpml_set_vf(struct e1000_hw *, u16);
 s32 e1000_promisc_set_vf(struct e1000_hw *, enum e1000_promisc_type type);
+s32 e1000_set_uc_addr_vf(struct e1000_hw *, u32, u8 *);
 void e1000_write_vfta_i350(struct e1000_hw *hw, u32 offset, u32 value);
 u16 e1000_rxpbs_adjust_82580(u32 data);
 s32 e1000_read_emi_reg(struct e1000_hw *hw, u16 addr, u16 *data);
diff --git a/sys/dev/e1000/e1000_hw.h b/sys/dev/e1000/e1000_hw.h
index 43f41869a6f5..980aaedf845b 100644
--- a/sys/dev/e1000/e1000_hw.h
+++ b/sys/dev/e1000/e1000_hw.h
@@ -944,13 +944,14 @@ struct e1000_fc_info {
 
 struct e1000_mbx_operations {
 	s32 (*init_params)(struct e1000_hw *hw);
-	s32 (*read)(struct e1000_hw *, u32 *, u16,  u16);
+	s32 (*read)(struct e1000_hw *, u32 *, u16, u16, bool);
 	s32 (*write)(struct e1000_hw *, u32 *, u16, u16);
 	s32 (*read_posted)(struct e1000_hw *, u32 *, u16,  u16);
 	s32 (*write_posted)(struct e1000_hw *, u32 *, u16, u16);
 	s32 (*check_for_msg)(struct e1000_hw *, u16);
 	s32 (*check_for_ack)(struct e1000_hw *, u16);
 	s32 (*check_for_rst)(struct e1000_hw *, u16);
+	s32 (*unlock)(struct e1000_hw *, u16);
 };
 
 struct e1000_mbx_stats {
diff --git a/sys/dev/e1000/e1000_mbx.c b/sys/dev/e1000/e1000_mbx.c
index 97097fd777a1..985d315d527e 100644
--- a/sys/dev/e1000/e1000_mbx.c
+++ b/sys/dev/e1000/e1000_mbx.c
@@ -64,6 +64,17 @@ static s32 e1000_null_mbx_transact(struct e1000_hw E1000_UNUSEDARG *hw,
 	return E1000_SUCCESS;
 }
 
+static s32 e1000_null_mbx_read(struct e1000_hw E1000_UNUSEDARG *hw,
+			       u32 E1000_UNUSEDARG *msg,
+			       u16 E1000_UNUSEDARG size,
+			       u16 E1000_UNUSEDARG mbx_id,
+			       bool E1000_UNUSEDARG unlock)
+{
+	DEBUGFUNC("e1000_null_mbx_read");
+
+	return E1000_SUCCESS;
+}
+
 /**
  *  e1000_read_mbx - Reads a message from the mailbox
  *  @hw: pointer to the HW structure
@@ -73,7 +84,8 @@ static s32 e1000_null_mbx_transact(struct e1000_hw E1000_UNUSEDARG *hw,
  *
  *  returns SUCCESS if it successfully read message from buffer
  **/
-s32 e1000_read_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id)
+s32 e1000_read_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id,
+		   bool unlock)
 {
 	struct e1000_mbx_info *mbx = &hw->mbx;
 	s32 ret_val = -E1000_ERR_MBX;
@@ -85,7 +97,7 @@ s32 e1000_read_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id)
 		size = mbx->size;
 
 	if (mbx->ops.read)
-		ret_val = mbx->ops.read(hw, msg, size, mbx_id);
+		ret_val = mbx->ops.read(hw, msg, size, mbx_id, unlock);
 
 	return ret_val;
 }
@@ -175,6 +187,24 @@ s32 e1000_check_for_rst(struct e1000_hw *hw, u16 mbx_id)
 	return ret_val;
 }
 
+/**
+ *  e1000_unlock_mbx - release mailbox ownership
+ *  @hw: pointer to the HW structure
+ *  @mbx_id: id of mailbox to unlock
+ **/
+s32 e1000_unlock_mbx(struct e1000_hw *hw, u16 mbx_id)
+{
+	struct e1000_mbx_info *mbx = &hw->mbx;
+	s32 ret_val = -E1000_ERR_MBX;
+
+	DEBUGFUNC("e1000_unlock_mbx");
+
+	if (mbx->ops.unlock)
+		ret_val = mbx->ops.unlock(hw, mbx_id);
+
+	return (ret_val);
+}
+
 /**
  *  e1000_poll_for_msg - Wait for message notification
  *  @hw: pointer to the HW structure
@@ -261,7 +291,7 @@ s32 e1000_read_posted_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id)
 
 	/* if ack received read message, otherwise we timed out */
 	if (!ret_val)
-		ret_val = mbx->ops.read(hw, msg, size, mbx_id);
+		ret_val = mbx->ops.read(hw, msg, size, mbx_id, true);
 out:
 	return ret_val;
 }
@@ -307,11 +337,12 @@ void e1000_init_mbx_ops_generic(struct e1000_hw *hw)
 {
 	struct e1000_mbx_info *mbx = &hw->mbx;
 	mbx->ops.init_params = e1000_null_ops_generic;
-	mbx->ops.read = e1000_null_mbx_transact;
+	mbx->ops.read = e1000_null_mbx_read;
 	mbx->ops.write = e1000_null_mbx_transact;
 	mbx->ops.check_for_msg = e1000_null_mbx_check_for_flag;
 	mbx->ops.check_for_ack = e1000_null_mbx_check_for_flag;
 	mbx->ops.check_for_rst = e1000_null_mbx_check_for_flag;
+	mbx->ops.unlock = e1000_null_mbx_check_for_flag;
 	mbx->ops.read_posted = e1000_read_posted_mbx;
 	mbx->ops.write_posted = e1000_write_posted_mbx;
 }
@@ -500,7 +531,8 @@ out_no_write:
  *  returns SUCCESS if it successfully read message from buffer
  **/
 static s32 e1000_read_mbx_vf(struct e1000_hw *hw, u32 *msg, u16 size,
-			     u16 E1000_UNUSEDARG mbx_id)
+			     u16 E1000_UNUSEDARG mbx_id,
+			     bool E1000_UNUSEDARG unlock)
 {
 	s32 ret_val = E1000_SUCCESS;
 	u16 i;
@@ -649,28 +681,37 @@ static s32 e1000_obtain_mbx_lock_pf(struct e1000_hw *hw, u16 vf_number)
 {
 	s32 ret_val = -E1000_ERR_MBX;
 	u32 p2v_mailbox;
-	int count = 10;
 
 	DEBUGFUNC("e1000_obtain_mbx_lock_pf");
 
-	do {
-		/* Take ownership of the buffer */
-		E1000_WRITE_REG(hw, E1000_P2VMAILBOX(vf_number),
-				E1000_P2VMAILBOX_PFU);
-
-		/* reserve mailbox for pf use */
-		p2v_mailbox = E1000_READ_REG(hw, E1000_P2VMAILBOX(vf_number));
-		if (p2v_mailbox & E1000_P2VMAILBOX_PFU) {
-			ret_val = E1000_SUCCESS;
-			break;
-		}
-		usec_delay(1000);
-	} while (count-- > 0);
+	/*
+	 * A VF request releases VFU as it raises REQ.  If the VF still owns
+	 * the buffer, leave the request for a later admin pass rather than
+	 * sleeping under the PF's context lock and delaying every other VF.
+	 */
+	E1000_WRITE_REG(hw, E1000_P2VMAILBOX(vf_number),
+	    E1000_P2VMAILBOX_PFU);
+	p2v_mailbox = E1000_READ_REG(hw, E1000_P2VMAILBOX(vf_number));
+	if (p2v_mailbox & E1000_P2VMAILBOX_PFU)
+		ret_val = E1000_SUCCESS;
 
 	return ret_val;
 
 }
 
+static s32
+e1000_release_mbx_lock_pf(struct e1000_hw *hw, u16 vf_number)
+{
+	u32 p2v_mailbox;
+
+	p2v_mailbox = E1000_READ_REG(hw, E1000_P2VMAILBOX(vf_number));
+	if (p2v_mailbox & E1000_P2VMAILBOX_PFU)
+		E1000_WRITE_REG(hw, E1000_P2VMAILBOX(vf_number),
+		    p2v_mailbox & ~E1000_P2VMAILBOX_PFU);
+
+	return (E1000_SUCCESS);
+}
+
 /**
  *  e1000_write_mbx_pf - Places a message in the mailbox
  *  @hw: pointer to the HW structure
@@ -693,6 +734,17 @@ static s32 e1000_write_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size,
 	if (ret_val)
 		goto out_no_write;
 
+	/*
+	 * A VF request wins over an asynchronous PF message.  Do not clear
+	 * VFREQ here: the PF mailbox handler still needs to consume it.
+	 */
+	if (E1000_READ_REG(hw, E1000_MBVFICR) &
+	    (E1000_MBVFICR_VFREQ_VF1 << vf_number)) {
+		e1000_release_mbx_lock_pf(hw, vf_number);
+		ret_val = -E1000_ERR_MBX;
+		goto out_no_write;
+	}
+
 	/* flush msg and acks as we are overwriting the message buffer */
 	e1000_check_for_msg_pf(hw, vf_number);
 	e1000_check_for_ack_pf(hw, vf_number);
@@ -724,7 +776,7 @@ out_no_write:
  *  a message due to a VF request so no polling for message is needed.
  **/
 static s32 e1000_read_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size,
-			     u16 vf_number)
+			     u16 vf_number, bool unlock)
 {
 	s32 ret_val;
 	u16 i;
@@ -736,12 +788,21 @@ static s32 e1000_read_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size,
 	if (ret_val)
 		goto out_no_read;
 
+	/*
+	 * A second VF request can arrive while the PF retries ownership.  PFU
+	 * now keeps VMBMEM stable, so consume any reasserted VFREQ before
+	 * reading the request that it describes.
+	 */
+	(void)e1000_check_for_msg_pf(hw, vf_number);
+
 	/* copy the message to the mailbox memory buffer */
 	for (i = 0; i < size; i++)
 		msg[i] = E1000_READ_REG_ARRAY(hw, E1000_VMBMEM(vf_number), i);
 
-	/* Acknowledge the message and release buffer */
-	E1000_WRITE_REG(hw, E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_ACK);
+	/* Acknowledge the message and optionally retain PF ownership. */
+	E1000_WRITE_REG(hw, E1000_P2VMAILBOX(vf_number),
+	    E1000_P2VMAILBOX_ACK |
+	    (unlock ? 0 : E1000_P2VMAILBOX_PFU));
 
 	/* update stats */
 	hw->mbx.stats.msgs_rx++;
@@ -776,6 +837,7 @@ s32 e1000_init_mbx_params_pf(struct e1000_hw *hw)
 		mbx->ops.check_for_msg = e1000_check_for_msg_pf;
 		mbx->ops.check_for_ack = e1000_check_for_ack_pf;
 		mbx->ops.check_for_rst = e1000_check_for_rst_pf;
+		mbx->ops.unlock = e1000_release_mbx_lock_pf;
 
 		mbx->stats.msgs_tx = 0;
 		mbx->stats.msgs_rx = 0;
@@ -787,4 +849,3 @@ s32 e1000_init_mbx_params_pf(struct e1000_hw *hw)
 		return E1000_SUCCESS;
 	}
 }
-
diff --git a/sys/dev/e1000/e1000_mbx.h b/sys/dev/e1000/e1000_mbx.h
index 61f8d5cbe265..edd74a49f00b 100644
--- a/sys/dev/e1000/e1000_mbx.h
+++ b/sys/dev/e1000/e1000_mbx.h
@@ -77,6 +77,8 @@
 
 #define E1000_VF_RESET			0x01 /* VF requests reset */
 #define E1000_VF_SET_MAC_ADDR		0x02 /* VF requests to set MAC addr */
+#define E1000_VF_MAC_FILTER_CLR		(0x01 << E1000_VT_MSGINFO_SHIFT)
+#define E1000_VF_MAC_FILTER_ADD		(0x02 << E1000_VT_MSGINFO_SHIFT)
 #define E1000_VF_SET_MULTICAST		0x03 /* VF requests to set MC addr */
 #define E1000_VF_SET_MULTICAST_COUNT_MASK (0x1F << E1000_VT_MSGINFO_SHIFT)
 #define E1000_VF_SET_MULTICAST_OVERFLOW	(0x80 << E1000_VT_MSGINFO_SHIFT)
@@ -92,13 +94,14 @@
 #define E1000_VF_MBX_INIT_TIMEOUT	2000 /* number of retries on mailbox */
 #define E1000_VF_MBX_INIT_DELAY		500  /* microseconds between retries */
 
-s32 e1000_read_mbx(struct e1000_hw *, u32 *, u16, u16);
+s32 e1000_read_mbx(struct e1000_hw *, u32 *, u16, u16, bool);
 s32 e1000_write_mbx(struct e1000_hw *, u32 *, u16, u16);
 s32 e1000_read_posted_mbx(struct e1000_hw *, u32 *, u16, u16);
 s32 e1000_write_posted_mbx(struct e1000_hw *, u32 *, u16, u16);
 s32 e1000_check_for_msg(struct e1000_hw *, u16);
 s32 e1000_check_for_ack(struct e1000_hw *, u16);
 s32 e1000_check_for_rst(struct e1000_hw *, u16);
+s32 e1000_unlock_mbx(struct e1000_hw *, u16);
 void e1000_init_mbx_ops_generic(struct e1000_hw *hw);
 s32 e1000_init_mbx_params_vf(struct e1000_hw *);
 s32 e1000_init_mbx_params_pf(struct e1000_hw *);
diff --git a/sys/dev/e1000/e1000_osdep.h b/sys/dev/e1000/e1000_osdep.h
index bac71d34de45..b3b16b102edb 100644
--- a/sys/dev/e1000/e1000_osdep.h
+++ b/sys/dev/e1000/e1000_osdep.h
@@ -162,10 +162,113 @@ struct e1000_osdep
 	device_t	   dev;
 	if_ctx_t	   ctx;
 	bool		   pcim2pci_arbiter_wa;
+	bool		   vf;
+	bool		   vf_82576;
 };
 
 void e1000_pcim2pci_write(struct e1000_osdep *, uint32_t, uint32_t);
 
+#ifdef INVARIANTS
+/*
+ * 82576 and I350 VFs expose a sparse register file.  Keep this list local to
+ * the OS accessors so a PF-only register that leaks into the shared VF path
+ * fails at its first access instead of returning reserved-register garbage.
+ *
+ * The driver intentionally uses only queue pair zero.  It also clears the
+ * retained configuration of the unused second 82576 queue pair after VFLR.
+ * Expanding VF data-path queue support must extend this predicate from the
+ * applicable device CSR map.
+ */
+static __inline bool
+e1000_vf_reg_valid(uint32_t reg, bool write, bool vf_82576)
+{
+	/* VF mailbox memory: 16 dwords beginning at 0x800. */
+	if (reg >= 0x00800 && reg <= 0x0083c && (reg & 3) == 0)
+		return (true);
+
+	/* The three VF MSI-X interrupt-throttling registers. */
+	if (reg >= 0x01680 && reg <= 0x01688 && (reg & 3) == 0)
+		return (true);
+
+	/* Receive queue zero. */
+	switch (reg) {
+	case 0x02800:	/* RDBAL */
+	case 0x02804:	/* RDBAH */
+	case 0x02808:	/* RDLEN */
+	case 0x0280c:	/* SRRCTL */
+	case 0x02810:	/* RDH */
+	case 0x02814:	/* RXCTL */
+	case 0x02818:	/* RDT */
+	case 0x02828:	/* RXDCTL */
+		return (true);
+	}
+
+	/* Transmit queue zero. */
+	switch (reg) {
+	case 0x03800:	/* TDBAL */
+	case 0x03804:	/* TDBAH */
+	case 0x03808:	/* TDLEN */
+	case 0x03810:	/* TDH */
+	case 0x03814:	/* TXCTL */
+	case 0x03818:	/* TDT */
+	case 0x03828:	/* TXDCTL */
+	case 0x03838:	/* TDWBAL */
+	case 0x0383c:	/* TDWBAH */
+		return (true);
+	}
+
+	/* Retained configuration from 82576 virtual queue one. */
+	if (vf_82576) {
+		switch (reg) {
+		case 0x0290c:	/* SRRCTL(1) */
+		case 0x02914:	/* RXCTL(1) */
+		case 0x02928:	/* RXDCTL(1) */
+		case 0x03914:	/* TXCTL(1) */
+		case 0x03928:	/* TXDCTL(1) */
+		case 0x03938:	/* TDWBAL(1) */
+		case 0x0393c:	/* TDWBAH(1) */
+			return (true);
+		}
+	}
+
+	/*
+	 * 82576 exposes VFMPRC at 0xf3c.  I350 erratum 31 makes
+	 * its corrected 0xf38 address inaccessible to a VF.
+	 */
+	if (vf_82576 && reg == 0x00f3c)
+		return (!write);
+
+	switch (reg) {
+	case 0x00000:	/* CTRL */
+	case 0x000c4:	/* Legacy ITR, listed but unused by igb VFs */
+	case 0x00c40:	/* V2PMAILBOX(0) */
+	case 0x00f0c:	/* VFPSRTYPE */
+	case 0x01524:	/* EIMS */
+	case 0x0152c:	/* EIAC */
+	case 0x01530:	/* EIAM */
+	case 0x01700:	/* IVAR0 */
+	case 0x01740:	/* IVAR_MISC */
+		return (true);
+	case 0x01520:	/* EICS */
+	case 0x01528:	/* EIMC */
+		return (write);
+	case 0x00008:	/* STATUS */
+	case 0x00f10:	/* VFGPRC */
+	case 0x00f14:	/* VFGPTC */
+	case 0x00f18:	/* VFGORC */
+	case 0x00f34:	/* VFGOTC */
+	case 0x00f40:	/* VFGPRLBC */
+	case 0x00f44:	/* VFGPTLBC */
+	case 0x00f48:	/* VFGORLBC */
+	case 0x00f50:	/* VFGOTLBC */
+	case 0x01580:	/* EICR */
+		return (!write);
+	default:
+		return (false);
+	}
+}
+#endif
+
 #define E1000_REGISTER(hw, reg) (((hw)->mac.type >= e1000_82543) \
     ? reg : e1000_translate_register_82542(reg))
 
@@ -183,6 +286,11 @@ e1000_rd32(struct e1000_osdep *osdep, uint32_t reg)
 	KASSERT(reg < osdep->mem_bus_space_size,
 	    ("e1000: register offset %#jx too large (max is %#jx)",
 	    (uintmax_t)reg, (uintmax_t)osdep->mem_bus_space_size));
+#ifdef INVARIANTS
+	KASSERT(!osdep->vf ||
+	    e1000_vf_reg_valid(reg, false, osdep->vf_82576),
+	    ("e1000: invalid VF register read at %#x", reg));
+#endif
 
 	return (bus_space_read_4(osdep->mem_bus_space_tag,
 	    osdep->mem_bus_space_handle, reg));
@@ -196,6 +304,11 @@ e1000_wr32(struct e1000_osdep *osdep, uint32_t reg, uint32_t value)
 	KASSERT(reg < osdep->mem_bus_space_size,
 	    ("e1000: register offset %#jx too large (max is %#jx)",
 	    (uintmax_t)reg, (uintmax_t)osdep->mem_bus_space_size));
+#ifdef INVARIANTS
+	KASSERT(!osdep->vf ||
+	    e1000_vf_reg_valid(reg, true, osdep->vf_82576),
+	    ("e1000: invalid VF register write at %#x", reg));
+#endif
 
 	if (__predict_true(!osdep->pcim2pci_arbiter_wa)) {
 		bus_space_write_4(osdep->mem_bus_space_tag,
@@ -283,4 +396,3 @@ e1000_wr32(struct e1000_osdep *osdep, uint32_t reg, uint32_t value)
 #endif
 
 #endif  /* _FREEBSD_OS_H_ */
-
diff --git a/sys/dev/e1000/e1000_regs.h b/sys/dev/e1000/e1000_regs.h
index 3cb2d62a5a4c..b70fc4aa1255 100644
--- a/sys/dev/e1000/e1000_regs.h
+++ b/sys/dev/e1000/e1000_regs.h
@@ -299,6 +299,7 @@
 #define E1000_TXDMAC		0x03000  /* Tx DMA Control - RW */
 #define E1000_KABGTXD		0x03004  /* AFE Band Gap Transmit Ref Data */
 #define E1000_PSRTYPE(_i)	(0x05480 + ((_i) * 4))
+#define E1000_VFPSRTYPE		0x00F0C
 #define E1000_RAL(_i)		(((_i) <= 15) ? (0x05400 + ((_i) * 8)) : \
 				 (0x054E0 + ((_i - 16) * 8)))
 #define E1000_RAH(_i)		(((_i) <= 15) ? (0x05404 + ((_i) * 8)) : \
diff --git a/sys/dev/e1000/e1000_vf.c b/sys/dev/e1000/e1000_vf.c
index 5b19375e6034..9528ab6009cb 100644
--- a/sys/dev/e1000/e1000_vf.c
+++ b/sys/dev/e1000/e1000_vf.c
@@ -260,7 +260,7 @@ static s32 e1000_reset_hw_vf(struct e1000_hw *hw)
 	struct e1000_mbx_info *mbx = &hw->mbx;
 	u32 timeout = E1000_VF_INIT_TIMEOUT;
 	s32 ret_val = -E1000_ERR_MAC_INIT;
-	u32 ctrl, msgbuf[3];
+	u32 ctrl, msgbuf[3] = { E1000_VF_RESET, ~0U, ~0U };
 	u8 *addr = (u8 *)(&msgbuf[1]);
 
 	DEBUGFUNC("e1000_reset_hw_vf");
@@ -275,23 +275,40 @@ static s32 e1000_reset_hw_vf(struct e1000_hw *hw)
 		usec_delay(5);
 	}
 
-	if (timeout) {
-		/* mailbox timeout can now become active */
-		mbx->timeout = E1000_VF_MBX_INIT_TIMEOUT;
+	if (!timeout)
+		return -E1000_ERR_RESET;
 
-		msgbuf[0] = E1000_VF_RESET;
-		mbx->ops.write_posted(hw, msgbuf, 1, 0);
+	/* mailbox timeout can now become active */
+	mbx->timeout = E1000_VF_MBX_INIT_TIMEOUT;
 
-		msec_delay(10);
-
-		/* set our "perm_addr" based on info provided by PF */
-		ret_val = mbx->ops.read_posted(hw, msgbuf, 3, 0);
-		if (!ret_val) {
-			if (msgbuf[0] == (E1000_VF_RESET |
-			    E1000_VT_MSGTYPE_ACK))
-				memcpy(hw->mac.perm_addr, addr, 6);
+	/*
+	 * Linux PFs return a three-dword, zero-filled NACK when the reset
+	 * completed without an assigned MAC address.  FreeBSD PFs also use a
+	 * one-dword NACK while retained queues are still being sanitized.  Seed
+	 * the unused request payload so the two responses remain distinguishable.
+	 */
+	ret_val = mbx->ops.write_posted(hw, msgbuf, 3, 0);
+	if (ret_val)
+		return ret_val;
+
+	msec_delay(10);
+
+	/* set our "perm_addr" based on info provided by PF */
+	ret_val = mbx->ops.read_posted(hw, msgbuf, 3, 0);
+	if (!ret_val) {
+		switch (msgbuf[0]) {
+		case E1000_VF_RESET | E1000_VT_MSGTYPE_ACK:
+			memcpy(hw->mac.perm_addr, addr, ETHER_ADDR_LEN);
+			break;
+		case E1000_VF_RESET | E1000_VT_MSGTYPE_NACK:
+			if (msgbuf[1] == 0 && msgbuf[2] == 0)
+				memset(hw->mac.perm_addr, 0, ETHER_ADDR_LEN);
 			else
 				ret_val = -E1000_ERR_MAC_INIT;
+			break;
+		default:
+			ret_val = -E1000_ERR_MAC_INIT;
+			break;
 		}
 	}
 
@@ -390,6 +407,41 @@ static void e1000_write_msg_read_ack(struct e1000_hw *hw,
 		mbx->ops.read_posted(hw, retmsg, E1000_VFMAILBOX_SIZE, 0);
 }
 
+/**
+ *  e1000_set_uc_addr_vf - Add or clear secondary unicast addresses
+ *  @hw: pointer to the HW structure
+ *  @sub_cmd: E1000_VF_MAC_FILTER_ADD or E1000_VF_MAC_FILTER_CLR
+ *  @addr: address to add, or a valid compatibility address when clearing
+ *
+ *  Uses the secondary-MAC mailbox subprotocol implemented by Linux igbvf.
+ *  Linux igb PFs validate this field before dispatching the clear subcommand,
+ *  even though they do not otherwise use it for a clear request.
+ **/
+s32
+e1000_set_uc_addr_vf(struct e1000_hw *hw, u32 sub_cmd, u8 *addr)
+{
+	struct e1000_mbx_info *mbx = &hw->mbx;
+	u32 msgbuf[3] = {};
+	u32 request;
+	s32 ret_val;
+
+	msgbuf[0] = E1000_VF_SET_MAC_ADDR | sub_cmd;
+	request = msgbuf[0];
+	if (addr != NULL)
+		memcpy(&msgbuf[1], addr, ETHER_ADDR_LEN);
+
+	ret_val = mbx->ops.write_posted(hw, msgbuf, 3, 0);
+	if (ret_val == E1000_SUCCESS)
+		ret_val = mbx->ops.read_posted(hw, msgbuf, 3, 0);
+
+	msgbuf[0] &= ~E1000_VT_MSGTYPE_CTS;
+	if (ret_val == E1000_SUCCESS &&
+	    msgbuf[0] == (request | E1000_VT_MSGTYPE_NACK))
+		ret_val = -E1000_ERR_NO_SPACE;
+
+	return (ret_val);
+}
+
 /**
  *  e1000_update_mc_addr_list_vf - Update Multicast addresses
  *  @hw: pointer to the HW structure
@@ -402,7 +454,7 @@ static void e1000_write_msg_read_ack(struct e1000_hw *hw,
 void e1000_update_mc_addr_list_vf(struct e1000_hw *hw,
 				  u8 *mc_addr_list, u32 mc_addr_count)
 {
-	u32 msgbuf[E1000_VFMAILBOX_SIZE];
+	u32 msgbuf[E1000_VFMAILBOX_SIZE] = {};
 	u16 *hash_list = (u16 *)&msgbuf[1];
 	u32 hash_value;
 	u32 i;
@@ -444,10 +496,14 @@ void e1000_update_mc_addr_list_vf(struct e1000_hw *hw,
  *  @hw: pointer to the HW structure
  *  @vid: determines the vfta register and bit to set/unset
  *  @set: if true then set bit, else clear bit
+ *
+ *  Returns success if the PF accepted the request, or an error otherwise.
  **/
-void e1000_vfta_set_vf(struct e1000_hw *hw, u16 vid, bool set)
+s32 e1000_vfta_set_vf(struct e1000_hw *hw, u16 vid, bool set)
 {
+	struct e1000_mbx_info *mbx = &hw->mbx;
 	u32 msgbuf[2];
+	s32 ret_val;
 
 	msgbuf[0] = E1000_VF_SET_VLAN;
 	msgbuf[1] = vid;
@@ -455,7 +511,15 @@ void e1000_vfta_set_vf(struct e1000_hw *hw, u16 vid, bool set)
 	if (set)
 		msgbuf[0] |= E1000_VF_SET_VLAN_ADD;
 
-	e1000_write_msg_read_ack(hw, msgbuf, 2);
*** 6279 LINES SKIPPED ***