git: 47bf0dc861b6 - stable/15 - qlnx: Report SR-IOV VF status

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

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

commit 47bf0dc861b69b4a2cd1bf18c0fc5cca96eed17d
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-08 18:00:44 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-10-31 18:57:04 +0000

    qlnx: Report SR-IOV VF status
    
    Retain the PF-known MAC and allocated transmit and receive queue counts
    for each VF and expose them through the PF ifnet.  Sum queues across all
    hardware functions rather than reporting only the first one.
    
    Publish and snapshot the VF cache under the existing hardware lock.
    Unpublish it before hardware teardown, and invalidate queue-allocation
    knowledge when error recovery releases the underlying VF resources.
    
    Initialize the cache, release it on failed initialization, and reject an
    out-of-range VF index instead of continuing into the array.
    
    Tested by:      zlei
    Place the new status flags in existing padding on stable/15, preserving
    the VF attribute array stride and existing member offsets.
    
    Sponsored by:   BBOX.io
    Differential Revision:  https://reviews.freebsd.org/D58742
    
    (cherry picked from commit 9b8bc7b827ef99a0488d0bed32293bad323e8cdb)
---
 sys/dev/qlnx/qlnxe/qlnx_def.h |   2 +
 sys/dev/qlnx/qlnxe/qlnx_os.c  | 166 ++++++++++++++++++++++++++++++++++++------
 2 files changed, 145 insertions(+), 23 deletions(-)

diff --git a/sys/dev/qlnx/qlnxe/qlnx_def.h b/sys/dev/qlnx/qlnxe/qlnx_def.h
index f895487c47e4..3489dc70893f 100644
--- a/sys/dev/qlnx/qlnxe/qlnx_def.h
+++ b/sys/dev/qlnx/qlnxe/qlnx_def.h
@@ -326,6 +326,8 @@ typedef struct _qlnx_mcast {
 
 typedef struct _qlnx_vf_attr {
 	uint8_t		mac_addr[ETHER_ADDR_LEN];
+	bool		configured;
+	bool		queues_valid;
 	uint32_t	num_rings;
 } qlnx_vf_attr_t;
 
diff --git a/sys/dev/qlnx/qlnxe/qlnx_os.c b/sys/dev/qlnx/qlnxe/qlnx_os.c
index 6b896729bf67..8c7d2027d905 100644
--- a/sys/dev/qlnx/qlnxe/qlnx_os.c
+++ b/sys/dev/qlnx/qlnxe/qlnx_os.c
@@ -72,6 +72,7 @@
 #include <sys/nv.h>
 #include <sys/iov_schema.h>
 #include <dev/pci/pci_iov.h>
+#include <net/if_vf_status.h>
 #endif /* #ifdef CONFIG_ECORE_SRIOV */
 
 #include <sys/smp.h>
@@ -174,6 +175,7 @@ static int qlnx_pci_detach (device_t);
 static int qlnx_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t *params);
 static void qlnx_iov_uninit(device_t dev);
 static int qlnx_iov_add_vf(device_t dev, uint16_t vfnum, const nvlist_t *params);
+static int qlnx_vf_status(if_t ifp, struct if_vf_status **statusp);
 static void qlnx_initialize_sriov(qlnx_host_t *ha);
 static void qlnx_pf_taskqueue(void *context, int pending);
 static int qlnx_create_pf_taskqueues(qlnx_host_t *ha);
@@ -651,6 +653,21 @@ qlnx_get_params(qlnx_host_t *ha)
 	return;
 }
 
+#if !defined(QLNX_VF) && defined(CONFIG_ECORE_SRIOV)
+static void
+qlnx_invalidate_vf_queue_status(qlnx_host_t *ha)
+{
+	int i;
+
+	QLNX_LOCK(ha);
+	if (ha->vf_attr != NULL) {
+		for (i = 0; i < ha->num_vfs; i++)
+			ha->vf_attr[i].queues_valid = false;
+	}
+	QLNX_UNLOCK(ha);
+}
+#endif
+
 static void
 qlnx_error_recovery_taskqueue(void *context, int pending)
 {
@@ -668,6 +685,9 @@ qlnx_error_recovery_taskqueue(void *context, int pending)
 	qlnx_rdma_dev_remove(ha);
 #endif /* #ifdef QLNX_ENABLE_IWARP */
 
+#if !defined(QLNX_VF) && defined(CONFIG_ECORE_SRIOV)
+	qlnx_invalidate_vf_queue_status(ha);
+#endif
         qlnx_slowpath_stop(ha);
         qlnx_slowpath_start(ha);
 
@@ -2313,6 +2333,11 @@ qlnx_init_ifnet(device_t dev, qlnx_host_t *ha)
         if_setsoftc(ifp, ha);
         if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
         if_setioctlfn(ifp, qlnx_ioctl);
+#ifndef QLNX_VF
+#ifdef CONFIG_ECORE_SRIOV
+	if_setvfstatusfn(ifp, qlnx_vf_status);
+#endif
+#endif
         if_settransmitfn(ifp, qlnx_transmit);
         if_setqflushfn(ifp, qlnx_qflush);
 
@@ -7914,11 +7939,14 @@ static int
 qlnx_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t *nvlist_params)
 {
 	qlnx_host_t		*ha;
+	qlnx_vf_attr_t		*vf_attrs;
 	struct ecore_dev	*cdev;
 	struct ecore_iov_vf_init_params params;
 	int ret, j, i;
 	uint32_t max_vfs;
 
+	vf_attrs = NULL;
+
 	if ((ha = device_get_softc(dev)) == NULL) {
 		device_printf(dev, "%s: cannot get softc\n", __func__);
 		return (-1);
@@ -7940,10 +7968,10 @@ qlnx_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t *nvlist_params)
 		goto qlnx_iov_init_err0;
         }
 
