git: 85524d4891ab - stable/15 - cxgbe: Report SR-IOV VF status

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Sat, 31 Oct 2026 18:58:48 UTC
The branch stable/15 has been updated by kbowling:

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

commit 85524d4891abe3eb11ae79216d2dae2ca154fa36
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-08 17:53:29 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-10-31 18:57:04 +0000

    cxgbe: Report SR-IOV VF status
    
    Retain the PF-accepted MAC and VLAN settings from the per-port t4iov
    companion and expose them through the corresponding cxgbe ifnet.
    
    Publish, snapshot, and destroy the cache under the existing adapter
    synchronized-operation mechanism so status queries cannot race IOV
    configuration or teardown.
    
    Track successful t4iov attachment independently of the active VF count.
    Restrict reporting to the port main VI, return an empty status for a
    supported but unconfigured PF, and omit status from VF and auxiliary
    VIs.
    
    On stable/15, append the cached status fields to port_info to preserve
    all existing member offsets.  Reject VF adapters before accessing the
    extended portion, which an older VF module does not allocate.
    
    Reviewed by:    jhb
    Sponsored by:   BBOX.io
    Differential Revision:  https://reviews.freebsd.org/D58741
    
    (cherry picked from commit e1c59c909d63931dae999479ecd01e3cf1855543)
---
 sys/dev/cxgbe/adapter.h |  12 +++++
 sys/dev/cxgbe/t4_iov.c  | 115 +++++++++++++++++++++++++++++++++++++++---------
 sys/dev/cxgbe/t4_main.c |  66 +++++++++++++++++++++++++++
 3 files changed, 171 insertions(+), 22 deletions(-)

diff --git a/sys/dev/cxgbe/adapter.h b/sys/dev/cxgbe/adapter.h
index 6ff1f07ddf50..b84c673f75ab 100644
--- a/sys/dev/cxgbe/adapter.h
+++ b/sys/dev/cxgbe/adapter.h
@@ -302,6 +302,13 @@ struct tx_sched_params {
 	struct tx_cl_rl_params cl_rl[];
 };
 
