git: aae9d87526e1 - stable/15 - mlx5en: Report SR-IOV VF status

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

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

commit aae9d87526e183395469a9901da3106ef7454af0
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-08 17:50:12 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-10-31 18:57:04 +0000

    mlx5en: Report SR-IOV VF status
    
    Query each enabled eswitch vport to report its effective MAC and VLAN
    configuration and administrative link-state policy.  Serialize the
    query with vport enable and disable, and hold the interface state lock
    so teardown joins an in-progress report.
    
    Propagate firmware errors from MAC, administrative link-state, and
    supported VLAN queries instead of returning a partially initialized
    configuration.  When the hardware lacks CVLAN enforcement, report the
    remaining status with no imposed access VLAN.
    
    Keep the existing caller-owned vport-info structure unchanged on
    stable/15.  Return the additional CVLAN state through a new helper,
    and retain the original query entry point as a compatibility wrapper.
    
    Reviewed by:    kib
    Differential Revision:  https://reviews.freebsd.org/D58740
    
    (cherry picked from commit 0c08d85c99b7b57e45816d011685d349b240a3e3)
---
 sys/dev/mlx5/mlx5_core/eswitch.h      |   2 +
 sys/dev/mlx5/mlx5_core/mlx5_eswitch.c |  38 +++++++---
 sys/dev/mlx5/mlx5_core/mlx5_vport.c   |  20 +++++-
 sys/dev/mlx5/mlx5_en/mlx5_en_main.c   | 127 ++++++++++++++++++++++++++++++++++
 sys/dev/mlx5/vport.h                  |   2 +
 5 files changed, 177 insertions(+), 12 deletions(-)

diff --git a/sys/dev/mlx5/mlx5_core/eswitch.h b/sys/dev/mlx5/mlx5_core/eswitch.h
index 50d06951bf07..8ab5bb122162 100644
--- a/sys/dev/mlx5/mlx5_core/eswitch.h
+++ b/sys/dev/mlx5/mlx5_core/eswitch.h
@@ -165,5 +165,7 @@ int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
 				int vport, u16 vlan, u8 qos);
 int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
 				  int vport, struct mlx5_esw_vport_info *evi);
+int mlx5_eswitch_get_vport_config_cvlan(struct mlx5_eswitch *esw,
+	int vport, struct mlx5_esw_vport_info *evi, bool *cvlan_enabled);
 
 #endif /* __MLX5_ESWITCH_H__ */
diff --git a/sys/dev/mlx5/mlx5_core/mlx5_eswitch.c b/sys/dev/mlx5/mlx5_core/mlx5_eswitch.c
index 30f04144502b..89b5fa3f7ed1 100644
--- a/sys/dev/mlx5/mlx5_core/mlx5_eswitch.c
+++ b/sys/dev/mlx5/mlx5_core/mlx5_eswitch.c
@@ -130,7 +130,7 @@ static int query_esw_vport_context_cmd(struct mlx5_core_dev *mdev, u32 vport,
 }
 
 static int query_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport,
