git: a0f2994b312e - main - iavf: Report the programmed VF RSS configuration

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Sat, 31 Oct 2026 23:30:08 UTC
The branch main has been updated by kbowling:

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

commit a0f2994b312e5a78ffce7dd7bc4521455edbc2cd
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-10-31 23:27:44 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-10-31 23:28:22 +0000

    iavf: Report the programmed VF RSS configuration
    
    Implement the iflib RSS key and hash query methods for register-based
    and PF-managed RSS. Cache the programmed key and HENA selections so hn
    can synchronize its synthetic RSS configuration with the VF without
    reading hardware or issuing mailbox requests from the query path.
    
    For PF-managed RSS, publish the snapshot only after successful replies
    to both CONFIG_RSS_KEY and SET_RSS_HENA. Failed sends, rejected requests,
    and missing replies leave it unavailable. Protect the snapshot with
    vc_mtx and invalidate it at init, stop, mailbox loss, and reset
    notification. Gate queries with the existing running, reset, and mailbox
    state.
    
    Report the first 40 key bytes used by standard IPv4/IPv6 tuples, or the
    shorter key length supplied by the PF. Preserve register byte order and
    report the merged HENA on the direct-programming path. Translate the six
    representable IPv4/IPv6 TCP/UDP selections without treating fragment
    PCTYPEs as IPv6 extension-header selections. The single-queue register
    path reports RSS_FUNC_NONE.
    
    Leave RSS policy and queue distribution unchanged. Queries return ENXIO
    until the current configuration is confirmed, without waiting for a PF
    reply themselves.
    
    MFC after:      2 weeks
    Sponsored by:   BBOX.io
    Differential Revision:  https://reviews.freebsd.org/D59808
---
 sys/dev/iavf/iavf_iflib.h     |  8 +++++
 sys/dev/iavf/iavf_lib.c       | 25 +++++++++++++
 sys/dev/iavf/iavf_vc_common.c | 50 ++++++++++++++++++++++----
 sys/dev/iavf/if_iavf_iflib.c  | 84 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 161 insertions(+), 6 deletions(-)