+struct t4_vf_info {
+	uint8_t mac[ETHER_ADDR_LEN];
+	uint16_t vlan;
+	bool configured;
+	bool access_vlan;
+};
+
 struct port_info {
 	device_t dev;
 	struct adapter *adapter;
@@ -338,6 +345,11 @@ struct port_info {
 	uint64_t fcs_base;
 
 	struct sysctl_ctx_list ctx;
+
+	/* Keep existing member offsets for stable/15 module consumers. */
+	struct t4_vf_info *iov_vfs;
+	uint16_t iov_num_vfs;
+	bool iov_status_supported;
 };
 
 #define	IS_MAIN_VI(vi)		((vi) == &((vi)->pi->vi[0]))
diff --git a/sys/dev/cxgbe/t4_iov.c b/sys/dev/cxgbe/t4_iov.c
index 9ccf63fd6516..f40b2e986b33 100644
--- a/sys/dev/cxgbe/t4_iov.c
+++ b/sys/dev/cxgbe/t4_iov.c
@@ -49,6 +49,7 @@
 struct t4iov_softc {
 	device_t sc_dev;
 	device_t sc_main;
+	struct port_info *sc_port;
 	bool sc_attached;
 
 	int pf;
@@ -277,6 +278,7 @@ t4iov_attach_child(device_t dev)
 {
 	struct t4iov_softc *sc;
 #ifdef PCI_IOV
+	struct adapter *adap;
 	nvlist_t *pf_schema, *vf_schema;
 #endif
 	device_t pdev;
@@ -306,6 +308,16 @@ t4iov_attach_child(device_t dev)
 		device_printf(dev, "Failed to initialize SR-IOV: %d\n", error);
 		return (0);
 	}
+	sc->sc_port = device_get_softc(pdev);
+	adap = device_get_softc(sc->sc_main);
+	error = begin_synchronized_op(adap, NULL, SLEEP_OK, "t4iovatt");
+	if (error != 0) {
+		(void)pci_iov_detach(dev);
+		sc->sc_port = NULL;
+		return (error);
+	}
+	sc->sc_port->iov_status_supported = true;
+	end_synchronized_op(adap, 0);
 #endif
 
 	sc->sc_attached = true;
@@ -317,6 +329,7 @@ t4iov_detach_child(device_t dev)
 {
 	struct t4iov_softc *sc;
 #ifdef PCI_IOV
+	struct adapter *adap;
 	int error;
 #endif
 
@@ -330,6 +343,13 @@ t4iov_detach_child(device_t dev)
 		device_printf(dev, "Failed to disable SR-IOV\n");
 		return (error);
 	}
+	adap = device_get_softc(sc->sc_main);
+	error = begin_synchronized_op(adap, NULL, SLEEP_OK, "t4iovdet");
+	if (error != 0)
+		return (error);
+	sc->sc_port->iov_status_supported = false;
+	end_synchronized_op(adap, 0);
+	sc->sc_port = NULL;
 #endif
 
 	sc->sc_attached = false;
@@ -359,14 +379,46 @@ t4iov_detach(device_t dev)
 static int
 t4iov_iov_init(device_t dev, uint16_t num_vfs, const struct nvlist *config)
 {
+	struct t4iov_softc *sc;
+	struct t4_vf_info *vfs;
+	struct adapter *adap;
+	int error;
 
 	/* XXX: The Linux driver sets up a vf_monitor task on T4 adapters. */
+	sc = device_get_softc(dev);
+	MPASS(sc->sc_port != NULL);
+	vfs = mallocarray(num_vfs, sizeof(*vfs), M_CXGBE,
+	    M_WAITOK | M_ZERO);
+	adap = device_get_softc(sc->sc_main);
+	error = begin_synchronized_op(adap, NULL, SLEEP_OK, "t4vfinit");
+	if (error != 0) {
+		free(vfs, M_CXGBE);
+		return (error);
+	}
+	MPASS(sc->sc_port->iov_vfs == NULL);
+	sc->sc_port->iov_vfs = vfs;
+	sc->sc_port->iov_num_vfs = num_vfs;
+	end_synchronized_op(adap, 0);
 	return (0);
 }
 
 static void
 t4iov_iov_uninit(device_t dev)
 {
+	struct t4iov_softc *sc;
+	struct t4_vf_info *vfs;
+	struct adapter *adap;
+
+	sc = device_get_softc(dev);
+	MPASS(sc->sc_port != NULL);
+	adap = device_get_softc(sc->sc_main);
+	if (begin_synchronized_op(adap, NULL, SLEEP_OK, "t4vffini") != 0)
+		return;
+	vfs = sc->sc_port->iov_vfs;
+	sc->sc_port->iov_vfs = NULL;
+	sc->sc_port->iov_num_vfs = 0;
+	end_synchronized_op(adap, 0);
+	free(vfs, M_CXGBE);
 }
 
 static int
@@ -376,6 +428,8 @@ t4iov_add_vf(device_t dev, uint16_t vfnum, const struct nvlist *config)
 	struct t4iov_softc *sc;
 	struct adapter *adap;
 	uint8_t ma[ETHER_ADDR_LEN];
+	uint16_t vlan;
+	bool access_vlan, has_mac, has_vlan;
 	size_t size;
 	int rc;
 
@@ -383,27 +437,19 @@ t4iov_add_vf(device_t dev, uint16_t vfnum, const struct nvlist *config)
 	MPASS(sc->sc_attached);
 	MPASS(sc->sc_main != NULL);
 	adap = device_get_softc(sc->sc_main);
+	memset(ma, 0, sizeof(ma));
+	vlan = 0;
+	access_vlan = false;
+	has_mac = nvlist_exists_binary(config, "mac-addr");
+	has_vlan = nvlist_exists_number(config, "vlan");
 
-	if (nvlist_exists_binary(config, "mac-addr")) {
+	if (has_mac) {
 		mac = nvlist_get_binary(config, "mac-addr", &size);
 		bcopy(mac, ma, ETHER_ADDR_LEN);
-
-		if (begin_synchronized_op(adap, NULL, SLEEP_OK | INTR_OK,
-		    "t4vfma") != 0)
-			return (ENXIO);
-		rc = -t4_set_vf_mac(adap, sc->pf, vfnum + 1, 1, ma);
-		end_synchronized_op(adap, 0);
-		if (rc != 0) {
-			device_printf(dev,
-			    "Failed to set VF%d MAC address to "
-			    "%02x:%02x:%02x:%02x:%02x:%02x, rc = %d\n", vfnum,
-			    ma[0], ma[1], ma[2], ma[3], ma[4], ma[5], rc);
-			return (rc);
-		}
 	}
 
-	if (nvlist_exists_number(config, "vlan")) {
-		uint16_t vlan = nvlist_get_number(config, "vlan");
+	if (has_vlan) {
+		vlan = nvlist_get_number(config, "vlan");
 
 		/* We can't restrict to VID 0 */
 		if (vlan == DOT1Q_VID_NULL)
@@ -411,21 +457,46 @@ t4iov_add_vf(device_t dev, uint16_t vfnum, const struct nvlist *config)
 
 		if (vlan == VF_VLAN_TRUNK)
 			vlan = DOT1Q_VID_NULL;
+		else
+			access_vlan = true;
+	}
+
+	if (begin_synchronized_op(adap, NULL, SLEEP_OK | INTR_OK,
+	    "t4vfadd") != 0)
+		return (ENXIO);
 
-		if (begin_synchronized_op(adap, NULL, SLEEP_OK | INTR_OK,
-		    "t4vfvl") != 0)
-			return (ENXIO);
+	if (has_mac) {
+		rc = -t4_set_vf_mac(adap, sc->pf, vfnum + 1, 1, ma);
+		if (rc != 0) {
+			device_printf(dev,
+			    "Failed to set VF%d MAC address to "
+			    "%02x:%02x:%02x:%02x:%02x:%02x, rc = %d\n", vfnum,
+			    ma[0], ma[1], ma[2], ma[3], ma[4], ma[5], rc);
+			goto out;
+		}
+	}
+
+	if (has_vlan) {
 		rc = t4_set_vlan_acl(adap, sc->pf, vfnum + 1, vlan);
-		end_synchronized_op(adap, 0);
 		if (rc != 0) {
 			device_printf(dev,
 			    "Failed to set VF%d VLAN to %d, rc = %d\n",
 			    vfnum, vlan, rc);
-			return (rc);
+			goto out;
 		}
 	}
 
-	return (0);
+	if (has_mac)
+		memcpy(sc->sc_port->iov_vfs[vfnum].mac, ma,
+		    ETHER_ADDR_LEN);
+	sc->sc_port->iov_vfs[vfnum].vlan = vlan;
+	sc->sc_port->iov_vfs[vfnum].access_vlan = access_vlan;
+	sc->sc_port->iov_vfs[vfnum].configured = true;
+	rc = 0;
+
+out:
+	end_synchronized_op(adap, 0);
+	return (rc);
 }
 #endif
 
diff --git a/sys/dev/cxgbe/t4_main.c b/sys/dev/cxgbe/t4_main.c
index 396bd5a8b6a6..77ff10bc861b 100644
--- a/sys/dev/cxgbe/t4_main.c
+++ b/sys/dev/cxgbe/t4_main.c
@@ -42,6 +42,7 @@
 #include <sys/eventhandler.h>
 #include <sys/module.h>
 #include <sys/malloc.h>
+#include <sys/nv.h>
 #include <sys/queue.h>
 #include <sys/taskqueue.h>
 #include <dev/pci/pcireg.h>
@@ -57,6 +58,7 @@
 #include <net/if_types.h>
 #include <net/if_dl.h>
 #include <net/if_vlan_var.h>
+#include <net/if_vf_status.h>
 #include <net/rss_config.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
@@ -280,6 +282,7 @@ static driver_t vche_driver = {
 /* ifnet interface */
 static void cxgbe_init(void *);
 static int cxgbe_ioctl(if_t, unsigned long, caddr_t);
+static int cxgbe_vf_status(if_t, struct if_vf_status **);
 static int cxgbe_transmit(if_t, struct mbuf *);
 static void cxgbe_qflush(if_t);
 #if defined(KERN_TLS) || defined(RATELIMIT)
@@ -2865,6 +2868,7 @@ cxgbe_vi_attach(device_t dev, struct vi_info *vi)
 
 	if_setinitfn(ifp, cxgbe_init);
 	if_setioctlfn(ifp, cxgbe_ioctl);
+	if_setvfstatusfn(ifp, cxgbe_vf_status);
 	if_settransmitfn(ifp, cxgbe_transmit);
 	if_setqflushfn(ifp, cxgbe_qflush);
 	if (vi->pi->nvi > 1 || sc->flags & IS_VF)
@@ -3055,6 +3059,68 @@ cxgbe_init(void *arg)
 	end_synchronized_op(sc, 0);
 }
 
+static int
+cxgbe_vf_status(if_t ifp, struct if_vf_status **statusp)
+{
+	struct port_info *pi;
+	struct adapter *sc;
+	struct t4_vf_info *snapshot;
+	struct if_vf_info *vf;
+	struct if_vf_status *status;
+	struct vi_info *vi;
+	uint16_t num_vfs;
+	int error, i;
+
+	vi = if_getsoftc(ifp);
+	pi = vi->pi;
+	sc = pi->adapter;
+	if ((sc->flags & IS_VF) != 0 || !IS_MAIN_VI(vi))
+		return (EOPNOTSUPP);
+	error = begin_synchronized_op(sc, vi, SLEEP_OK | INTR_OK,
+	    "t4vfstat");
+	if (error != 0)
+		return (error);
+	if (!pi->iov_status_supported) {
+		end_synchronized_op(sc, 0);
+		return (EOPNOTSUPP);
+	}
+	num_vfs = pi->iov_num_vfs;
+	snapshot = NULL;
+	if (num_vfs != 0) {
+		snapshot = mallocarray(num_vfs, sizeof(*snapshot), M_CXGBE,
+		    M_WAITOK);
+		memcpy(snapshot, pi->iov_vfs, num_vfs * sizeof(*snapshot));
+	}
+	end_synchronized_op(sc, 0);
+
+	status = if_vf_status_alloc(num_vfs);
+	for (i = 0; i < num_vfs; i++) {
+		vf = &status->vfs[i];
+		vf->fields = IFVF_F_CONFIGURED;
+		vf->index = i;
+		vf->configured = snapshot[i].configured;
+		if (!vf->configured)
+			continue;
+		vf->fields |= IFVF_F_VLAN_MODE;
+		if (!ETHER_IS_ZERO(snapshot[i].mac)) {
+			memcpy(vf->mac, snapshot[i].mac, sizeof(vf->mac));
+			vf->fields |= IFVF_F_MAC;
+		}
+		if (snapshot[i].access_vlan) {
+			vf->vlan_mode = IFVF_VLAN_ACCESS;
+			vf->vlan = snapshot[i].vlan;
+			vf->vlan_proto = ETHERTYPE_VLAN;
+			vf->vlan_count = 1;
+			vf->fields |= IFVF_F_VLAN | IFVF_F_VLAN_PROTO |
+			    IFVF_F_VLAN_COUNT;
+		} else
+			vf->vlan_mode = IFVF_VLAN_TRUNK;
+	}
+	free(snapshot, M_CXGBE);
+	*statusp = status;
+	return (0);
+}
+
 static int
 cxgbe_ioctl(if_t ifp, unsigned long cmd, caddr_t data)
 {