-				 u16 *vlan, u8 *qos)
+				 u16 *vlan, u8 *qos, bool *enabled)
 {
 	u32 out[MLX5_ST_SZ_DW(query_esw_vport_context_out)] = {0};
 	int err;
@@ -139,6 +139,7 @@ static int query_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport,
 
 	*vlan = 0;
 	*qos = 0;
+	*enabled = false;
 
 	if (!MLX5_CAP_ESW(dev, vport_cvlan_strip) ||
 	    !MLX5_CAP_ESW(dev, vport_cvlan_insert_if_not_exist))
@@ -154,7 +155,8 @@ static int query_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport,
 	cvlan_insert = MLX5_GET(query_esw_vport_context_out, out,
 				esw_vport_context.vport_cvlan_insert);
 
-	if (cvlan_strip || cvlan_insert) {
+	*enabled = cvlan_strip || cvlan_insert;
+	if (*enabled) {
 		*vlan = MLX5_GET(query_esw_vport_context_out, out,
 				 esw_vport_context.cvlan_id);
 		*qos = MLX5_GET(query_esw_vport_context_out, out,
@@ -1218,9 +1220,20 @@ int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw,
 
 int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
 				  int vport, struct mlx5_esw_vport_info *ivi)
+{
+	bool cvlan_enabled;
+
+	return mlx5_eswitch_get_vport_config_cvlan(esw, vport, ivi,
+	    &cvlan_enabled);
+}
+
+/* Preserve the existing caller-supplied structure size on stable/15. */
+int mlx5_eswitch_get_vport_config_cvlan(struct mlx5_eswitch *esw,
+	int vport, struct mlx5_esw_vport_info *ivi, bool *cvlan_enabled)
 {
 	u16 vlan;
-	u8 qos;
+	u8 linkstate, qos;
+	int err;
 
 	if (!ESW_ALLOWED(esw))
 		return -EPERM;
@@ -1230,11 +1243,19 @@ int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
 	memset(ivi, 0, sizeof(*ivi));
 	ivi->vf = vport - 1;
 
-	mlx5_query_nic_vport_mac_address(esw->dev, vport, ivi->mac);
-	ivi->linkstate = mlx5_query_vport_admin_state(esw->dev,
-						      MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
-						      vport);
-	query_esw_vport_cvlan(esw->dev, vport, &vlan, &qos);
+	err = mlx5_query_nic_vport_mac_address(esw->dev, vport, ivi->mac);
+	if (err != 0)
+		return err;
+	err = mlx5_query_vport_admin_state_checked(esw->dev,
+	    MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT, vport,
+	    &linkstate);
+	if (err != 0)
+		return err;
+	ivi->linkstate = linkstate;
+	err = query_esw_vport_cvlan(esw->dev, vport, &vlan, &qos,
+				    cvlan_enabled);
+	if (err != 0 && err != -ENOTSUPP)
+		return err;
 	ivi->vlan = vlan;
 	ivi->qos = qos;
 	ivi->spoofchk = 0;
@@ -1273,4 +1294,3 @@ int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
 	mutex_unlock(&evport->state_lock);
 	return err;
 }
-
diff --git a/sys/dev/mlx5/mlx5_core/mlx5_vport.c b/sys/dev/mlx5/mlx5_core/mlx5_vport.c
index 67893360044e..9d3e3c752111 100644
--- a/sys/dev/mlx5/mlx5_core/mlx5_vport.c
+++ b/sys/dev/mlx5/mlx5_core/mlx5_vport.c
@@ -64,13 +64,27 @@ u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport)
 }
 EXPORT_SYMBOL_GPL(mlx5_query_vport_state);
 
-u8 mlx5_query_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport)
+int
+mlx5_query_vport_admin_state_checked(struct mlx5_core_dev *mdev, u8 opmod,
+				     u16 vport, u8 *state)
 {
 	u32 out[MLX5_ST_SZ_DW(query_vport_state_out)] = {0};
+	int err;
 
-	_mlx5_query_vport_state(mdev, opmod, vport, out, sizeof(out));
+	err = _mlx5_query_vport_state(mdev, opmod, vport, out, sizeof(out));
+	if (err != 0)
+		return err;
+	*state = MLX5_GET(query_vport_state_out, out, admin_state);
+	return 0;
+}
+
+u8 mlx5_query_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport)
+{
+	u8 state;
 
-	return MLX5_GET(query_vport_state_out, out, admin_state);
+	state = 0;
+	(void)mlx5_query_vport_admin_state_checked(mdev, opmod, vport, &state);
+	return (state);
 }
 EXPORT_SYMBOL(mlx5_query_vport_admin_state);
 
diff --git a/sys/dev/mlx5/mlx5_en/mlx5_en_main.c b/sys/dev/mlx5/mlx5_en/mlx5_en_main.c
index c565c22c6028..c12cecb1064d 100644
--- a/sys/dev/mlx5/mlx5_en/mlx5_en_main.c
+++ b/sys/dev/mlx5/mlx5_en/mlx5_en_main.c
@@ -37,14 +37,19 @@
 #include <machine/atomic.h>
 
 #include <net/debugnet.h>
+#include <net/if_vf_status.h>
 #include <netinet/tcp_ratelimit.h>
 #include <netipsec/keydb.h>
 #include <netipsec/ipsec_offload.h>
 
+#include <dev/mlx5/mlx5_core/eswitch.h>
+
 static int mlx5e_get_wqe_sz(struct mlx5e_priv *priv, u32 *wqe_sz, u32 *nsegs);
 static if_snd_tag_query_t mlx5e_ul_snd_tag_query;
 static if_snd_tag_free_t mlx5e_ul_snd_tag_free;
 
+static int mlx5e_vf_status(if_t, struct if_vf_status **);
+
 struct mlx5e_channel_param {
 	struct mlx5e_rq_param rq;
 	struct mlx5e_sq_param sq;
@@ -3516,6 +3521,127 @@ mlx5e_is_tlsrx_capable(struct mlx5_core_dev *mdev)
 	return (false);
 }
 
