git: 49d90d9ddfc1 - main - lagg: Handle a port count of zero

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Mon, 01 Jun 2026 18:30:13 UTC
The branch main has been updated by markj:

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

commit 49d90d9ddfc1ecda9ad9b6cb5565e5fbdcc14964
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-06-01 16:40:43 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-06-01 18:22:57 +0000

    lagg: Handle a port count of zero
    
    The sc_count check in lagg_transmit_ethernet() and
    lagg_transmit_infiniband() is racy, as the lagg protocol handlers are
    only synchronized by net_epoch.  Handle a count of 0 in each protocol
    handler where it's needed, namely in the RR and LB handlers.
    
    Reported by:    Yuxiang Yang, Yizhou Zhao, Xuewei Feng, Qi Li, and Ke Xu from Tsinghua University using GLM5.1 from Z.ai
    Reviewed by:    pouria, zlei
    MFC after:      2 weeks
    Differential Revision:  https://reviews.freebsd.org/D56942
---
 sys/net/if_lagg.c | 40 +++++++++++++++++++++++-----------------
 1 file changed, 23 insertions(+), 17 deletions(-)

diff --git a/sys/net/if_lagg.c b/sys/net/if_lagg.c
index 0333162da0d4..da08e02d50be 100644
--- a/sys/net/if_lagg.c
+++ b/sys/net/if_lagg.c
@@ -1828,6 +1828,7 @@ lookup_snd_tag_port(struct ifnet *ifp, uint32_t flowid, uint32_t flowtype,
 	struct lagg_port *lp;
 	struct lagg_lb *lb;
 	uint32_t hash, p;
+	unsigned int count;
 	int err;
 
 	sc = ifp->if_softc;
@@ -1839,8 +1840,11 @@ lookup_snd_tag_port(struct ifnet *ifp, uint32_t flowid, uint32_t flowtype,
 		if ((sc->sc_opts & LAGG_OPT_USE_FLOWID) == 0 ||
 		    flowtype == M_HASHTYPE_NONE)
 			return (NULL);
+		count = atomic_load_int(&sc->sc_count);
+		if (count == 0)
+			return (NULL);
 		p = flowid >> sc->flowid_shift;
-		p %= sc->sc_count;
+		p %= count;
 		lb = (struct lagg_lb *)sc->sc_psc;
 		lp = lb->lb_ports[p];
 		return (lagg_link_active(sc, lp));
@@ -2128,12 +2132,6 @@ lagg_transmit_ethernet(struct ifnet *ifp, struct mbuf *m)
 	if (m->m_pkthdr.csum_flags & CSUM_SND_TAG)
 		MPASS(m->m_pkthdr.snd_tag->ifp == ifp);
 #endif
-	/* We need at least one port */
-	if (sc->sc_count == 0) {
-		m_freem(m);
-		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
-		return (ENXIO);
-	}
 
 	ETHER_BPF_MTAP(ifp, m);
 
@@ -2150,12 +2148,6 @@ lagg_transmit_infiniband(struct ifnet *ifp, struct mbuf *m)
 	if (m->m_pkthdr.csum_flags & CSUM_SND_TAG)
 		MPASS(m->m_pkthdr.snd_tag->ifp == ifp);
 #endif
-	/* We need at least one port */
-	if (sc->sc_count == 0) {
-		m_freem(m);
-		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
-		return (ENXIO);
-	}
 
 	infiniband_bpf_mtap(ifp, m);
 
@@ -2421,13 +2413,20 @@ lagg_rr_start(struct lagg_softc *sc, struct mbuf *m)
 {
 	struct lagg_port *lp;
 	uint32_t p;
+	unsigned int count;
 
+	count = atomic_load_int(&sc->sc_count);
+	if (__predict_false(count == 0)) {
+		if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1);
+		m_freem(m);
+		return (ENETDOWN);
+	}
 	p = atomic_fetchadd_32(&sc->sc_seq, 1);
 	p /= sc->sc_stride;
-	p %= sc->sc_count;
-	lp = CK_SLIST_FIRST(&sc->sc_ports);
+	p %= count;
 
-	while (p--)
+	lp = CK_SLIST_FIRST(&sc->sc_ports);
+	while (p-- && lp != NULL)
 		lp = CK_SLIST_NEXT(lp, lp_entries);
 
 	/*
@@ -2611,13 +2610,20 @@ lagg_lb_start(struct lagg_softc *sc, struct mbuf *m)
 	struct lagg_lb *lb = (struct lagg_lb *)sc->sc_psc;
 	struct lagg_port *lp = NULL;
 	uint32_t p = 0;
+	unsigned int count;
 
+	count = atomic_load_int(&sc->sc_count);
+	if (__predict_false(count == 0)) {
+		if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1);
+		m_freem(m);
+		return (ENETDOWN);
+	}
 	if ((sc->sc_opts & LAGG_OPT_USE_FLOWID) &&
 	    M_HASHTYPE_GET(m) != M_HASHTYPE_NONE)
 		p = m->m_pkthdr.flowid >> sc->flowid_shift;
 	else
 		p = m_ether_tcpip_hash(sc->sc_flags, m, lb->lb_key);
-	p %= sc->sc_count;
+	p %= count;
 	lp = lb->lb_ports[p];
 
 	/*