git: e1c59c909d63 - main - cxgbe: Report SR-IOV VF status

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Tue, 29 Sep 2026 07:20:28 UTC
The branch main has been updated by kbowling:

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

commit e1c59c909d63931dae999479ecd01e3cf1855543
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-08 17:53:29 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-09-29 07:19:24 +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.
    
    Reviewed by:    jhb
    Sponsored by:   BBOX.io
    Differential Revision:  https://reviews.freebsd.org/D58741
---
 sys/dev/cxgbe/adapter.h |  10 +++++
 sys/dev/cxgbe/t4_iov.c  | 115 +++++++++++++++++++++++++++++++++++++++---------
 sys/dev/cxgbe/t4_main.c |  66 +++++++++++++++++++++++++++
 3 files changed, 169 insertions(+), 22 deletions(-)

diff --git a/sys/dev/cxgbe/adapter.h b/sys/dev/cxgbe/adapter.h
index 8c5cf052b689..df2ab0d946f9 100644
--- a/sys/dev/cxgbe/adapter.h
+++ b/sys/dev/cxgbe/adapter.h
@@ -312,6 +312,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;
@@ -323,6 +330,9 @@ struct port_info {
 	bool vxlan_tcam_entry;
 
 	struct tx_sched_params *sched_params;
+	struct t4_vf_info *iov_vfs;
+	uint16_t iov_num_vfs;
+	bool iov_status_supported;
 
 	struct mtx pi_lock;
 	char lockname[16];
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 4aca284415df..726a5259dede 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)
@@ -2868,6 +2871,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)
@@ -3058,6 +3062,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 (!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)
 {