+static int
+mlx5e_vf_status(if_t ifp, struct if_vf_status **statusp)
+{
+	struct mlx5e_vf_status {
+		u8 mac[ETH_ALEN];
+		u16 vlan;
+		u8 qos;
+		bool cvlan_enabled;
+		u32 vf;
+		u32 linkstate;
+	} *snapshot;
+	struct mlx5_esw_vport_info info;
+	struct mlx5_eswitch *esw;
+	struct mlx5_vport *vport;
+	struct if_vf_info *vf;
+	struct if_vf_status *status;
+	struct mlx5e_priv *priv;
+	int error, i, num_vfs;
+
+	priv = if_getsoftc(ifp);
+	PRIV_LOCK(priv);
+	if (priv->gone != 0) {
+		PRIV_UNLOCK(priv);
+		return (EOPNOTSUPP);
+	}
+	esw = priv->mdev->priv.eswitch;
+	if (!priv->mdev->iov_pf || esw == NULL || esw->total_vports <= 1) {
+		PRIV_UNLOCK(priv);
+		return (EOPNOTSUPP);
+	}
+	snapshot = mallocarray(esw->total_vports - 1, sizeof(*snapshot),
+	    M_DEVBUF, M_WAITOK | M_ZERO);
+	num_vfs = 0;
+	for (i = 1; i < esw->total_vports; i++) {
+		vport = &esw->vports[i];
+		mutex_lock(&vport->state_lock);
+		if (!vport->enabled) {
+			mutex_unlock(&vport->state_lock);
+			continue;
+		}
+		/*
+		 * The eswitch does not cache all effective vport policy.  Query
+		 * firmware while the vport is stable and return errors rather than
+		 * publishing a partial snapshot.
+		 */
+		error = mlx5_eswitch_get_vport_config_cvlan(esw, i, &info,
+		    &snapshot[num_vfs].cvlan_enabled);
+		mutex_unlock(&vport->state_lock);
+		if (error != 0) {
+			PRIV_UNLOCK(priv);
+			free(snapshot, M_DEVBUF);
+			return (-error);
+		}
+		snapshot[num_vfs].vf = info.vf;
+		snapshot[num_vfs].vlan = info.vlan;
+		snapshot[num_vfs].qos = info.qos;
+		snapshot[num_vfs].linkstate = info.linkstate;
+		memcpy(snapshot[num_vfs].mac, info.mac, ETH_ALEN);
+		num_vfs++;
+	}
+	PRIV_UNLOCK(priv);
+	if (num_vfs == 0) {
+		free(snapshot, M_DEVBUF);
+		status = if_vf_status_alloc(0);
+		if (status == NULL)
+			return (ENOMEM);
+		*statusp = status;
+		return (0);
+	}
+
+	status = if_vf_status_alloc(num_vfs);
+	if (status == NULL) {
+		free(snapshot, M_DEVBUF);
+		return (ENOMEM);
+	}
+	for (i = 0; i < num_vfs; i++) {
+		vf = &status->vfs[i];
+		vf->fields = IFVF_F_CONFIGURED | IFVF_F_VLAN_MODE;
+		vf->index = snapshot[i].vf;
+		vf->configured = true;
+		/* An active priority policy may have both VID and PCP zero. */
+		if (snapshot[i].cvlan_enabled) {
+			vf->vlan_pcp = snapshot[i].qos;
+			vf->fields |= IFVF_F_VLAN_PCP;
+		}
+		if (!ETHER_IS_ZERO(snapshot[i].mac)) {
+			memcpy(vf->mac, snapshot[i].mac, sizeof(vf->mac));
+			vf->fields |= IFVF_F_MAC;
+		}
+		/* A priority-only tag does not impose an access VLAN. */
+		if (snapshot[i].vlan == 0)
+			vf->vlan_mode = IFVF_VLAN_TRUNK;
+		else {
+			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;
+		}
+		switch (snapshot[i].linkstate) {
+		case MLX5_ESW_VPORT_ADMIN_STATE_DOWN:
+			vf->link_state_policy = IFVF_LINK_DOWN;
+			break;
+		case MLX5_ESW_VPORT_ADMIN_STATE_UP:
+			vf->link_state_policy = IFVF_LINK_UP;
+			break;
+		case MLX5_ESW_VPORT_ADMIN_STATE_AUTO:
+			vf->link_state_policy = IFVF_LINK_AUTO;
+			break;
+		default:
+			vf->link_state_policy = IFVF_LINK_UNKNOWN;
+			break;
+		}
+		vf->fields |= IFVF_F_LINK_STATE_POLICY;
+	}
+	free(snapshot, M_DEVBUF);
+	*statusp = status;
+	return (0);
+}
+
 static int
 mlx5e_ioctl(if_t ifp, u_long command, caddr_t data)
 {
@@ -4643,6 +4769,7 @@ mlx5e_create_ifp(struct mlx5_core_dev *mdev)
 	if_setinitfn(ifp, mlx5e_open);
 	if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
 	if_setioctlfn(ifp, mlx5e_ioctl);
+	if_setvfstatusfn(ifp, mlx5e_vf_status);
 	if_settransmitfn(ifp, mlx5e_xmit);
 	if_setqflushfn(ifp, if_qflush);
 	if_setgetcounterfn(ifp, mlx5e_get_counter);
diff --git a/sys/dev/mlx5/vport.h b/sys/dev/mlx5/vport.h
index fbb24773c128..335374cd4b49 100644
--- a/sys/dev/mlx5/vport.h
+++ b/sys/dev/mlx5/vport.h
@@ -61,6 +61,8 @@ int mlx5_nic_vport_modify_local_lb(struct mlx5_core_dev *mdev,
 u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport);
 u8 mlx5_query_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod,
 				u16 vport);
+int mlx5_query_vport_admin_state_checked(struct mlx5_core_dev *mdev,
+				u8 opmod, u16 vport, u8 *state);
 int mlx5_modify_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod,
 				  u16 vport, u8 state);