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>
