git: 713ceb99b685 - main - lagg: fix lagg ifioctl after SIOCSIFCAPNV

From: Andrew Gallatin <gallatin_at_FreeBSD.org>
Date: Thu, 28 Jul 2022 14:39:19 UTC
The branch main has been updated by gallatin:

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

commit 713ceb99b68568232bf9895bbe1811797bfde63c
Author:     Andrew Gallatin <gallatin@FreeBSD.org>
AuthorDate: 2022-07-28 14:36:22 +0000
Commit:     Andrew Gallatin <gallatin@FreeBSD.org>
CommitDate: 2022-07-28 14:39:00 +0000

    lagg: fix lagg ifioctl after SIOCSIFCAPNV
    
    Lagg was broken by SIOCSIFCAPNV when all underlying devices
    support SIOCSIFCAPNV.  This change updates lagg to work with
    SIOCSIFCAPNV and if_capabilities2.
    
    Reviewed by: kib, hselasky
    Sponsored by: Netflix
    Differential Revision: https://reviews.freebsd.org/D35865
---
 sys/net/if_lagg.c | 62 +++++++++++++++++++++++++++++++++++++++----------------
 sys/net/if_lagg.h |  1 +
 2 files changed, 45 insertions(+), 18 deletions(-)

diff --git a/sys/net/if_lagg.c b/sys/net/if_lagg.c
index 3894b6d55cea..8e273c4ed391 100644
--- a/sys/net/if_lagg.c
+++ b/sys/net/if_lagg.c
@@ -157,7 +157,7 @@ static void     lagg_ratelimit_query(struct ifnet *,
 #endif
 static int	lagg_setmulti(struct lagg_port *);
 static int	lagg_clrmulti(struct lagg_port *);
-static	int	lagg_setcaps(struct lagg_port *, int cap);
+static	void	lagg_setcaps(struct lagg_port *, int cap, int cap2);
 static	int	lagg_setflag(struct lagg_port *, int, int,
 		    int (*func)(struct ifnet *, int));
 static	int	lagg_setflags(struct lagg_port *, int status);
@@ -664,17 +664,20 @@ static void
 lagg_capabilities(struct lagg_softc *sc)
 {
 	struct lagg_port *lp;
-	int cap, ena, pena;
+	int cap, cap2, ena, ena2, pena, pena2;
 	uint64_t hwa;
 	struct ifnet_hw_tsomax hw_tsomax;
 
 	LAGG_XLOCK_ASSERT(sc);
 
 	/* Get common enabled capabilities for the lagg ports */
-	ena = ~0;
-	CK_SLIST_FOREACH(lp, &sc->sc_ports, lp_entries)
+	ena = ena2 = ~0;
+	CK_SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
 		ena &= lp->lp_ifp->if_capenable;
-	ena = (ena == ~0 ? 0 : ena);
+		ena2 &= lp->lp_ifp->if_capenable2;
+	}
+	if (CK_SLIST_FIRST(&sc->sc_ports) == NULL)
+		ena = ena2 = 0;
 
 	/*
 	 * Apply common enabled capabilities back to the lagg ports.
@@ -682,30 +685,36 @@ lagg_capabilities(struct lagg_softc *sc)
 	 */
 	do {
 		pena = ena;
+		pena2 = ena2;
 		CK_SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
-			lagg_setcaps(lp, ena);
+			lagg_setcaps(lp, ena, ena2);
 			ena &= lp->lp_ifp->if_capenable;
+			ena2 &= lp->lp_ifp->if_capenable2;
 		}
-	} while (pena != ena);
+	} while (pena != ena || pena2 != ena2);
 
 	/* Get other capabilities from the lagg ports */
-	cap = ~0;
+	cap = cap2 = ~0;
 	hwa = ~(uint64_t)0;
 	memset(&hw_tsomax, 0, sizeof(hw_tsomax));
 	CK_SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
 		cap &= lp->lp_ifp->if_capabilities;
+		cap2 &= lp->lp_ifp->if_capabilities2;
 		hwa &= lp->lp_ifp->if_hwassist;
 		if_hw_tsomax_common(lp->lp_ifp, &hw_tsomax);
 	}