diff --git a/sys/dev/iavf/iavf_iflib.h b/sys/dev/iavf/iavf_iflib.h
index bbd05e8279ca..3b4de1c8bca7 100644
--- a/sys/dev/iavf/iavf_iflib.h
+++ b/sys/dev/iavf/iavf_iflib.h
@@ -366,6 +366,13 @@ struct iavf_sc {
 	struct virtchnl_vf_resource *vf_res;
 	struct virtchnl_vsi_resource *vsi_res;
 
+	/* RSS query snapshot and outstanding requests, protected by vc_mtx. */
+	u8			rss_key[RSS_KEYSIZE];
+	u16			rss_key_size;
+	u64			rss_hena;
+	u32			rss_pending;
+	u32			rss_configured;
+
 	/* Misc stats maintained by the driver */
 	u64			admin_irq;
 
@@ -407,4 +414,5 @@ void		iavf_add_queues_sysctls(device_t dev, struct iavf_vsi *vsi);
 
 void	iavf_enable_intr(struct iavf_vsi *);
 void	iavf_disable_intr(struct iavf_vsi *);
+void	iavf_invalidate_rss(struct iavf_sc *);
 #endif /* _IAVF_IFLIB_H_ */
diff --git a/sys/dev/iavf/iavf_lib.c b/sys/dev/iavf/iavf_lib.c
index c23e14a47444..01d14a58f0b9 100644
--- a/sys/dev/iavf/iavf_lib.c
+++ b/sys/dev/iavf/iavf_lib.c
@@ -1083,6 +1083,11 @@ iavf_config_rss_reg(struct iavf_sc *sc)
 	u32		rss_seed[IAVF_RSS_KEY_SIZE_REG] = {0};
 	u32		rss_hash_config;
 
+	IAVF_VC_LOCK_ASSERT(sc);
+	sc->rss_hena = 0;
+	sc->rss_key_size = 0;
+	sc->rss_configured = IAVF_FLAG_AQ_CONFIG_RSS_KEY |
+	    IAVF_FLAG_AQ_SET_RSS_HENA;
 	/* Don't set up RSS if using a single queue */
 	if (IAVF_NRXQS(vsi) == 1) {
 		wr32(hw, IAVF_VFQF_HENA(0), 0);
@@ -1097,6 +1102,12 @@ iavf_config_rss_reg(struct iavf_sc *sc)
 	/* Fill out hash function seed */
 	for (i = 0; i < IAVF_RSS_KEY_SIZE_REG; i++)
                 wr32(hw, IAVF_VFQF_HKEY(i), rss_seed[i]);
+	/* Report the register byte order, not the host's u32 representation. */
+	_Static_assert(sizeof(rss_seed) >= sizeof(sc->rss_key),
+	    "RSS register key too small");
+	for (i = 0; i < RSS_KEYSIZE / sizeof(u32); i++)
+		le32enc(sc->rss_key + i * sizeof(u32), rss_seed[i]);
+	sc->rss_key_size = sizeof(sc->rss_key);
 
 	/* Enable PCTYPES for RSS: */
 	rss_hash_config = rss_gethashconfig();
@@ -1119,6 +1130,7 @@ iavf_config_rss_reg(struct iavf_sc *sc)
 	hena |= set_hena;
 	wr32(hw, IAVF_VFQF_HENA(0), (u32)hena);
 	wr32(hw, IAVF_VFQF_HENA(1), (u32)(hena >> 32));
+	sc->rss_hena = hena;
 
 	/* Populate the LUT with max no. of queues in round robin fashion */
 	for (i = 0, j = 0; i < IAVF_RSS_VSI_LUT_SIZE; i++, j++) {
@@ -1177,7 +1189,9 @@ iavf_config_rss(struct iavf_sc *sc)
 {
 	if (sc->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_RSS_REG) {
 		iavf_dbg_info(sc, "Setting up RSS using VF registers...\n");
+		IAVF_VC_LOCK(sc);
 		iavf_config_rss_reg(sc);
+		IAVF_VC_UNLOCK(sc);
 	} else if (sc->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_RSS_PF) {
 		iavf_dbg_info(sc, "Setting up RSS using messages to PF...\n");
 		iavf_config_rss_pf(sc);
@@ -1185,6 +1199,17 @@ iavf_config_rss(struct iavf_sc *sc)
 		device_printf(sc->dev, "VF does not support RSS capability sent by PF.\n");
 }
 
+/* Discard RSS query state when leaving the current running instance. */
+void
+iavf_invalidate_rss(struct iavf_sc *sc)
+{
+
+	IAVF_VC_LOCK(sc);
+	sc->rss_pending = 0;
+	sc->rss_configured = 0;
+	IAVF_VC_UNLOCK(sc);
+}
+
 /**
  * iavf_config_promisc - setup promiscuous mode
  * @sc: device private softc
diff --git a/sys/dev/iavf/iavf_vc_common.c b/sys/dev/iavf/iavf_vc_common.c
index 5f39f0334e83..2a11f02db59b 100644
--- a/sys/dev/iavf/iavf_vc_common.c
+++ b/sys/dev/iavf/iavf_vc_common.c
@@ -723,9 +723,11 @@ int
 iavf_config_rss_key(struct iavf_sc *sc)
 {
 	struct virtchnl_rss_key *rss_key_msg;
-	int msg_len, key_length;
+	int error, msg_len, key_length;
 	u32		rss_seed[IAVF_RSS_KEY_SIZE_REG] = {0};
 
+	mtx_assert(&sc->vc_mtx, MA_NOTOWNED);
+
 	/* Fetch the configured RSS key */
 	rss_getkey((uint8_t *) &rss_seed);
 
@@ -746,11 +748,21 @@ iavf_config_rss_key(struct iavf_sc *sc)
 	iavf_dbg_vc(sc, "%s: vsi_id %d, key_len %d\n", __func__,
 	    rss_key_msg->vsi_id, rss_key_msg->key_len);
 
-	iavf_send_pf_msg(sc, VIRTCHNL_OP_CONFIG_RSS_KEY,
+	IAVF_VC_LOCK(sc);
+	sc->rss_configured &= ~IAVF_FLAG_AQ_CONFIG_RSS_KEY;
+	sc->rss_pending |= IAVF_FLAG_AQ_CONFIG_RSS_KEY;
+	/* Standard IPv4/IPv6 tuples use the first 40 bytes of the 52-byte key. */
+	sc->rss_key_size = MIN(key_length, sizeof(sc->rss_key));
+	bzero(sc->rss_key, sizeof(sc->rss_key));
+	memcpy(sc->rss_key, rss_seed, sc->rss_key_size);
+	error = iavf_send_pf_msg(sc, VIRTCHNL_OP_CONFIG_RSS_KEY,
 			  (u8 *)rss_key_msg, msg_len);
+	if (error != 0)
+		sc->rss_pending &= ~IAVF_FLAG_AQ_CONFIG_RSS_KEY;
+	IAVF_VC_UNLOCK(sc);
 
 	free(rss_key_msg, M_IAVF);
-	return (0);
+	return (error);
 }
 
 /**
@@ -761,13 +773,16 @@ iavf_config_rss_key(struct iavf_sc *sc)
  *
  * @remark the reply from the PF is not checked by this function.
  *
- * @returns zero
+ * @returns zero on success, or an error code on failure.
  */
 int
 iavf_set_rss_hena(struct iavf_sc *sc)
 {
 	struct virtchnl_rss_hena hena;
 	struct iavf_hw *hw = &sc->hw;
+	int error;
+
+	mtx_assert(&sc->vc_mtx, MA_NOTOWNED);
 
 	if (hw->mac.type == IAVF_MAC_VF)
 		hena.hena = IAVF_DEFAULT_RSS_HENA_AVF;
@@ -776,9 +791,16 @@ iavf_set_rss_hena(struct iavf_sc *sc)
 	else
 		hena.hena = IAVF_DEFAULT_RSS_HENA_BASE;
 
-	iavf_send_pf_msg(sc, VIRTCHNL_OP_SET_RSS_HENA,
+	IAVF_VC_LOCK(sc);
+	sc->rss_configured &= ~IAVF_FLAG_AQ_SET_RSS_HENA;
+	sc->rss_pending |= IAVF_FLAG_AQ_SET_RSS_HENA;
+	sc->rss_hena = hena.hena;
+	error = iavf_send_pf_msg(sc, VIRTCHNL_OP_SET_RSS_HENA,
 	    (u8 *)&hena, sizeof(hena));
-	return (0);
+	if (error != 0)
+		sc->rss_pending &= ~IAVF_FLAG_AQ_SET_RSS_HENA;
+	IAVF_VC_UNLOCK(sc);
+	return (error);
 }
 
 /**
@@ -1207,6 +1229,9 @@ iavf_vc_completion(struct iavf_sc *sc,
     enum virtchnl_status_code v_retval, u8 *msg, u16 msglen __unused)
 {
 	device_t	dev = sc->dev;
+	u32 rss_op;
+
+	IAVF_VC_LOCK_ASSERT(sc);
 
 	if (v_opcode != VIRTCHNL_OP_GET_STATS)
 		iavf_dbg_vc(sc, "%s: opcode %s\n", __func__,
@@ -1223,6 +1248,8 @@ iavf_vc_completion(struct iavf_sc *sc,
 		case VIRTCHNL_EVENT_RESET_IMPENDING:
 			device_printf(dev, "PF initiated reset!\n");
 			iavf_set_state(&sc->state, IAVF_STATE_RESET_PENDING);
+			sc->rss_pending = 0;
+			sc->rss_configured = 0;
 			break;
 		default:
 			iavf_dbg_vc(sc, "Unknown event %d from AQ\n",
@@ -1233,6 +1260,17 @@ iavf_vc_completion(struct iavf_sc *sc,
 		return;
 	}
 
+	rss_op = 0;
+	if (v_opcode == VIRTCHNL_OP_CONFIG_RSS_KEY)
+		rss_op = IAVF_FLAG_AQ_CONFIG_RSS_KEY;
+	else if (v_opcode == VIRTCHNL_OP_SET_RSS_HENA)
+		rss_op = IAVF_FLAG_AQ_SET_RSS_HENA;
+	if (sc->rss_pending & rss_op) {
+		sc->rss_pending &= ~rss_op;
+		if (v_retval == VIRTCHNL_STATUS_SUCCESS)
+			sc->rss_configured |= rss_op;
+	}
+
 	/* Catch-all error response */
 	if (v_retval) {
 		bool print_error = true;
diff --git a/sys/dev/iavf/if_iavf_iflib.c b/sys/dev/iavf/if_iavf_iflib.c
index f0d1f39ee20c..e761ac86c9b0 100644
--- a/sys/dev/iavf/if_iavf_iflib.c
+++ b/sys/dev/iavf/if_iavf_iflib.c
@@ -85,6 +85,8 @@ static void	 iavf_if_timer(if_ctx_t ctx, uint16_t qid);
 static void	 iavf_if_vlan_register(if_ctx_t ctx, u16 vtag);
 static void	 iavf_if_vlan_unregister(if_ctx_t ctx, u16 vtag);
 static uint64_t	 iavf_if_get_counter(if_ctx_t ctx, ift_counter cnt);
+static int	 iavf_if_get_rss_key(if_ctx_t, struct ifrsskey *);
+static int	 iavf_if_get_rss_hash(if_ctx_t, struct ifrsshash *);
 static void	 iavf_if_init(if_ctx_t ctx);
 static void	 iavf_if_stop(if_ctx_t ctx);
 static bool	 iavf_if_needs_restart(if_ctx_t, enum iflib_restart_event);
@@ -195,6 +197,8 @@ static device_method_t iavf_if_methods[] = {
 	DEVMETHOD(ifdi_vlan_register, iavf_if_vlan_register),
 	DEVMETHOD(ifdi_vlan_unregister, iavf_if_vlan_unregister),
 	DEVMETHOD(ifdi_get_counter, iavf_if_get_counter),
+	DEVMETHOD(ifdi_get_rss_key, iavf_if_get_rss_key),
+	DEVMETHOD(ifdi_get_rss_hash, iavf_if_get_rss_hash),
 	DEVMETHOD(ifdi_needs_restart, iavf_if_needs_restart),
 	DEVMETHOD_END
 };
@@ -881,6 +885,7 @@ iavf_mbx_lost(struct iavf_sc *sc)
 	atomic_store_rel_32(&sc->vc_reinit_required, 1);
 	if (atomic_readandclear_32(&sc->mbx_ready) == 0)
 		return;
+	iavf_invalidate_rss(sc);
 	vsi = &sc->vsi;
 	iavf_clear_state(&sc->state, IAVF_STATE_RUNNING);
 	sc->link_up = false;
@@ -1032,6 +1037,7 @@ iavf_if_init(if_ctx_t ctx)
 	INIT_DBG_IF(ifp, "begin");
 
 	sx_assert(iflib_ctx_lock_get(ctx), SA_XLOCKED);
+	iavf_invalidate_rss(sc);
 	iavf_mbx_retry_prepare(sc);
 	replay_filters = atomic_load_acq_32(&sc->vc_reinit_required) != 0;
 
@@ -1120,6 +1126,83 @@ fail:
 	iavf_mbx_retry_failed(ctx);
 }
 
+/* Query only a complete configuration from the current running instance. */
+static int
+iavf_rss_query_status(struct iavf_sc *sc)
+{
+
+	sx_assert(iflib_ctx_lock_get(sc->vsi.ctx), SA_XLOCKED);
+	IAVF_VC_LOCK_ASSERT(sc);
+	if (sc->vf_res == NULL)
+		return (ENXIO);
+	if ((sc->vf_res->vf_cap_flags & (VIRTCHNL_VF_OFFLOAD_RSS_REG |
+	    VIRTCHNL_VF_OFFLOAD_RSS_PF)) == 0)
+		return (EOPNOTSUPP);
+	if (!iavf_test_state(&sc->state, IAVF_STATE_RUNNING) ||
+	    iavf_test_state(&sc->state, IAVF_STATE_RESET_PENDING) ||
+	    iavf_test_state(&sc->state, IAVF_STATE_RESET_REQUIRED) ||
+	    atomic_load_acq_32(&sc->mbx_ready) == 0 ||
+	    atomic_load_acq_32(&sc->vc_reinit_required) != 0 ||
+	    sc->rss_configured != (IAVF_FLAG_AQ_CONFIG_RSS_KEY |
+	    IAVF_FLAG_AQ_SET_RSS_HENA))
+		return (ENXIO);
+	return (0);
+}
+
+static int
+iavf_if_get_rss_key(if_ctx_t ctx, struct ifrsskey *ifrk)
+{
+	struct iavf_sc *sc = iavf_sc_from_ctx(ctx);
+	int error;
+
+	IAVF_VC_LOCK(sc);
+	error = iavf_rss_query_status(sc);
+	if (error == 0) {
+		ifrk->ifrk_func = sc->rss_hena == 0 ? RSS_FUNC_NONE :
+		    RSS_FUNC_TOEPLITZ;
+		ifrk->ifrk_keylen = sc->rss_hena == 0 ? 0 : sc->rss_key_size;
+		_Static_assert(sizeof(ifrk->ifrk_key) >= sizeof(sc->rss_key),
+		    "RSS query buffer too small");
+		bzero(ifrk->ifrk_key, sizeof(ifrk->ifrk_key));
+		memcpy(ifrk->ifrk_key, sc->rss_key, ifrk->ifrk_keylen);
+	}
+	IAVF_VC_UNLOCK(sc);
+	return (error);
+}
+
+static int
+iavf_if_get_rss_hash(if_ctx_t ctx, struct ifrsshash *ifrh)
+{
+	struct iavf_sc *sc = iavf_sc_from_ctx(ctx);
+	u64 hena;
+	int error;
+
+	IAVF_VC_LOCK(sc);
+	error = iavf_rss_query_status(sc);
+	if (error == 0) {
+		hena = sc->rss_hena;
+		ifrh->ifrh_func = hena == 0 ? RSS_FUNC_NONE : RSS_FUNC_TOEPLITZ;
+		ifrh->ifrh_types = 0;
+		/* Fragment PCTYPEs are not IPv6 extension-header selections. */
+		if (hena & (BIT_ULL(IAVF_FILTER_PCTYPE_NONF_IPV4_OTHER) |
+		    BIT_ULL(IAVF_FILTER_PCTYPE_FRAG_IPV4)))
+			ifrh->ifrh_types |= RSS_TYPE_IPV4;
+		if (hena & BIT_ULL(IAVF_FILTER_PCTYPE_NONF_IPV4_TCP))
+			ifrh->ifrh_types |= RSS_TYPE_TCP_IPV4;
+		if (hena & BIT_ULL(IAVF_FILTER_PCTYPE_NONF_IPV4_UDP))
+			ifrh->ifrh_types |= RSS_TYPE_UDP_IPV4;
+		if (hena & (BIT_ULL(IAVF_FILTER_PCTYPE_NONF_IPV6_OTHER) |
+		    BIT_ULL(IAVF_FILTER_PCTYPE_FRAG_IPV6)))
+			ifrh->ifrh_types |= RSS_TYPE_IPV6;
+		if (hena & BIT_ULL(IAVF_FILTER_PCTYPE_NONF_IPV6_TCP))
+			ifrh->ifrh_types |= RSS_TYPE_TCP_IPV6;
+		if (hena & BIT_ULL(IAVF_FILTER_PCTYPE_NONF_IPV6_UDP))
+			ifrh->ifrh_types |= RSS_TYPE_UDP_IPV6;
+	}
+	IAVF_VC_UNLOCK(sc);
+	return (error);
+}
+
 /**
  * iavf_if_msix_intr_assign - Assign MSI-X interrupts
  * @ctx: the iflib context pointer
@@ -2129,6 +2212,7 @@ iavf_stop(struct iavf_sc *sc)
 	vsi = &sc->vsi;
 	iavf_mbx_retry_stop(sc);
 	iavf_clear_state(&sc->state, IAVF_STATE_RUNNING);
+	iavf_invalidate_rss(sc);
 
 	iavf_disable_intr(vsi);