-	ha->vf_attr =  malloc(((sizeof (qlnx_vf_attr_t) * num_vfs)), M_QLNXBUF,
-				M_NOWAIT);
+	vf_attrs = mallocarray(num_vfs, sizeof(*vf_attrs), M_QLNXBUF,
+	    M_NOWAIT | M_ZERO);
 
-	if (ha->vf_attr == NULL)
+	if (vf_attrs == NULL)
 		goto qlnx_iov_init_err0;
 
         memset(&params, 0, sizeof(params));
@@ -7978,10 +8006,20 @@ qlnx_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t *nvlist_params)
                         }
                 }
 
-                ecore_ptt_release(hwfn, ptt);
-        }
+		ecore_ptt_release(hwfn, ptt);
+	}
 
+	for (i = 0; i < num_vfs; i++) {
+		for_each_hwfn(cdev, j)
+			vf_attrs[i].num_rings += ecore_iov_get_vf_num_rxqs(
+			    &cdev->hwfns[j], i);
+		vf_attrs[i].queues_valid = true;
+	}
+
+	QLNX_LOCK(ha);
+	ha->vf_attr = vf_attrs;
 	ha->num_vfs = num_vfs;
+	QLNX_UNLOCK(ha);
 	qlnx_inform_vf_link_state(&cdev->hwfns[0], ha);
 
 	QL_DPRINT2(ha," dev = %p exit num_vfs = %d\n", dev, num_vfs);
@@ -7993,7 +8031,7 @@ qlnx_iov_init_err1:
 
 qlnx_iov_init_err0:
 	qlnx_destroy_pf_taskqueues(ha);
-	ha->num_vfs = 0;
+	free(vf_attrs, M_QLNXBUF);
 
 	return (-1);
 }
