git: 9332fd555588 - main - igbv: Support secondary unicast filters
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 30 Jul 2026 05:04:56 UTC
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)
{