The branch main has been updated by kbowling:

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

commit 9332fd555588ef4f7913664f8bdb57febc828e76
Author:     Kevin Bowling <[email protected]>
AuthorDate: 2026-07-29 04:22:32 +0000
Commit:     Kevin Bowling <[email protected]>
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)
 {

Reply via email to