Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 30 Jul 2026 05:11:12 +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: e8f3b96ba794 - main - igb: Rate-limit VF VLAN rebuild requests
Message-ID:  <6a6adcf0.3337e.7f35a7b2@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=e8f3b96ba794521af9904a451da33cf119381da8

commit e8f3b96ba794521af9904a451da33cf119381da8
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-07-29 08:04:40 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
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))


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a6adcf0.3337e.7f35a7b2>