The branch main has been updated by kbowling:

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

commit e8f3b96ba794521af9904a451da33cf119381da8
Author:     Kevin Bowling <[email protected]>
AuthorDate: 2026-07-29 08:04:40 +0000
Commit:     Kevin Bowling <[email protected]>
CommitDate: 2026-07-30 05:10:00 +0000

    igb: Rate-limit VF VLAN rebuild requests
    
    Give each VF a burst of 64 VLAN additions and refill it at eight
    additions per second. Removals remain unrestricted, idempotent requests
    consume nothing, and trusted PF-wide initialization replenishes the
    burst while guest resets do not.  Checks VLVF capacity before charging
    a token.
    
    Do not apply this policy to multicast requests because Linux igbvf and
    DPDK ignore their reply status; aggregate MTA write elision bounds
    those updates instead.
    
    Sponsored by:   BBOX.io
---
 sys/dev/e1000/if_igb_iov.c | 62 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 57 insertions(+), 5 deletions(-)

diff --git a/sys/dev/e1000/if_igb_iov.c b/sys/dev/e1000/if_igb_iov.c
index 9b2c77a92eb7..f43e994c0440 100644
--- a/sys/dev/e1000/if_igb_iov.c
+++ b/sys/dev/e1000/if_igb_iov.c
@@ -19,6 +19,9 @@
 #define        IGB_IOV_MAX_MAC_FILTERS         3
 #define        IGB_IOV_MAX_MC_HASHES           30
 #define        IGB_IOV_MBX_RETRY_COUNT         6
+/* Allow two complete 31-VID replays, then sustain eight additions/second. */
+#define        IGB_IOV_VLAN_CHANGE_BURST       64
+#define        IGB_IOV_VLAN_CHANGE_INTERVAL    (SBT_1S / 8)
 /* 82576 Datasheet rev. 2.0, Section 8.14.16: VMOLR[31] must be one. */
 #define        IGB_82576_VMOLR_RSV             (1U << 31)
 #define        IGB_82576_LVMMC_BLOCK_MASK      0x1c
@@ -58,11 +61,13 @@ struct igb_vf {
        struct timeval  last_mdd_log;
        sbintime_t      mbx_retry_at;
        sbintime_t      mdd_notify_at;
+       sbintime_t      vlan_token_time;
        u16     pool;
        u16     rar_index;
        u16     max_frame_size;
        u16     mc_count;
        u16     vlan_count;
+       u16     vlan_tokens;
        u16     default_vlan;
        u8      mbx_retry_count;
        u8      mac[ETHER_ADDR_LEN];
@@ -167,6 +172,34 @@ igb_iov_nack_allowed(struct igb_vf *vf)
        return (ratecheck(&vf->last_nack, &igb_iov_nack_interval) != 0);
 }
 
+static void
+igb_iov_reset_vlan_rate(struct igb_vf *vf)
+{
+
+       vf->vlan_token_time = getsbinuptime();
+       vf->vlan_tokens = IGB_IOV_VLAN_CHANGE_BURST;
+}
+
+static bool
+igb_iov_vlan_add_allowed(struct igb_vf *vf)
+{
+       sbintime_t elapsed, now;
+       uint64_t refill;
+
+       now = getsbinuptime();
+       elapsed = now - vf->vlan_token_time;
+       if (elapsed >= IGB_IOV_VLAN_CHANGE_INTERVAL) {
+               refill = elapsed / IGB_IOV_VLAN_CHANGE_INTERVAL;
+               vf->vlan_tokens = min((uint64_t)IGB_IOV_VLAN_CHANGE_BURST,
+                   vf->vlan_tokens + refill);
+               vf->vlan_token_time = now;
+       }
+       if (vf->vlan_tokens == 0)
+               return (false);
+       vf->vlan_tokens--;
+       return (true);
+}
+
 static u32
 igb_iov_reply_header(u32 request, bool cts, bool ack)
 {
@@ -642,7 +675,6 @@ igb_iov_rebuild_mta(struct e1000_softc *sc)
                        mta[(hash >> 5) & (hw->mac.mta_reg_count - 1)] |=
                            1U << (hash & 0x1f);
                }
-               igb_iov_configure_vmolr(sc, vf);
        }
 
        changed = false;
@@ -881,12 +913,17 @@ igb_iov_vlan_present(struct e1000_softc *sc, u16 vid, 
bool include_pf)
 static int
 igb_iov_vlan_unique_count(struct e1000_softc *sc, bool include_pf)
 {
-       int count, vid;
+       u32 vlans;
+       int count, i, word;
 
        count = 0;
-       for (vid = 0; vid < 4096; vid++)
-               if (igb_iov_vlan_present(sc, vid, include_pf))
-                       count++;
+       for (word = 0; word < EM_VFTA_SIZE; word++) {
+               vlans = include_pf ? sc->shadow_vfta[word] : 0;
+               for (i = 0; i < sc->num_vfs; i++)
+                       if ((sc->vfs[i].flags & IGB_VF_ACTIVE) != 0)
+                               vlans |= sc->vfs[i].vlans[word];
+               count += bitcount32(vlans);
+       }
        return (count);
 }
 
@@ -909,10 +946,17 @@ igb_iov_set_vlan(struct e1000_softc *sc, struct igb_vf 
*vf, u16 vid,
        if (add == present)
                return (0);
 
+       /*
+        * Removals always reduce privilege and remain available.  Charge only
+        * additions, which a hostile VF must alternate with removals to force
+        * repeated global VLAN rebuilds.
+        */
        if (add && !igb_iov_vlan_present(sc, vid, false) &&
            igb_iov_vlan_unique_count(sc, false) >=
            E1000_VLVF_ARRAY_SIZE)
                return (ENOSPC);
+       if (add && !igb_iov_vlan_add_allowed(vf))
+               return (EBUSY);
 
        if (add) {
                vf->vlans[vid >> 5] |= bit;
@@ -1170,6 +1214,7 @@ igb_iov_set_multicast(struct e1000_softc *sc, struct 
igb_vf *vf, u32 *msg)
                    "VF %u multicast list exceeds 30 entries; "
                    "enabling all-multicast reception\n", vf->pool);
        }
+       igb_iov_configure_vmolr(sc, vf);
        igb_iov_rebuild_mta(sc);
        return (0);
 }
@@ -1660,6 +1705,12 @@ igb_iov_initialize(struct e1000_softc *sc)
                vf = &sc->vfs[i];
                if (!(vf->flags & IGB_VF_ACTIVE))
                        continue;
+               /*
+                * A PF-wide reset is trusted and can require a complete guest
+                * replay.  Guest-controlled RESET and VFLR do not refill this
+                * allowance.
+                */
+               igb_iov_reset_vlan_rate(vf);
                igb_iov_clear_mac_filters(sc, vf);
                igb_iov_reset_vf_state(sc, vf);
                igb_iov_clear_rar(sc, vf->rar_index);
@@ -1875,6 +1926,7 @@ igb_if_iov_vf_add(if_ctx_t ctx, u16 vfnum, const nvlist_t 
*config)
        vf->rar_index = sc->hw.mac.rar_entry_count - (vfnum + 1);
        vf->max_frame_size = ETHER_MAX_LEN;
        vf->default_vlan = vlan;
+       igb_iov_reset_vlan_rate(vf);
        if (nvlist_exists_binary(config, "mac-addr")) {
                mac = nvlist_get_binary(config, "mac-addr", &mac_size);
                if (mac_size != ETHER_ADDR_LEN || !igb_iov_mac_valid(mac))

Reply via email to