-	cap = (cap == ~0 ? 0 : cap);
-	hwa = (hwa == ~(uint64_t)0 ? 0 : hwa);
+	if (CK_SLIST_FIRST(&sc->sc_ports) == NULL)
+		cap = cap2 = hwa = 0;
 
 	if (sc->sc_ifp->if_capabilities != cap ||
 	    sc->sc_ifp->if_capenable != ena ||
+	    sc->sc_ifp->if_capenable2 != ena2 ||
 	    sc->sc_ifp->if_hwassist != hwa ||
 	    if_hw_tsomax_update(sc->sc_ifp, &hw_tsomax) != 0) {
 		sc->sc_ifp->if_capabilities = cap;
+		sc->sc_ifp->if_capabilities2 = cap2;
 		sc->sc_ifp->if_capenable = ena;
+		sc->sc_ifp->if_capenable2 = ena2;
 		sc->sc_ifp->if_hwassist = hwa;
 		getmicrotime(&sc->sc_ifp->if_lastchange);
 
@@ -982,7 +991,7 @@ lagg_port_destroy(struct lagg_port *lp, int rundelport)
 
 	if (lp->lp_detaching == 0) {
 		lagg_setflags(lp, 0);
-		lagg_setcaps(lp, lp->lp_ifcapenable);
+		lagg_setcaps(lp, lp->lp_ifcapenable, lp->lp_ifcapenable2);
 		if_setlladdr(ifp, lp->lp_lladdr, ifp->if_addrlen);
 	}
 
@@ -1038,6 +1047,7 @@ lagg_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 		break;
 
 	case SIOCSIFCAP:
+	case SIOCSIFCAPNV:
 		if (lp->lp_ioctl == NULL) {
 			error = EINVAL;
 			break;
@@ -1690,6 +1700,7 @@ lagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 		break;
 
 	case SIOCSIFCAP:
+	case SIOCSIFCAPNV:
 		LAGG_XLOCK(sc);
 		CK_SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
 			if (lp->lp_ioctl != NULL)
@@ -1701,6 +1712,10 @@ lagg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 		error = 0;
 		break;
 
+	case SIOCGIFCAPNV:
+		error = 0;
+		break;
+
 	case SIOCSIFMTU:
 		LAGG_XLOCK(sc);
 		CK_SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
@@ -2013,17 +2028,28 @@ lagg_clrmulti(struct lagg_port *lp)
 	return (0);
 }
 
-static int
-lagg_setcaps(struct lagg_port *lp, int cap)
+static void
+lagg_setcaps(struct lagg_port *lp, int cap, int cap2)
 {
 	struct ifreq ifr;
+	struct siocsifcapnv_driver_data drv_ioctl_data;
 
-	if (lp->lp_ifp->if_capenable == cap)
-		return (0);
+	if (lp->lp_ifp->if_capenable == cap &&
+	    lp->lp_ifp->if_capenable2 == cap2)
+		return;
 	if (lp->lp_ioctl == NULL)
-		return (ENXIO);
-	ifr.ifr_reqcap = cap;
-	return ((*lp->lp_ioctl)(lp->lp_ifp, SIOCSIFCAP, (caddr_t)&ifr));
+		return;
+	/* XXX */
+	if ((lp->lp_ifp->if_capabilities & IFCAP_NV) != 0) {
+		drv_ioctl_data.reqcap = cap;
+		drv_ioctl_data.reqcap2 = cap2;
+		drv_ioctl_data.nvcap = NULL;
+		(*lp->lp_ioctl)(lp->lp_ifp, SIOCSIFCAPNV,
+		    (caddr_t)&drv_ioctl_data);
+	} else {
+		ifr.ifr_reqcap = cap;
+		(*lp->lp_ioctl)(lp->lp_ifp, SIOCSIFCAP, (caddr_t)&ifr);
+	}
 }
 
 /* Handle a ref counted flag that should be set on the lagg port as well */
diff --git a/sys/net/if_lagg.h b/sys/net/if_lagg.h
index 94f3487af6f7..9fc6e3862712 100644
--- a/sys/net/if_lagg.h
+++ b/sys/net/if_lagg.h
@@ -273,6 +273,7 @@ struct lagg_port {
 	uint32_t			lp_flags;	/* port flags */
 	int				lp_ifflags;	/* saved ifp flags */
 	int				lp_ifcapenable;	/* saved ifp capenable */
+	int				lp_ifcapenable2;/* saved ifp capenable2 */
 	void				*lh_cookie;	/* if state hook */
 	void				*lp_psc;	/* protocol data */
 	int				lp_detaching;	/* ifnet is detaching */