@@ -8002,6 +8040,7 @@ static void
 qlnx_iov_uninit(device_t dev)
 {
 	qlnx_host_t	*ha;
+	qlnx_vf_attr_t	*vf_attrs;
 
 	if ((ha = device_get_softc(dev)) == NULL) {
 		device_printf(dev, "%s: cannot get softc\n", __func__);
@@ -8010,13 +8049,15 @@ qlnx_iov_uninit(device_t dev)
 
 	QL_DPRINT2(ha," dev = %p enter\n", dev);
 
-	qlnx_sriov_disable(ha);
-	qlnx_destroy_pf_taskqueues(ha);
-
-	free(ha->vf_attr, M_QLNXBUF);
+	QLNX_LOCK(ha);
+	vf_attrs = ha->vf_attr;
 	ha->vf_attr = NULL;
-
 	ha->num_vfs = 0;
+	QLNX_UNLOCK(ha);
+
+	qlnx_sriov_disable(ha);
+	qlnx_destroy_pf_taskqueues(ha);
+	free(vf_attrs, M_QLNXBUF);
 
 	QL_DPRINT2(ha," dev = %p exit\n", dev);
 	return;
@@ -8028,8 +8069,10 @@ qlnx_iov_add_vf(device_t dev, uint16_t vfnum, const nvlist_t *params)
 	qlnx_host_t	*ha;
 	qlnx_vf_attr_t	*vf_attr;
 	unsigned const char *mac;
+	uint8_t mac_addr[ETHER_ADDR_LEN];
 	size_t size;
 	struct ecore_hwfn *p_hwfn;
+	enum _ecore_status_t rc;
 
 	if ((ha = device_get_softc(dev)) == NULL) {
 		device_printf(dev, "%s: cannot get softc\n", __func__);
@@ -8038,31 +8081,108 @@ qlnx_iov_add_vf(device_t dev, uint16_t vfnum, const nvlist_t *params)
 
 	QL_DPRINT2(ha," dev = %p enter vfnum = %d\n", dev, vfnum);
 
-	if (vfnum > (ha->num_vfs - 1)) {
-		QL_DPRINT1(ha, " VF[%d] is greater than max allowed [%d]\n",
-			vfnum, (ha->num_vfs - 1));
+	QLNX_LOCK(ha);
+	if (ha->vf_attr == NULL || vfnum >= ha->num_vfs) {
+		QL_DPRINT1(ha, "VF[%u] is not configured\n", vfnum);
+		QLNX_UNLOCK(ha);
+		return (EINVAL);
 	}
-		
-	vf_attr = &ha->vf_attr[vfnum];
+	QLNX_UNLOCK(ha);
+	bzero(mac_addr, sizeof(mac_addr));
 
         if (nvlist_exists_binary(params, "mac-addr")) {
                 mac = nvlist_get_binary(params, "mac-addr", &size);
-                bcopy(mac, vf_attr->mac_addr, ETHER_ADDR_LEN);
+		if (size != ETHER_ADDR_LEN)
+			return (EINVAL);
+		memcpy(mac_addr, mac, ETHER_ADDR_LEN);
 		device_printf(dev,
 			"%s: mac_addr = %02x:%02x:%02x:%02x:%02x:%02x\n", 
-			__func__, vf_attr->mac_addr[0],
-			vf_attr->mac_addr[1], vf_attr->mac_addr[2],
-			vf_attr->mac_addr[3], vf_attr->mac_addr[4],
-			vf_attr->mac_addr[5]);
+			__func__, mac_addr[0], mac_addr[1], mac_addr[2],
+			mac_addr[3], mac_addr[4], mac_addr[5]);
 		p_hwfn = &ha->cdev.hwfns[0];
-		ecore_iov_bulletin_set_mac(p_hwfn, vf_attr->mac_addr,
-			vfnum);
+		rc = ecore_iov_bulletin_set_mac(p_hwfn, mac_addr, vfnum);
+		if (rc != ECORE_SUCCESS)
+			return (EIO);
+	}
+
+	QLNX_LOCK(ha);
+	if (ha->vf_attr == NULL || vfnum >= ha->num_vfs) {
+		QLNX_UNLOCK(ha);
+		return (ENXIO);
 	}
+	vf_attr = &ha->vf_attr[vfnum];
+	memcpy(vf_attr->mac_addr, mac_addr, ETHER_ADDR_LEN);
+	vf_attr->configured = true;
+	QLNX_UNLOCK(ha);
 
 	QL_DPRINT2(ha," dev = %p exit vfnum = %d\n", dev, vfnum);
 	return (0);
 }
 
+static int
+qlnx_vf_status(if_t ifp, struct if_vf_status **statusp)
+{
+	qlnx_vf_attr_t *snapshot;
+	struct if_vf_info *vf;
+	struct if_vf_status *status;
+	qlnx_host_t *ha;
+	uint16_t num_vfs;
+	int i;
+
+	ha = if_getsoftc(ifp);
+	QLNX_LOCK(ha);
+	if (!ha->sriov_initialized) {
+		QLNX_UNLOCK(ha);
+		return (EOPNOTSUPP);
+	}
+	num_vfs = ha->num_vfs;
+	if (num_vfs == 0) {
+		QLNX_UNLOCK(ha);
+		status = if_vf_status_alloc(0);
+		if (status == NULL)
+			return (ENOMEM);
+		*statusp = status;
+		return (0);
+	}
+	if (ha->vf_attr == NULL) {
+		QLNX_UNLOCK(ha);
+		return (EIO);
+	}
+	snapshot = mallocarray(num_vfs, sizeof(*snapshot), M_QLNXBUF,
+	    M_NOWAIT);
+	if (snapshot == NULL) {
+		QLNX_UNLOCK(ha);
+		return (ENOMEM);
+	}
+	memcpy(snapshot, ha->vf_attr, num_vfs * sizeof(*snapshot));
+	QLNX_UNLOCK(ha);
+
+	status = if_vf_status_alloc(num_vfs);
+	if (status == NULL) {
+		free(snapshot, M_QLNXBUF);
+		return (ENOMEM);
+	}
+	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 (!ETHER_IS_ZERO(snapshot[i].mac_addr)) {
+			memcpy(vf->mac, snapshot[i].mac_addr, sizeof(vf->mac));
+			vf->fields |= IFVF_F_MAC;
+		}
+		if (snapshot[i].queues_valid) {
+			vf->tx_queue_count = snapshot[i].num_rings;
+			vf->rx_queue_count = snapshot[i].num_rings;
+			vf->fields |= IFVF_F_NUM_TX_QUEUES |
+			    IFVF_F_NUM_RX_QUEUES;
+		}
+	}
+	free(snapshot, M_QLNXBUF);
+	*statusp = status;
+	return (0);
+}
+
 static void
 qlnx_handle_vf_msg(qlnx_host_t *ha, struct ecore_hwfn *p_hwfn)
 {