Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 30 Jul 2026 05:04:56 +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: 9332fd555588 - main - igbv: Support secondary unicast filters
Message-ID:  <6a6adb78.32021.3f4bb023@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=9332fd555588ef4f7913664f8bdb57febc828e76

commit 9332fd555588ef4f7913664f8bdb57febc828e76
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-07-29 04:22:32 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-07-30 05:01:32 +0000

    igbv: Support secondary unicast filters
    
    Support the Linux igbvf secondary-MAC mailbox subprotocol, used by
    Linux guests running MacVTap.
    
    Replay up to three non-primary unicast addresses after reset and
    whenever the address list changes, subject to PF allow-set-mac policy.
    
    Sponsored by:   BBOX.io
---
 sys/dev/e1000/e1000_82575.h |  1 +
 sys/dev/e1000/e1000_vf.c    | 35 ++++++++++++++++++++++++
 sys/dev/e1000/e1000_vf.h    |  1 +
 sys/dev/e1000/if_em.c       |  1 +
 sys/dev/e1000/if_em.h       |  3 +++
 sys/dev/e1000/if_igbv.c     | 66 +++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 107 insertions(+)

diff --git a/sys/dev/e1000/e1000_82575.h b/sys/dev/e1000/e1000_82575.h
index 720ecf8fc817..c919a8064476 100644
--- a/sys/dev/e1000/e1000_82575.h
+++ b/sys/dev/e1000/e1000_82575.h
@@ -405,6 +405,7 @@ enum e1000_promisc_type {
 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_vf.c b/sys/dev/e1000/e1000_vf.c
index d25dc7a23056..d48a4153c1b3 100644
--- a/sys/dev/e1000/e1000_vf.c
+++ b/sys/dev/e1000/e1000_vf.c
@@ -388,6 +388,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
diff --git a/sys/dev/e1000/e1000_vf.h b/sys/dev/e1000/e1000_vf.h
index b509ba48eb0b..17116a86b084 100644
--- a/sys/dev/e1000/e1000_vf.h
+++ b/sys/dev/e1000/e1000_vf.h
@@ -294,4 +294,5 @@ s32  e1000_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value);
 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);
+s32 e1000_set_uc_addr_vf(struct e1000_hw *, u32, u8 *);
 #endif /* _E1000_VF_H_ */
diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c
index 9f9d3dc3024e..4f4eeee5da50 100644
--- a/sys/dev/e1000/if_em.c
+++ b/sys/dev/e1000/if_em.c
@@ -2550,6 +2550,7 @@ em_if_multi_set(if_ctx_t ctx)
 
 	if (sc->vf_ifp) {
 		e1000_update_mc_addr_list(&sc->hw, mta, mcnt);
+		igbv_update_uc_addr_list(sc, ifp);
 		return;
 	}
 
diff --git a/sys/dev/e1000/if_em.h b/sys/dev/e1000/if_em.h
index dbc3e945f78b..93ddf0bfc25a 100644
--- a/sys/dev/e1000/if_em.h
+++ b/sys/dev/e1000/if_em.h
@@ -643,6 +643,8 @@ struct e1000_softc {
 
 	u16			vf_ifp;
 	bool			vf_reset_pending;
+	/* A PF can retain auxiliary filters across a VF reset. */
+	bool			vf_uc_filters_set;
 };
 
 /*
@@ -669,6 +671,7 @@ 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);
+void	igbv_update_uc_addr_list(struct e1000_softc *, if_t);
 
 /********************************************************************************
  * vendor_info_array
diff --git a/sys/dev/e1000/if_igbv.c b/sys/dev/e1000/if_igbv.c
index 78b07d204aef..4f869388ce81 100644
--- a/sys/dev/e1000/if_igbv.c
+++ b/sys/dev/e1000/if_igbv.c
@@ -30,6 +30,13 @@
 
 #include <sys/sbuf.h>
 
+#define	IGBV_MAX_MAC_FILTERS	3
+
+struct igb_vf_uc_addr_list {
+	struct e1000_softc	*sc;
+	u8			addrs[IGBV_MAX_MAC_FILTERS][ETHER_ADDR_LEN];
+};
+
 static bool	igbv_tx_pending(struct e1000_softc *);
 
 int
@@ -293,6 +300,65 @@ igbv_get_regs(SYSCTL_HANDLER_ARGS)
 	return (error);
 }
 
+static u_int
+igbv_copy_uc_addr(void *arg, struct sockaddr_dl *sdl, u_int idx)
+{
+	struct igb_vf_uc_addr_list *list;
+	const u8 *addr;
+
+	list = arg;
+	addr = (const u8 *)LLADDR(sdl);
+	if (memcmp(addr, list->sc->hw.mac.addr, ETHER_ADDR_LEN) == 0)
+		return (0);
+	if (idx < IGBV_MAX_MAC_FILTERS)
+		memcpy(list->addrs[idx], addr, ETHER_ADDR_LEN);
+	return (1);
+}
+
+void
+igbv_update_uc_addr_list(struct e1000_softc *sc, if_t ifp)
+{
+	struct igb_vf_uc_addr_list list = {
+		.sc = sc,
+	};
+	u_int count;
+
+	count = if_foreach_lladdr(ifp, igbv_copy_uc_addr, &list);
+	if (count > IGBV_MAX_MAC_FILTERS) {
+		device_printf(sc->dev,
+		    "too many secondary unicast addresses; maximum is %u\n",
+		    IGBV_MAX_MAC_FILTERS);
+	}
+	if (count == 0 && !sc->vf_uc_filters_set)
+		return;
+	/*
+	 * Linux igb PFs validate the address field before dispatching the CLR
+	 * subcommand.  Supply the primary address rather than the zero payload
+	 * used by igbvf so those PFs actually remove the old filters.  FreeBSD
+	 * PFs dispatch CLR before inspecting the otherwise-ignored address.
+	 */
+	if (e1000_set_uc_addr_vf(&sc->hw, E1000_VF_MAC_FILTER_CLR,
+	    sc->hw.mac.addr) != E1000_SUCCESS) {
+		device_printf(sc->dev,
+		    "VF secondary unicast filter clear request failed\n");
+		return;
+	}
+	sc->vf_uc_filters_set = false;
+	if (count > IGBV_MAX_MAC_FILTERS)
+		return;
+
+	for (u_int i = 0; i < count; i++) {
+		if (e1000_set_uc_addr_vf(&sc->hw, E1000_VF_MAC_FILTER_ADD,
+		    list.addrs[i]) != E1000_SUCCESS) {
+			device_printf(sc->dev,
+			    "VF secondary unicast filter add request failed "
+			    "for %6D\n", list.addrs[i], ":");
+		} else
+			sc->vf_uc_filters_set = true;
+		usec_delay(200);
+	}
+}
+
 void
 igbv_reconcile_mac(struct e1000_softc *sc, if_t ifp)
 {


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a6adb78.32021.3f4bb023>