Index | Thread | Search

From:
Carsten Beckmann <carsten_beckmann@genua.de>
Subject:
iavf: multiple bugfixes and support for Intel 800 series
To:
"tech@openbsd.org" <tech@openbsd.org>
Date:
Wed, 7 Oct 2026 14:10:09 +0000

Download raw body.

Thread
Hi,

I recently tried using iavf(4) in an OpenBSD VM with an Intel E810-C on
the host. This did not work at all though. iavf(4) went into a quick
reset loop after attach and then just died. While debugging this I
noticed a few other issues which in the end lead to this series of
patches.

The first 6 patches are all bugfixes and affect the current iavf(4) even
with NICs of the Intel 700 series on the host. I did use an Intel XXV710
to debug and verify the fixes.

The last 2 patches are then required to make iavf(4) work with an Intel
E810-C on the host. I believe that it should also work with other
variants of the 800 series now, but I did not have them available to
test it.

I did not want to spam the list with 8 patches without any prior
discussion, so for now all of them follow inline. I'll also attach them
individually in case this makes reviewing easier. Let me know if you
would prefer me to send each patch individually to the list though.


From 1268811f218612658b21006e0e6cc25c569433f5 Mon Sep 17 00:00:00 2001
From: Carsten Beckmann <cbeck@genua.de>
Date: Wed, 16 Sep 2026 14:49:58 +0200
Subject: [PATCH 1/8] iavf: reset atq indices on VF reset

On a VF reset the hardware registers atq_head and atq_tail are reset to
0, but sc_atq_cons and sc_atq_prod keep their old value. If something is
posted to the admin queue the new descriptor is put at whatever position
the driver left off before the reset and atq_tail is updated to that
value. To the PF on the host that looks like the VF just resend all the
previous descriptors which is obviously wrong. Resetting sc_atq_cons and
sc_atq_prod on a VF reset fixes this issue.

While there also initialize atq_head_mask using the correct define. Both
defines have the same value, so this is no functional change.
---
 sys/dev/pci/if_iavf.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/sys/dev/pci/if_iavf.c b/sys/dev/pci/if_iavf.c
index b6e7f681993..e41396e7873 100644
--- a/sys/dev/pci/if_iavf.c
+++ b/sys/dev/pci/if_iavf.c
@@ -758,7 +758,7 @@ static const struct iavf_aq_regs iavf_aq_regs = {
 	.atq_tail	= I40E_VF_ATQT1,
 	.atq_tail_mask	= I40E_VF_ATQT1_ATQT_MASK,
 	.atq_head	= I40E_VF_ATQH1,
-	.atq_head_mask	= I40E_VF_ARQH1_ARQH_MASK,
+	.atq_head_mask	= I40E_VF_ATQH1_ATQH_MASK,
 	.atq_len	= I40E_VF_ATQLEN1,
 	.atq_bal	= I40E_VF_ATQBAL1,
 	.atq_bah	= I40E_VF_ATQBAH1,
@@ -1746,6 +1746,11 @@ iavf_reset(void *xsc)
 		goto failed;
 	}
 
+	mtx_enter(&sc->sc_atq_mtx);
+	sc->sc_atq_cons = 0;
+	sc->sc_atq_prod = 0;
+	mtx_leave(&sc->sc_atq_mtx);
+
 	iavf_arq_unfill(sc);
 	sc->sc_arq_cons = 0;
 	sc->sc_arq_prod = 0;
-- 
2.53.0


From 713dd6f44291f078cb249b1a8deb1b5c02c2cd70 Mon Sep 17 00:00:00 2001
From: Carsten Beckmann <cbeck@genua.de>
Date: Wed, 16 Sep 2026 14:54:10 +0200
Subject: [PATCH 2/8] iavf: improve VF reset handling

Currently VF resets are detected by checking if a register contains
0xdeadbeef in the admin queue interrupt. The interrupt is triggered
because the host driver sends the VF a RESET_IMPENDING event before
actually triggering the VF reset. If the interrupt is processed too
early or too late the temporary 0xdeadbeef state and therefore the VF
reset can be missed. Triggering a reset when a RESET_IMPENDING event
arrives fixes this issue.

In theory this could now allow the reset task to run before the actual
VF reset is triggered by the host. To prevent this the reset task now
waits until the arq_len_enable bit goes low which would indicate a VF
reset. The same approach is used by the Linux iavf driver.
---
 sys/dev/pci/if_iavf.c | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/sys/dev/pci/if_iavf.c b/sys/dev/pci/if_iavf.c
index e41396e7873..5a15f80db48 100644
--- a/sys/dev/pci/if_iavf.c
+++ b/sys/dev/pci/if_iavf.c
@@ -1708,6 +1708,7 @@ iavf_reset(void *xsc)
 	struct iavf_softc *sc = xsc;
 	struct ifnet *ifp = &sc->sc_ac.ac_if;
 	int tries, up, link_state;
+	uint32_t reg;
 
 	NET_LOCK();
 
@@ -1732,7 +1733,20 @@ iavf_reset(void *xsc)
 	sc->sc_got_irq_map = 0;
 
 	for (tries = 0; tries < 100; tries++) {
-		uint32_t reg;
+		reg = iavf_rd(sc, sc->sc_aq_regs->arq_len) &
+		    sc->sc_aq_regs->arq_len_enable;
+		if (!reg)
+			break;
+
+		delay(10000);
+	}
+
+	if (tries == 100) {
+		printf("%s: did not detect VF reset\n", DEVNAME(sc));
+		goto failed;
+	}
+
+	for (tries = 0; tries < 100; tries++) {
 		reg = iavf_rd(sc, I40E_VFGEN_RSTAT) &
 		    I40E_VFGEN_RSTAT_VFR_STATE_MASK;
 		if (reg == IAVF_VFR_VFACTIVE ||
@@ -2654,7 +2668,11 @@ iavf_process_vc_event(struct iavf_softc *sc, struct iavf_aq_desc *desc,
 				if_link_state_change(ifp);
 		}
 		break;
-
+	case IAVF_VC_EVENT_RESET_IMPENDING:
+		printf("%s: VF reset impending\n", DEVNAME(sc));
+		sc->sc_resetting = 1;
+		task_add(systq, &sc->sc_reset_task);
+		break;
 	default:
 		break;
 	}
-- 
2.53.0


From b02aaccc6beb72654c16307217843662ce916eea Mon Sep 17 00:00:00 2001
From: Carsten Beckmann <cbeck@genua.de>
Date: Mon, 21 Sep 2026 10:48:32 +0200
Subject: [PATCH 3/8] iavf: prevent panic in iavf_process_arq during reset

There is a small window during a VF reset where the ICR register in
iavf_intr still contains its normal value but when arq_head is read in
iavf_process_arq it returns 0xdeadbeef. Using this for prod (even
masked) will lead to a panic, so check for 0xdeadbeef first and trigger
a reset if it is detected.
---
 sys/dev/pci/if_iavf.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/sys/dev/pci/if_iavf.c b/sys/dev/pci/if_iavf.c
index 5a15f80db48..6e7921e22e1 100644
--- a/sys/dev/pci/if_iavf.c
+++ b/sys/dev/pci/if_iavf.c
@@ -2723,8 +2723,16 @@ iavf_process_arq(struct iavf_softc *sc, int fill)
 	unsigned int prod;
 	int done = 0;
 
-	prod = iavf_rd(sc, sc->sc_aq_regs->arq_head) &
-	    sc->sc_aq_regs->arq_head_mask;
+	prod = iavf_rd(sc, sc->sc_aq_regs->arq_head);
+
+	if (prod == IAVF_REG_VFR) {
+		printf("%s: VF reset in progress\n", DEVNAME(sc));
+		sc->sc_resetting = 1;
+		task_add(systq, &sc->sc_reset_task);
+		return (1);
+	}
+
+	prod &= sc->sc_aq_regs->arq_head_mask;
 
 	if (cons == prod)
 		return (0);
-- 
2.53.0


From cc10c1d7095f4a3b3c104559aff55883be1cb30a Mon Sep 17 00:00:00 2001
From: Carsten Beckmann <cbeck@genua.de>
Date: Thu, 17 Sep 2026 15:13:33 +0200
Subject: [PATCH 4/8] iavf: rework arq refilling

Currently iavf_arq_fill is not called directly when admin queue
responses are processed in the interrupt. This is fine when it actually
is a response to some command that the driver issued, then the command
issuer calls iavf_arq_fill. Some messages are sent to the VF unsolicited
though, for example LINK_CHANGE events. Since the admin response queue
is never refilled in this case only a few of those messages can be
proccessed and all further messages will then be lost.

iavf_arq_fill can not be called from an interrupt because iavf_aqb_alloc
is not safe in that context. The simplest solution to that is to just
preallocate all buffers during attach. Since iavf_arq_fill no longer
needs to allocate anything it can now always be called which fixes
messages being lost. To not waste too much memory with this approach
also reduce the amount of descriptors in the admin queue to 32.
---
 sys/dev/pci/if_iavf.c | 45 ++++++++++++++++++++++++++++---------------
 1 file changed, 30 insertions(+), 15 deletions(-)

diff --git a/sys/dev/pci/if_iavf.c b/sys/dev/pci/if_iavf.c
index 6e7921e22e1..6c17b44057a 100644
--- a/sys/dev/pci/if_iavf.c
+++ b/sys/dev/pci/if_iavf.c
@@ -507,7 +507,7 @@ struct iavf_rx_wb_desc_32 {
 #define IAVF_ITR2			0x2
 #define IAVF_NOITR			0x3
 
-#define IAVF_AQ_NUM			256
+#define IAVF_AQ_NUM			32
 #define IAVF_AQ_MASK			(IAVF_AQ_NUM - 1)
 #define IAVF_AQ_ALIGN			64 /* lol */
 #define IAVF_AQ_BUFLEN			4096
@@ -691,6 +691,9 @@ static int	iavf_dmamem_alloc(struct iavf_softc *, struct iavf_dmamem *,
 		    bus_size_t, u_int);
 static void	iavf_dmamem_free(struct iavf_softc *, struct iavf_dmamem *);
 
+static struct iavf_aq_buf *
+		iavf_aqb_alloc(struct iavf_softc *);
+static void	iavf_aqb_free(struct iavf_softc *, struct iavf_aq_buf *);
 static int	iavf_arq_fill(struct iavf_softc *, int);
 static void	iavf_arq_unfill(struct iavf_softc *);
 static void	iavf_arq_timeout(void *);
@@ -709,7 +712,7 @@ static int	iavf_config_rss_lut(struct iavf_softc *);
 static int	iavf_config_irq_map(struct iavf_softc *);
 
 static int	iavf_add_del_addr(struct iavf_softc *, uint8_t *, int);
-static int	iavf_process_arq(struct iavf_softc *, int);
+static int	iavf_process_arq(struct iavf_softc *);
 
 static int	iavf_match(struct device *, void *, void *);
 static int	iavf_setup_interrupts(struct iavf_softc *, struct pci_attach_args *);
@@ -936,8 +939,9 @@ iavf_attach(struct device *parent, struct device *self, void *aux)
 	struct iavf_softc *sc = (struct iavf_softc *)self;
 	struct ifnet *ifp = &sc->sc_ac.ac_if;
 	struct pci_attach_args *pa = aux;
+	struct iavf_aq_buf *aqb;
 	pcireg_t memtype;
-	int nmsix, tries;
+	int nmsix, tries, i;
 	unsigned int nqueues;
 
 	rw_init(&sc->sc_cfg_lock, "iavfcfg");
@@ -996,6 +1000,16 @@ iavf_attach(struct device *parent, struct device *self, void *aux)
 		goto free_atq;
 	}
 
+	for (i = 0; i < IAVF_AQ_NUM - 1; i++) {
+		aqb = iavf_aqb_alloc(sc);
+		if (aqb == NULL) {
+			printf("\n" "%s: unable to allocate aqb\n",
+			    DEVNAME(sc));
+			goto free_arq;
+		}
+		SIMPLEQ_INSERT_TAIL(&sc->sc_arq_idle, aqb, aqb_entry);
+	}
+
 	if (!iavf_arq_fill(sc, 0)) {
 		printf("\n" "%s: unable to fill arq descriptors\n",
 		    DEVNAME(sc));
@@ -1130,6 +1144,10 @@ shutdown:
 
 	iavf_arq_unfill(sc);
 free_arq:
+	while ((aqb = SIMPLEQ_FIRST(&sc->sc_arq_idle)) != NULL) {
+		SIMPLEQ_REMOVE_HEAD(&sc->sc_arq_idle, aqb_entry);
+		iavf_aqb_free(sc, aqb);
+	}
 	iavf_dmamem_free(sc, &sc->sc_arq);
 free_atq:
 	iavf_dmamem_free(sc, &sc->sc_atq);
@@ -2568,7 +2586,7 @@ iavf_intr(void *xsc)
 
 	if (ISSET(icr, I40E_VFINT_ICR01_ADMINQ_MASK)) {
 		iavf_atq_done(sc);
-		iavf_process_arq(sc, 0);
+		iavf_process_arq(sc);
 		rv = 1;
 	}
 
@@ -2714,7 +2732,7 @@ iavf_init_admin_queue(struct iavf_softc *sc)
 }
 
 static int
-iavf_process_arq(struct iavf_softc *sc, int fill)
+iavf_process_arq(struct iavf_softc *sc)
 {
 	struct iavf_aq_desc *arq, *iaq;
 	struct iavf_aq_buf *aqb;
@@ -2797,8 +2815,7 @@ iavf_process_arq(struct iavf_softc *sc, int fill)
 		done = 1;
 	} while (cons != prod);
 
-	if (fill)
-		iavf_arq_fill(sc, 1);
+	iavf_arq_fill(sc, 1);
 
 	bus_dmamap_sync(sc->sc_dmat, IAVF_DMA_MAP(&sc->sc_arq),
 	    0, IAVF_DMA_LEN(&sc->sc_arq),
@@ -2909,7 +2926,7 @@ iavf_get_version(struct iavf_softc *sc)
 	iavf_atq_post(sc, &iaq);
 
 	for (tries = 0; tries < 100; tries++) {
-		iavf_process_arq(sc, 1);
+		iavf_process_arq(sc);
 		if (sc->sc_major_ver != -1)
 			break;
 
@@ -2961,7 +2978,7 @@ iavf_get_vf_resources(struct iavf_softc *sc)
 	iavf_atq_post(sc, &iaq);
 
 	for (tries = 0; tries < 100; tries++) {
-		iavf_process_arq(sc, 1);
+		iavf_process_arq(sc);
 		if (sc->sc_got_vf_resources != 0)
 			return (0);
 
@@ -3024,7 +3041,7 @@ iavf_config_irq_map(struct iavf_softc *sc)
 	iavf_atq_post(sc, &iaq);
 
 	for (tries = 0; tries < 100; tries++) {
-		iavf_process_arq(sc, 1);
+		iavf_process_arq(sc);
 		if (sc->sc_got_irq_map != 0)
 			return (0);
 
@@ -3092,10 +3109,9 @@ iavf_arq_fill(struct iavf_softc *sc, int post)
 
 	while (n > 0) {
 		aqb = SIMPLEQ_FIRST(&sc->sc_arq_idle);
-		if (aqb != NULL)
-			SIMPLEQ_REMOVE_HEAD(&sc->sc_arq_idle, aqb_entry);
-		else if ((aqb = iavf_aqb_alloc(sc)) == NULL)
+		if (aqb == NULL)
 			break;
+		SIMPLEQ_REMOVE_HEAD(&sc->sc_arq_idle, aqb_entry);
 
 		memset(aqb->aqb_data, 0, IAVF_AQ_BUFLEN);
 
@@ -3143,7 +3159,7 @@ iavf_arq_unfill(struct iavf_softc *sc)
 
 		bus_dmamap_sync(sc->sc_dmat, aqb->aqb_map, 0, IAVF_AQ_BUFLEN,
 		    BUS_DMASYNC_POSTREAD);
-		iavf_aqb_free(sc, aqb);
+		SIMPLEQ_INSERT_TAIL(&sc->sc_arq_idle, aqb, aqb_entry);
 		if_rxr_put(&sc->sc_arq_ring, 1);
 	}
 }
@@ -3167,7 +3183,6 @@ iavf_arq_wait(struct iavf_softc *sc, int msec)
 	cond_wait(&sc->sc_admin_cond, "iavfarq");
 	timeout_del(&sc->sc_admin_timeout);
 
-	iavf_arq_fill(sc, 1);
 	return sc->sc_admin_result;
 }
 
-- 
2.53.0


From 73761654ab07138a0bfe0af6e4be4de49f341dab Mon Sep 17 00:00:00 2001
From: Carsten Beckmann <cbeck@genua.de>
Date: Mon, 21 Sep 2026 11:49:48 +0200
Subject: [PATCH 5/8] iavf: fix allocations using WAITOK during attach

WAITOK is not allowed during attach and would panic, so switch to the
respective NOWAIT variants.
---
 sys/dev/pci/if_iavf.c | 35 +++++++++++++++++++----------------
 1 file changed, 19 insertions(+), 16 deletions(-)

diff --git a/sys/dev/pci/if_iavf.c b/sys/dev/pci/if_iavf.c
index 6c17b44057a..dced9e1269a 100644
--- a/sys/dev/pci/if_iavf.c
+++ b/sys/dev/pci/if_iavf.c
@@ -688,7 +688,7 @@ struct iavf_softc {
 #define delaymsec(_ms)	delay(1000 * (_ms))
 
 static int	iavf_dmamem_alloc(struct iavf_softc *, struct iavf_dmamem *,
-		    bus_size_t, u_int);
+		    bus_size_t, u_int, int);
 static void	iavf_dmamem_free(struct iavf_softc *, struct iavf_dmamem *);
 
 static struct iavf_aq_buf *
@@ -882,7 +882,7 @@ iavf_setup_interrupts(struct iavf_softc *sc, struct pci_attach_args *pa)
 	}
 
 	sc->sc_vectors = mallocarray(sizeof(*sc->sc_vectors), nqueues,
-	    M_DEVBUF, M_WAITOK|M_CANFAIL|M_ZERO);
+	    M_DEVBUF, M_NOWAIT|M_CANFAIL|M_ZERO);
 	if (sc->sc_vectors == NULL) {
 		printf("%s: unable to allocate vectors\n", DEVNAME(sc));
 		return -1;
@@ -983,7 +983,7 @@ iavf_attach(struct device *parent, struct device *self, void *aux)
 	mtx_init(&sc->sc_atq_mtx, IPL_NET);
 
 	if (iavf_dmamem_alloc(sc, &sc->sc_atq,
-	    sizeof(struct iavf_aq_desc) * IAVF_AQ_NUM, IAVF_AQ_ALIGN) != 0) {
+	    sizeof(struct iavf_aq_desc) * IAVF_AQ_NUM, IAVF_AQ_ALIGN, 0) != 0) {
 		printf("\n" "%s: unable to allocate atq\n", DEVNAME(sc));
 		goto unmap;
 	}
@@ -995,7 +995,7 @@ iavf_attach(struct device *parent, struct device *self, void *aux)
 	sc->sc_arq_prod = 0;
 
 	if (iavf_dmamem_alloc(sc, &sc->sc_arq,
-	    sizeof(struct iavf_aq_desc) * IAVF_AQ_NUM, IAVF_AQ_ALIGN) != 0) {
+	    sizeof(struct iavf_aq_desc) * IAVF_AQ_NUM, IAVF_AQ_ALIGN, 0) != 0) {
 		printf("\n" "%s: unable to allocate arq\n", DEVNAME(sc));
 		goto free_atq;
 	}
@@ -1017,7 +1017,8 @@ iavf_attach(struct device *parent, struct device *self, void *aux)
 	}
 	timeout_set(&sc->sc_admin_timeout, iavf_arq_timeout, sc);
 
-	if (iavf_dmamem_alloc(sc, &sc->sc_scratch, PAGE_SIZE, IAVF_AQ_ALIGN) != 0) {
+	if (iavf_dmamem_alloc(sc, &sc->sc_scratch,
+	    PAGE_SIZE, IAVF_AQ_ALIGN, 0) != 0) {
 		printf("\n" "%s: unable to allocate scratch\n", DEVNAME(sc));
 		goto shutdown;
 	}
@@ -1869,7 +1870,7 @@ iavf_txr_alloc(struct iavf_softc *sc, unsigned int qid)
 
 	if (iavf_dmamem_alloc(sc, &txr->txr_mem,
 	    sizeof(struct iavf_tx_desc) * sc->sc_tx_ring_ndescs,
-	    IAVF_TX_QUEUE_ALIGN) != 0)
+	    IAVF_TX_QUEUE_ALIGN, 1) != 0)
 		goto freemap;
 
 	for (i = 0; i < sc->sc_tx_ring_ndescs; i++) {
@@ -2240,7 +2241,7 @@ iavf_rxr_alloc(struct iavf_softc *sc, unsigned int qid)
 
 	if (iavf_dmamem_alloc(sc, &rxr->rxr_mem,
 	    sizeof(struct iavf_rx_rd_desc_32) * sc->sc_rx_ring_ndescs,
-	    IAVF_RX_QUEUE_ALIGN) != 0)
+	    IAVF_RX_QUEUE_ALIGN, 1) != 0)
 		goto freemap;
 
 	for (i = 0; i < sc->sc_rx_ring_ndescs; i++) {
@@ -3056,22 +3057,22 @@ iavf_aqb_alloc(struct iavf_softc *sc)
 {
 	struct iavf_aq_buf *aqb;
 
-	aqb = malloc(sizeof(*aqb), M_DEVBUF, M_WAITOK);
+	aqb = malloc(sizeof(*aqb), M_DEVBUF, M_NOWAIT);
 	if (aqb == NULL)
 		return (NULL);
 
-	aqb->aqb_data = dma_alloc(IAVF_AQ_BUFLEN, PR_WAITOK);
+	aqb->aqb_data = dma_alloc(IAVF_AQ_BUFLEN, PR_NOWAIT);
 	if (aqb->aqb_data == NULL)
 		goto free;
 
 	if (bus_dmamap_create(sc->sc_dmat, IAVF_AQ_BUFLEN, 1,
 	    IAVF_AQ_BUFLEN, 0,
-	    BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW | BUS_DMA_64BIT,
+	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW | BUS_DMA_64BIT,
 	    &aqb->aqb_map) != 0)
 		goto dma_free;
 
 	if (bus_dmamap_load(sc->sc_dmat, aqb->aqb_map, aqb->aqb_data,
-	    IAVF_AQ_BUFLEN, NULL, BUS_DMA_WAITOK) != 0)
+	    IAVF_AQ_BUFLEN, NULL, BUS_DMA_NOWAIT) != 0)
 		goto destroy;
 
 	return (aqb);
@@ -3188,24 +3189,26 @@ iavf_arq_wait(struct iavf_softc *sc, int msec)
 
 static int
 iavf_dmamem_alloc(struct iavf_softc *sc, struct iavf_dmamem *ixm,
-    bus_size_t size, u_int align)
+    bus_size_t size, u_int align, int waitok)
 {
+	int wait_flag = waitok ? BUS_DMA_WAITOK : BUS_DMA_NOWAIT;
+
 	ixm->ixm_size = size;
 
 	if (bus_dmamap_create(sc->sc_dmat, ixm->ixm_size, 1,
 	    ixm->ixm_size, 0,
-	    BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW | BUS_DMA_64BIT,
+	    wait_flag | BUS_DMA_ALLOCNOW | BUS_DMA_64BIT,
 	    &ixm->ixm_map) != 0)
 		return (1);
 	if (bus_dmamem_alloc(sc->sc_dmat, ixm->ixm_size,
 	    align, 0, &ixm->ixm_seg, 1, &ixm->ixm_nsegs,
-	    BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_64BIT) != 0)
+	    wait_flag | BUS_DMA_ZERO | BUS_DMA_64BIT) != 0)
 		goto destroy;
 	if (bus_dmamem_map(sc->sc_dmat, &ixm->ixm_seg, ixm->ixm_nsegs,
-	    ixm->ixm_size, &ixm->ixm_kva, BUS_DMA_WAITOK) != 0)
+	    ixm->ixm_size, &ixm->ixm_kva, wait_flag) != 0)
 		goto free;
 	if (bus_dmamap_load(sc->sc_dmat, ixm->ixm_map, ixm->ixm_kva,
-	    ixm->ixm_size, NULL, BUS_DMA_WAITOK) != 0)
+	    ixm->ixm_size, NULL, wait_flag) != 0)
 		goto unmap;
 
 	return (0);
-- 
2.53.0


From d5a62c4abad4087b67276c50a066c38a21a32a98 Mon Sep 17 00:00:00 2001
From: Carsten Beckmann <cbeck@genua.de>
Date: Thu, 1 Oct 2026 14:41:59 +0200
Subject: [PATCH 6/8] iavf: clear stale data in scratch buffer

All messages in the admin transmit queue reuse the same scratch
buffer. For some messages not all fields were initialized which means
that they could contain stale data from older messages. In some cases
this lead to configuration errors. Explicitly initialize all fields for
messages were this could happen to fix this problem.
---
 sys/dev/pci/if_iavf.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/sys/dev/pci/if_iavf.c b/sys/dev/pci/if_iavf.c
index dced9e1269a..c484cc68c61 100644
--- a/sys/dev/pci/if_iavf.c
+++ b/sys/dev/pci/if_iavf.c
@@ -1278,6 +1278,8 @@ iavf_config_vsi_queues(struct iavf_softc *sc)
 	iavf_aq_dva(&iaq, IAVF_DMA_DVA(&sc->sc_scratch));
 
 	config = IAVF_DMA_KVA(&sc->sc_scratch);
+	memset(config, 0, sizeof(*config) +
+	    iavf_nqueues(sc) * sizeof(struct iavf_vc_queue_pair_info));
 	config->vsi_id = htole16(sc->sc_vsi_id);
 	config->num_queue_pairs = htole16(iavf_nqueues(sc));
 
@@ -1427,6 +1429,7 @@ iavf_queue_select(struct iavf_softc *sc, int opcode)
 
 	qsel = IAVF_DMA_KVA(&sc->sc_scratch);
 	qsel->vsi_id = htole16(sc->sc_vsi_id);
+	qsel->pad = 0;
 	qsel->rx_queues = htole32(iavf_allqueues(sc));
 	qsel->tx_queues = htole32(iavf_allqueues(sc));
 
@@ -1594,6 +1597,7 @@ iavf_add_del_addr(struct iavf_softc *sc, uint8_t *addr, int add)
 	iavf_aq_dva(&iaq, IAVF_DMA_DVA(&sc->sc_scratch));
 
 	addrs = IAVF_DMA_KVA(&sc->sc_scratch);
+	memset(addrs, 0, sizeof(*addrs) + sizeof(*vcaddr));
 	addrs->vsi_id = htole16(sc->sc_vsi_id);
 	addrs->num_elements = htole16(1);
 
@@ -3007,6 +3011,7 @@ iavf_config_irq_map(struct iavf_softc *sc)
 	iavf_aq_dva(&iaq, IAVF_DMA_DVA(&sc->sc_scratch));
 
 	map = IAVF_DMA_KVA(&sc->sc_scratch);
+	memset(map, 0, sizeof(*map) + sizeof(*vec) * sc->sc_nintrs);
 
 	vec = map->vecmap;
 	if (sc->sc_nintrs == 1) {
-- 
2.53.0


From f82ed0d5e131a36c33e5f14a000b6ff651b572a5 Mon Sep 17 00:00:00 2001
From: Carsten Beckmann <cbeck@genua.de>
Date: Wed, 23 Sep 2026 15:45:23 +0200
Subject: [PATCH 7/8] iavf: rework admin queue interrupt

Currently the admin queue interrupt detects VF resets by checking if the
ICR register contains 0xdeadbeef. This works for NICs of the Intel 700
series, but for an Intel E810-C (and presumably all other NICs of the
800 series) this register always contains 0xdeadbeef even during normal
operation, so it always triggers resets. Use one of the admin queue
registers instead which should work for both the 700 and 800 series.

This means we also can't check the bits of the ICR register, though this
should not be a problem. Both iavf_atq_done and iavf_process_arq can
just always be called. I am assuming the iavf_rxeof and iavf_txeof parts
are a leftover from the driver being based on ixl. The Linux sources
indicate that vector 0 can not be used for traffic, so we can just
remove this part.
---
 sys/dev/pci/if_iavf.c | 23 ++++++-----------------
 1 file changed, 6 insertions(+), 17 deletions(-)

diff --git a/sys/dev/pci/if_iavf.c b/sys/dev/pci/if_iavf.c
index c484cc68c61..4e1a1baef9e 100644
--- a/sys/dev/pci/if_iavf.c
+++ b/sys/dev/pci/if_iavf.c
@@ -2574,35 +2574,24 @@ static int
 iavf_intr(void *xsc)
 {
 	struct iavf_softc *sc = xsc;
-	struct ifnet *ifp = &sc->sc_ac.ac_if;
-	uint32_t icr, ena;
-	int i, rv = 0;
+	uint32_t icr, ena, arq_len;
 
 	ena = iavf_rd(sc, I40E_VFINT_ICR0_ENA1);
 	iavf_intr_enable(sc);
 	icr = iavf_rd(sc, I40E_VFINT_ICR01);
+	arq_len = iavf_rd(sc, sc->sc_aq_regs->arq_len);
 
-	if (icr == IAVF_REG_VFR) {
+	if (arq_len == IAVF_REG_VFR) {
 		printf("%s: VF reset in progress\n", DEVNAME(sc));
 		sc->sc_resetting = 1;
 		task_add(systq, &sc->sc_reset_task);
 		return (1);
 	}
 
-	if (ISSET(icr, I40E_VFINT_ICR01_ADMINQ_MASK)) {
-		iavf_atq_done(sc);
-		iavf_process_arq(sc);
-		rv = 1;
-	}
+	iavf_atq_done(sc);
+	iavf_process_arq(sc);
 
-	if (ISSET(icr, I40E_VFINT_ICR01_QUEUE_0_MASK)) {
-		for (i = 0; i < iavf_nqueues(sc); i++) {
-			rv |= iavf_rxeof(sc, ifp->if_iqs[i]);
-			rv |= iavf_txeof(sc, ifp->if_ifqs[i]);
-		}
-	}
-
-	return (rv);
+	return (1);
 }
 
 static void
-- 
2.53.0


From a6acd7f705605dbef55add5dc37367bff1fcefc9 Mon Sep 17 00:00:00 2001
From: Carsten Beckmann <cbeck@genua.de>
Date: Mon, 21 Sep 2026 16:50:44 +0200
Subject: [PATCH 8/8] iavf: rework RSS capability configuration

Currently the RSS capabilites are configured depending on the PCI ID of
the device. Only NICs of the 700 series are considered though, with an
Intel E810-C (and presumably other NICs of the 800 series)
iavf_config_hena fails. We could add another case for them and hardcode
their capabilities, which at least for the E810-C would be
ICE_DEFAULT_RSS_HENA. It seems better to just make it generic though, so
rework it to query the supported capabilities from the PF and then
configure them afterwards.
---
 sys/dev/pci/if_iavf.c | 43 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 39 insertions(+), 4 deletions(-)

diff --git a/sys/dev/pci/if_iavf.c b/sys/dev/pci/if_iavf.c
index 4e1a1baef9e..f2d7963062b 100644
--- a/sys/dev/pci/if_iavf.c
+++ b/sys/dev/pci/if_iavf.c
@@ -341,6 +341,10 @@ struct iavf_vc_rss_lut {
         uint8_t         lut[1];
 }__packed;
 
+struct iavf_vc_rss_hena_caps {
+	uint64_t	caps;
+}__packed;
+
 #define IAVF_RSS_VSI_LUT_ENTRY_MASK     0x3F
 
 /* aq response codes */
@@ -646,6 +650,7 @@ struct iavf_softc {
 	unsigned int		 sc_base_queue;
 	uint32_t		 sc_rss_key_size;
 	uint32_t		 sc_rss_lut_size;
+	uint64_t		 sc_rss_hena_caps;
 
 	struct cond		 sc_admin_cond;
 	int			 sc_admin_result;
@@ -1320,6 +1325,27 @@ iavf_config_vsi_queues(struct iavf_softc *sc)
 	return (0);
 }
 
+static int
+iavf_config_get_rss_hena_caps(struct iavf_softc *sc)
+{
+	struct iavf_aq_desc iaq;
+	int rv;
+
+	memset(&iaq, 0, sizeof(iaq));
+	iaq.iaq_opcode = htole16(IAVF_AQ_OP_SEND_TO_PF);
+	iaq.iaq_vc_opcode = htole32(IAVF_VC_OP_GET_RSS_HENA_CAPS);
+	iavf_aq_dva(&iaq, IAVF_DMA_DVA(&sc->sc_scratch));
+
+	iavf_atq_post(sc, &iaq);
+	rv = iavf_arq_wait(sc, IAVF_EXEC_TIMEOUT);
+	if (rv != IAVF_VC_RC_SUCCESS) {
+		printf("%s: GET_RSS_HENA_CAPS failed: %d\n", DEVNAME(sc), rv);
+		return (1);
+	}
+
+	return (0);
+}
+
 static int
 iavf_config_hena(struct iavf_softc *sc)
 {
@@ -1335,8 +1361,7 @@ iavf_config_hena(struct iavf_softc *sc)
 	iavf_aq_dva(&iaq, IAVF_DMA_DVA(&sc->sc_scratch));
 
 	caps = IAVF_DMA_KVA(&sc->sc_scratch);
-	*caps = (sc->sc_mac_type == I40E_MAC_X722_VF) ? IXL_RSS_HENA_BASE_722 :
-		IXL_RSS_HENA_BASE_710;
+	*caps = sc->sc_rss_hena_caps;
 
 	iavf_atq_post(sc, &iaq);
 	rv = iavf_arq_wait(sc, IAVF_EXEC_TIMEOUT);
@@ -1488,7 +1513,8 @@ iavf_up(struct iavf_softc *sc)
 	if (iavf_config_vsi_queues(sc) != 0)
 		goto down;
 
-	if (iavf_config_hena(sc) != 0 ||
+	if (iavf_config_get_rss_hena_caps(sc) != 0 ||
+	    iavf_config_hena(sc) != 0 ||
 	    iavf_config_rss_key(sc) != 0 ||
 	    iavf_config_rss_lut(sc) != 0)
 		goto down;
@@ -2731,6 +2757,7 @@ iavf_process_arq(struct iavf_softc *sc)
 	struct iavf_aq_desc *arq, *iaq;
 	struct iavf_aq_buf *aqb;
 	struct iavf_vc_version_info *ver;
+	struct iavf_vc_rss_hena_caps *caps;
 	unsigned int cons = sc->sc_arq_cons;
 	unsigned int prod;
 	int done = 0;
@@ -2782,12 +2809,20 @@ iavf_process_arq(struct iavf_softc *sc)
 			iavf_process_irq_map(sc, iaq);
 			break;
 
+		case IAVF_VC_OP_GET_RSS_HENA_CAPS:
+			sc->sc_admin_result = letoh32(iaq->iaq_vc_retval);
+			if (sc->sc_admin_result == IAVF_VC_RC_SUCCESS) {
+				caps = aqb->aqb_data;
+				sc->sc_rss_hena_caps = caps->caps;
+			}
+			cond_signal(&sc->sc_admin_cond);
+			break;
+
 		case IAVF_VC_OP_CONFIG_TX_QUEUE:
 		case IAVF_VC_OP_CONFIG_RX_QUEUE:
 		case IAVF_VC_OP_CONFIG_VSI_QUEUES:
 		case IAVF_VC_OP_ENABLE_QUEUES:
 		case IAVF_VC_OP_DISABLE_QUEUES:
-		case IAVF_VC_OP_GET_RSS_HENA_CAPS:
 		case IAVF_VC_OP_SET_RSS_HENA:
 		case IAVF_VC_OP_ADD_ETH_ADDR:
 		case IAVF_VC_OP_DEL_ETH_ADDR:
-- 
2.53.0

From 1268811f218612658b21006e0e6cc25c569433f5 Mon Sep 17 00:00:00 2001
From: Carsten Beckmann <cbeck@genua.de>
Date: Wed, 16 Sep 2026 14:49:58 +0200
Subject: [PATCH 1/8] iavf: reset atq indices on VF reset

On a VF reset the hardware registers atq_head and atq_tail are reset to
0, but sc_atq_cons and sc_atq_prod keep their old value. If something is
posted to the admin queue the new descriptor is put at whatever position
the driver left off before the reset and atq_tail is updated to that
value. To the PF on the host that looks like the VF just resend all the
previous descriptors which is obviously wrong. Resetting sc_atq_cons and
sc_atq_prod on a VF reset fixes this issue.

While there also initialize atq_head_mask using the correct define. Both
defines have the same value, so this is no functional change.
---
 sys/dev/pci/if_iavf.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/sys/dev/pci/if_iavf.c b/sys/dev/pci/if_iavf.c
index b6e7f681993..e41396e7873 100644
--- a/sys/dev/pci/if_iavf.c
+++ b/sys/dev/pci/if_iavf.c
@@ -758,7 +758,7 @@ static const struct iavf_aq_regs iavf_aq_regs = {
 	.atq_tail	= I40E_VF_ATQT1,
 	.atq_tail_mask	= I40E_VF_ATQT1_ATQT_MASK,
 	.atq_head	= I40E_VF_ATQH1,
-	.atq_head_mask	= I40E_VF_ARQH1_ARQH_MASK,
+	.atq_head_mask	= I40E_VF_ATQH1_ATQH_MASK,
 	.atq_len	= I40E_VF_ATQLEN1,
 	.atq_bal	= I40E_VF_ATQBAL1,
 	.atq_bah	= I40E_VF_ATQBAH1,
@@ -1746,6 +1746,11 @@ iavf_reset(void *xsc)
 		goto failed;
 	}
 
+	mtx_enter(&sc->sc_atq_mtx);
+	sc->sc_atq_cons = 0;
+	sc->sc_atq_prod = 0;
+	mtx_leave(&sc->sc_atq_mtx);
+
 	iavf_arq_unfill(sc);
 	sc->sc_arq_cons = 0;
 	sc->sc_arq_prod = 0;
-- 
2.53.0

From 713dd6f44291f078cb249b1a8deb1b5c02c2cd70 Mon Sep 17 00:00:00 2001
From: Carsten Beckmann <cbeck@genua.de>
Date: Wed, 16 Sep 2026 14:54:10 +0200
Subject: [PATCH 2/8] iavf: improve VF reset handling

Currently VF resets are detected by checking if a register contains
0xdeadbeef in the admin queue interrupt. The interrupt is triggered
because the host driver sends the VF a RESET_IMPENDING event before
actually triggering the VF reset. If the interrupt is processed too
early or too late the temporary 0xdeadbeef state and therefore the VF
reset can be missed. Triggering a reset when a RESET_IMPENDING event
arrives fixes this issue.

In theory this could now allow the reset task to run before the actual
VF reset is triggered by the host. To prevent this the reset task now
waits until the arq_len_enable bit goes low which would indicate a VF
reset. The same approach is used by the Linux iavf driver.
---
 sys/dev/pci/if_iavf.c | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/sys/dev/pci/if_iavf.c b/sys/dev/pci/if_iavf.c
index e41396e7873..5a15f80db48 100644
--- a/sys/dev/pci/if_iavf.c
+++ b/sys/dev/pci/if_iavf.c
@@ -1708,6 +1708,7 @@ iavf_reset(void *xsc)
 	struct iavf_softc *sc = xsc;
 	struct ifnet *ifp = &sc->sc_ac.ac_if;
 	int tries, up, link_state;
+	uint32_t reg;
 
 	NET_LOCK();
 
@@ -1732,7 +1733,20 @@ iavf_reset(void *xsc)
 	sc->sc_got_irq_map = 0;
 
 	for (tries = 0; tries < 100; tries++) {
-		uint32_t reg;
+		reg = iavf_rd(sc, sc->sc_aq_regs->arq_len) &
+		    sc->sc_aq_regs->arq_len_enable;
+		if (!reg)
+			break;
+
+		delay(10000);
+	}
+
+	if (tries == 100) {
+		printf("%s: did not detect VF reset\n", DEVNAME(sc));
+		goto failed;
+	}
+
+	for (tries = 0; tries < 100; tries++) {
 		reg = iavf_rd(sc, I40E_VFGEN_RSTAT) &
 		    I40E_VFGEN_RSTAT_VFR_STATE_MASK;
 		if (reg == IAVF_VFR_VFACTIVE ||
@@ -2654,7 +2668,11 @@ iavf_process_vc_event(struct iavf_softc *sc, struct iavf_aq_desc *desc,
 				if_link_state_change(ifp);
 		}
 		break;
-
+	case IAVF_VC_EVENT_RESET_IMPENDING:
+		printf("%s: VF reset impending\n", DEVNAME(sc));
+		sc->sc_resetting = 1;
+		task_add(systq, &sc->sc_reset_task);
+		break;
 	default:
 		break;
 	}
-- 
2.53.0

From b02aaccc6beb72654c16307217843662ce916eea Mon Sep 17 00:00:00 2001
From: Carsten Beckmann <cbeck@genua.de>
Date: Mon, 21 Sep 2026 10:48:32 +0200
Subject: [PATCH 3/8] iavf: prevent panic in iavf_process_arq during reset

There is a small window during a VF reset where the ICR register in
iavf_intr still contains its normal value but when arq_head is read in
iavf_process_arq it returns 0xdeadbeef. Using this for prod (even
masked) will lead to a panic, so check for 0xdeadbeef first and trigger
a reset if it is detected.
---
 sys/dev/pci/if_iavf.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/sys/dev/pci/if_iavf.c b/sys/dev/pci/if_iavf.c
index 5a15f80db48..6e7921e22e1 100644
--- a/sys/dev/pci/if_iavf.c
+++ b/sys/dev/pci/if_iavf.c
@@ -2723,8 +2723,16 @@ iavf_process_arq(struct iavf_softc *sc, int fill)
 	unsigned int prod;
 	int done = 0;
 
-	prod = iavf_rd(sc, sc->sc_aq_regs->arq_head) &
-	    sc->sc_aq_regs->arq_head_mask;
+	prod = iavf_rd(sc, sc->sc_aq_regs->arq_head);
+
+	if (prod == IAVF_REG_VFR) {
+		printf("%s: VF reset in progress\n", DEVNAME(sc));
+		sc->sc_resetting = 1;
+		task_add(systq, &sc->sc_reset_task);
+		return (1);
+	}
+
+	prod &= sc->sc_aq_regs->arq_head_mask;
 
 	if (cons == prod)
 		return (0);
-- 
2.53.0

From cc10c1d7095f4a3b3c104559aff55883be1cb30a Mon Sep 17 00:00:00 2001
From: Carsten Beckmann <cbeck@genua.de>
Date: Thu, 17 Sep 2026 15:13:33 +0200
Subject: [PATCH 4/8] iavf: rework arq refilling

Currently iavf_arq_fill is not called directly when admin queue
responses are processed in the interrupt. This is fine when it actually
is a response to some command that the driver issued, then the command
issuer calls iavf_arq_fill. Some messages are sent to the VF unsolicited
though, for example LINK_CHANGE events. Since the admin response queue
is never refilled in this case only a few of those messages can be
proccessed and all further messages will then be lost.

iavf_arq_fill can not be called from an interrupt because iavf_aqb_alloc
is not safe in that context. The simplest solution to that is to just
preallocate all buffers during attach. Since iavf_arq_fill no longer
needs to allocate anything it can now always be called which fixes
messages being lost. To not waste too much memory with this approach
also reduce the amount of descriptors in the admin queue to 32.
---
 sys/dev/pci/if_iavf.c | 45 ++++++++++++++++++++++++++++---------------
 1 file changed, 30 insertions(+), 15 deletions(-)

diff --git a/sys/dev/pci/if_iavf.c b/sys/dev/pci/if_iavf.c
index 6e7921e22e1..6c17b44057a 100644
--- a/sys/dev/pci/if_iavf.c
+++ b/sys/dev/pci/if_iavf.c
@@ -507,7 +507,7 @@ struct iavf_rx_wb_desc_32 {
 #define IAVF_ITR2			0x2
 #define IAVF_NOITR			0x3
 
-#define IAVF_AQ_NUM			256
+#define IAVF_AQ_NUM			32
 #define IAVF_AQ_MASK			(IAVF_AQ_NUM - 1)
 #define IAVF_AQ_ALIGN			64 /* lol */
 #define IAVF_AQ_BUFLEN			4096
@@ -691,6 +691,9 @@ static int	iavf_dmamem_alloc(struct iavf_softc *, struct iavf_dmamem *,
 		    bus_size_t, u_int);
 static void	iavf_dmamem_free(struct iavf_softc *, struct iavf_dmamem *);
 
+static struct iavf_aq_buf *
+		iavf_aqb_alloc(struct iavf_softc *);
+static void	iavf_aqb_free(struct iavf_softc *, struct iavf_aq_buf *);
 static int	iavf_arq_fill(struct iavf_softc *, int);
 static void	iavf_arq_unfill(struct iavf_softc *);
 static void	iavf_arq_timeout(void *);
@@ -709,7 +712,7 @@ static int	iavf_config_rss_lut(struct iavf_softc *);
 static int	iavf_config_irq_map(struct iavf_softc *);
 
 static int	iavf_add_del_addr(struct iavf_softc *, uint8_t *, int);
-static int	iavf_process_arq(struct iavf_softc *, int);
+static int	iavf_process_arq(struct iavf_softc *);
 
 static int	iavf_match(struct device *, void *, void *);
 static int	iavf_setup_interrupts(struct iavf_softc *, struct pci_attach_args *);
@@ -936,8 +939,9 @@ iavf_attach(struct device *parent, struct device *self, void *aux)
 	struct iavf_softc *sc = (struct iavf_softc *)self;
 	struct ifnet *ifp = &sc->sc_ac.ac_if;
 	struct pci_attach_args *pa = aux;
+	struct iavf_aq_buf *aqb;
 	pcireg_t memtype;
-	int nmsix, tries;
+	int nmsix, tries, i;
 	unsigned int nqueues;
 
 	rw_init(&sc->sc_cfg_lock, "iavfcfg");
@@ -996,6 +1000,16 @@ iavf_attach(struct device *parent, struct device *self, void *aux)
 		goto free_atq;
 	}
 
+	for (i = 0; i < IAVF_AQ_NUM - 1; i++) {
+		aqb = iavf_aqb_alloc(sc);
+		if (aqb == NULL) {
+			printf("\n" "%s: unable to allocate aqb\n",
+			    DEVNAME(sc));
+			goto free_arq;
+		}
+		SIMPLEQ_INSERT_TAIL(&sc->sc_arq_idle, aqb, aqb_entry);
+	}
+
 	if (!iavf_arq_fill(sc, 0)) {
 		printf("\n" "%s: unable to fill arq descriptors\n",
 		    DEVNAME(sc));
@@ -1130,6 +1144,10 @@ shutdown:
 
 	iavf_arq_unfill(sc);
 free_arq:
+	while ((aqb = SIMPLEQ_FIRST(&sc->sc_arq_idle)) != NULL) {
+		SIMPLEQ_REMOVE_HEAD(&sc->sc_arq_idle, aqb_entry);
+		iavf_aqb_free(sc, aqb);
+	}
 	iavf_dmamem_free(sc, &sc->sc_arq);
 free_atq:
 	iavf_dmamem_free(sc, &sc->sc_atq);
@@ -2568,7 +2586,7 @@ iavf_intr(void *xsc)
 
 	if (ISSET(icr, I40E_VFINT_ICR01_ADMINQ_MASK)) {
 		iavf_atq_done(sc);
-		iavf_process_arq(sc, 0);
+		iavf_process_arq(sc);
 		rv = 1;
 	}
 
@@ -2714,7 +2732,7 @@ iavf_init_admin_queue(struct iavf_softc *sc)
 }
 
 static int
-iavf_process_arq(struct iavf_softc *sc, int fill)
+iavf_process_arq(struct iavf_softc *sc)
 {
 	struct iavf_aq_desc *arq, *iaq;
 	struct iavf_aq_buf *aqb;
@@ -2797,8 +2815,7 @@ iavf_process_arq(struct iavf_softc *sc, int fill)
 		done = 1;
 	} while (cons != prod);
 
-	if (fill)
-		iavf_arq_fill(sc, 1);
+	iavf_arq_fill(sc, 1);
 
 	bus_dmamap_sync(sc->sc_dmat, IAVF_DMA_MAP(&sc->sc_arq),
 	    0, IAVF_DMA_LEN(&sc->sc_arq),
@@ -2909,7 +2926,7 @@ iavf_get_version(struct iavf_softc *sc)
 	iavf_atq_post(sc, &iaq);
 
 	for (tries = 0; tries < 100; tries++) {
-		iavf_process_arq(sc, 1);
+		iavf_process_arq(sc);
 		if (sc->sc_major_ver != -1)
 			break;
 
@@ -2961,7 +2978,7 @@ iavf_get_vf_resources(struct iavf_softc *sc)
 	iavf_atq_post(sc, &iaq);
 
 	for (tries = 0; tries < 100; tries++) {
-		iavf_process_arq(sc, 1);
+		iavf_process_arq(sc);
 		if (sc->sc_got_vf_resources != 0)
 			return (0);
 
@@ -3024,7 +3041,7 @@ iavf_config_irq_map(struct iavf_softc *sc)
 	iavf_atq_post(sc, &iaq);
 
 	for (tries = 0; tries < 100; tries++) {
-		iavf_process_arq(sc, 1);
+		iavf_process_arq(sc);
 		if (sc->sc_got_irq_map != 0)
 			return (0);
 
@@ -3092,10 +3109,9 @@ iavf_arq_fill(struct iavf_softc *sc, int post)
 
 	while (n > 0) {
 		aqb = SIMPLEQ_FIRST(&sc->sc_arq_idle);
-		if (aqb != NULL)
-			SIMPLEQ_REMOVE_HEAD(&sc->sc_arq_idle, aqb_entry);
-		else if ((aqb = iavf_aqb_alloc(sc)) == NULL)
+		if (aqb == NULL)
 			break;
+		SIMPLEQ_REMOVE_HEAD(&sc->sc_arq_idle, aqb_entry);
 
 		memset(aqb->aqb_data, 0, IAVF_AQ_BUFLEN);
 
@@ -3143,7 +3159,7 @@ iavf_arq_unfill(struct iavf_softc *sc)
 
 		bus_dmamap_sync(sc->sc_dmat, aqb->aqb_map, 0, IAVF_AQ_BUFLEN,
 		    BUS_DMASYNC_POSTREAD);
-		iavf_aqb_free(sc, aqb);
+		SIMPLEQ_INSERT_TAIL(&sc->sc_arq_idle, aqb, aqb_entry);
 		if_rxr_put(&sc->sc_arq_ring, 1);
 	}
 }
@@ -3167,7 +3183,6 @@ iavf_arq_wait(struct iavf_softc *sc, int msec)
 	cond_wait(&sc->sc_admin_cond, "iavfarq");
 	timeout_del(&sc->sc_admin_timeout);
 
-	iavf_arq_fill(sc, 1);
 	return sc->sc_admin_result;
 }
 
-- 
2.53.0

From 73761654ab07138a0bfe0af6e4be4de49f341dab Mon Sep 17 00:00:00 2001
From: Carsten Beckmann <cbeck@genua.de>
Date: Mon, 21 Sep 2026 11:49:48 +0200
Subject: [PATCH 5/8] iavf: fix allocations using WAITOK during attach

WAITOK is not allowed during attach and would panic, so switch to the
respective NOWAIT variants.
---
 sys/dev/pci/if_iavf.c | 35 +++++++++++++++++++----------------
 1 file changed, 19 insertions(+), 16 deletions(-)

diff --git a/sys/dev/pci/if_iavf.c b/sys/dev/pci/if_iavf.c
index 6c17b44057a..dced9e1269a 100644
--- a/sys/dev/pci/if_iavf.c
+++ b/sys/dev/pci/if_iavf.c
@@ -688,7 +688,7 @@ struct iavf_softc {
 #define delaymsec(_ms)	delay(1000 * (_ms))
 
 static int	iavf_dmamem_alloc(struct iavf_softc *, struct iavf_dmamem *,
-		    bus_size_t, u_int);
+		    bus_size_t, u_int, int);
 static void	iavf_dmamem_free(struct iavf_softc *, struct iavf_dmamem *);
 
 static struct iavf_aq_buf *
@@ -882,7 +882,7 @@ iavf_setup_interrupts(struct iavf_softc *sc, struct pci_attach_args *pa)
 	}
 
 	sc->sc_vectors = mallocarray(sizeof(*sc->sc_vectors), nqueues,
-	    M_DEVBUF, M_WAITOK|M_CANFAIL|M_ZERO);
+	    M_DEVBUF, M_NOWAIT|M_CANFAIL|M_ZERO);
 	if (sc->sc_vectors == NULL) {
 		printf("%s: unable to allocate vectors\n", DEVNAME(sc));
 		return -1;
@@ -983,7 +983,7 @@ iavf_attach(struct device *parent, struct device *self, void *aux)
 	mtx_init(&sc->sc_atq_mtx, IPL_NET);
 
 	if (iavf_dmamem_alloc(sc, &sc->sc_atq,
-	    sizeof(struct iavf_aq_desc) * IAVF_AQ_NUM, IAVF_AQ_ALIGN) != 0) {
+	    sizeof(struct iavf_aq_desc) * IAVF_AQ_NUM, IAVF_AQ_ALIGN, 0) != 0) {
 		printf("\n" "%s: unable to allocate atq\n", DEVNAME(sc));
 		goto unmap;
 	}
@@ -995,7 +995,7 @@ iavf_attach(struct device *parent, struct device *self, void *aux)
 	sc->sc_arq_prod = 0;
 
 	if (iavf_dmamem_alloc(sc, &sc->sc_arq,
-	    sizeof(struct iavf_aq_desc) * IAVF_AQ_NUM, IAVF_AQ_ALIGN) != 0) {
+	    sizeof(struct iavf_aq_desc) * IAVF_AQ_NUM, IAVF_AQ_ALIGN, 0) != 0) {
 		printf("\n" "%s: unable to allocate arq\n", DEVNAME(sc));
 		goto free_atq;
 	}
@@ -1017,7 +1017,8 @@ iavf_attach(struct device *parent, struct device *self, void *aux)
 	}
 	timeout_set(&sc->sc_admin_timeout, iavf_arq_timeout, sc);
 
-	if (iavf_dmamem_alloc(sc, &sc->sc_scratch, PAGE_SIZE, IAVF_AQ_ALIGN) != 0) {
+	if (iavf_dmamem_alloc(sc, &sc->sc_scratch,
+	    PAGE_SIZE, IAVF_AQ_ALIGN, 0) != 0) {
 		printf("\n" "%s: unable to allocate scratch\n", DEVNAME(sc));
 		goto shutdown;
 	}
@@ -1869,7 +1870,7 @@ iavf_txr_alloc(struct iavf_softc *sc, unsigned int qid)
 
 	if (iavf_dmamem_alloc(sc, &txr->txr_mem,
 	    sizeof(struct iavf_tx_desc) * sc->sc_tx_ring_ndescs,
-	    IAVF_TX_QUEUE_ALIGN) != 0)
+	    IAVF_TX_QUEUE_ALIGN, 1) != 0)
 		goto freemap;
 
 	for (i = 0; i < sc->sc_tx_ring_ndescs; i++) {
@@ -2240,7 +2241,7 @@ iavf_rxr_alloc(struct iavf_softc *sc, unsigned int qid)
 
 	if (iavf_dmamem_alloc(sc, &rxr->rxr_mem,
 	    sizeof(struct iavf_rx_rd_desc_32) * sc->sc_rx_ring_ndescs,
-	    IAVF_RX_QUEUE_ALIGN) != 0)
+	    IAVF_RX_QUEUE_ALIGN, 1) != 0)
 		goto freemap;
 
 	for (i = 0; i < sc->sc_rx_ring_ndescs; i++) {
@@ -3056,22 +3057,22 @@ iavf_aqb_alloc(struct iavf_softc *sc)
 {
 	struct iavf_aq_buf *aqb;
 
-	aqb = malloc(sizeof(*aqb), M_DEVBUF, M_WAITOK);
+	aqb = malloc(sizeof(*aqb), M_DEVBUF, M_NOWAIT);
 	if (aqb == NULL)
 		return (NULL);
 
-	aqb->aqb_data = dma_alloc(IAVF_AQ_BUFLEN, PR_WAITOK);
+	aqb->aqb_data = dma_alloc(IAVF_AQ_BUFLEN, PR_NOWAIT);
 	if (aqb->aqb_data == NULL)
 		goto free;
 
 	if (bus_dmamap_create(sc->sc_dmat, IAVF_AQ_BUFLEN, 1,
 	    IAVF_AQ_BUFLEN, 0,
-	    BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW | BUS_DMA_64BIT,
+	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW | BUS_DMA_64BIT,
 	    &aqb->aqb_map) != 0)
 		goto dma_free;
 
 	if (bus_dmamap_load(sc->sc_dmat, aqb->aqb_map, aqb->aqb_data,
-	    IAVF_AQ_BUFLEN, NULL, BUS_DMA_WAITOK) != 0)
+	    IAVF_AQ_BUFLEN, NULL, BUS_DMA_NOWAIT) != 0)
 		goto destroy;
 
 	return (aqb);
@@ -3188,24 +3189,26 @@ iavf_arq_wait(struct iavf_softc *sc, int msec)
 
 static int
 iavf_dmamem_alloc(struct iavf_softc *sc, struct iavf_dmamem *ixm,
-    bus_size_t size, u_int align)
+    bus_size_t size, u_int align, int waitok)
 {
+	int wait_flag = waitok ? BUS_DMA_WAITOK : BUS_DMA_NOWAIT;
+
 	ixm->ixm_size = size;
 
 	if (bus_dmamap_create(sc->sc_dmat, ixm->ixm_size, 1,
 	    ixm->ixm_size, 0,
-	    BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW | BUS_DMA_64BIT,
+	    wait_flag | BUS_DMA_ALLOCNOW | BUS_DMA_64BIT,
 	    &ixm->ixm_map) != 0)
 		return (1);
 	if (bus_dmamem_alloc(sc->sc_dmat, ixm->ixm_size,
 	    align, 0, &ixm->ixm_seg, 1, &ixm->ixm_nsegs,
-	    BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_64BIT) != 0)
+	    wait_flag | BUS_DMA_ZERO | BUS_DMA_64BIT) != 0)
 		goto destroy;
 	if (bus_dmamem_map(sc->sc_dmat, &ixm->ixm_seg, ixm->ixm_nsegs,
-	    ixm->ixm_size, &ixm->ixm_kva, BUS_DMA_WAITOK) != 0)
+	    ixm->ixm_size, &ixm->ixm_kva, wait_flag) != 0)
 		goto free;
 	if (bus_dmamap_load(sc->sc_dmat, ixm->ixm_map, ixm->ixm_kva,
-	    ixm->ixm_size, NULL, BUS_DMA_WAITOK) != 0)
+	    ixm->ixm_size, NULL, wait_flag) != 0)
 		goto unmap;
 
 	return (0);
-- 
2.53.0

From d5a62c4abad4087b67276c50a066c38a21a32a98 Mon Sep 17 00:00:00 2001
From: Carsten Beckmann <cbeck@genua.de>
Date: Thu, 1 Oct 2026 14:41:59 +0200
Subject: [PATCH 6/8] iavf: clear stale data in scratch buffer

All messages in the admin transmit queue reuse the same scratch
buffer. For some messages not all fields were initialized which means
that they could contain stale data from older messages. In some cases
this lead to configuration errors. Explicitly initialize all fields for
messages were this could happen to fix this problem.
---
 sys/dev/pci/if_iavf.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/sys/dev/pci/if_iavf.c b/sys/dev/pci/if_iavf.c
index dced9e1269a..c484cc68c61 100644
--- a/sys/dev/pci/if_iavf.c
+++ b/sys/dev/pci/if_iavf.c
@@ -1278,6 +1278,8 @@ iavf_config_vsi_queues(struct iavf_softc *sc)
 	iavf_aq_dva(&iaq, IAVF_DMA_DVA(&sc->sc_scratch));
 
 	config = IAVF_DMA_KVA(&sc->sc_scratch);
+	memset(config, 0, sizeof(*config) +
+	    iavf_nqueues(sc) * sizeof(struct iavf_vc_queue_pair_info));
 	config->vsi_id = htole16(sc->sc_vsi_id);
 	config->num_queue_pairs = htole16(iavf_nqueues(sc));
 
@@ -1427,6 +1429,7 @@ iavf_queue_select(struct iavf_softc *sc, int opcode)
 
 	qsel = IAVF_DMA_KVA(&sc->sc_scratch);
 	qsel->vsi_id = htole16(sc->sc_vsi_id);
+	qsel->pad = 0;
 	qsel->rx_queues = htole32(iavf_allqueues(sc));
 	qsel->tx_queues = htole32(iavf_allqueues(sc));
 
@@ -1594,6 +1597,7 @@ iavf_add_del_addr(struct iavf_softc *sc, uint8_t *addr, int add)
 	iavf_aq_dva(&iaq, IAVF_DMA_DVA(&sc->sc_scratch));
 
 	addrs = IAVF_DMA_KVA(&sc->sc_scratch);
+	memset(addrs, 0, sizeof(*addrs) + sizeof(*vcaddr));
 	addrs->vsi_id = htole16(sc->sc_vsi_id);
 	addrs->num_elements = htole16(1);
 
@@ -3007,6 +3011,7 @@ iavf_config_irq_map(struct iavf_softc *sc)
 	iavf_aq_dva(&iaq, IAVF_DMA_DVA(&sc->sc_scratch));
 
 	map = IAVF_DMA_KVA(&sc->sc_scratch);
+	memset(map, 0, sizeof(*map) + sizeof(*vec) * sc->sc_nintrs);
 
 	vec = map->vecmap;
 	if (sc->sc_nintrs == 1) {
-- 
2.53.0

From f82ed0d5e131a36c33e5f14a000b6ff651b572a5 Mon Sep 17 00:00:00 2001
From: Carsten Beckmann <cbeck@genua.de>
Date: Wed, 23 Sep 2026 15:45:23 +0200
Subject: [PATCH 7/8] iavf: rework admin queue interrupt

Currently the admin queue interrupt detects VF resets by checking if the
ICR register contains 0xdeadbeef. This works for NICs of the Intel 700
series, but for an Intel E810-C (and presumably all other NICs of the
800 series) this register always contains 0xdeadbeef even during normal
operation, so it always triggers resets. Use one of the admin queue
registers instead which should work for both the 700 and 800 series.

This means we also can't check the bits of the ICR register, though this
should not be a problem. Both iavf_atq_done and iavf_process_arq can
just always be called. I am assuming the iavf_rxeof and iavf_txeof parts
are a leftover from the driver being based on ixl. The Linux sources
indicate that vector 0 can not be used for traffic, so we can just
remove this part.
---
 sys/dev/pci/if_iavf.c | 23 ++++++-----------------
 1 file changed, 6 insertions(+), 17 deletions(-)

diff --git a/sys/dev/pci/if_iavf.c b/sys/dev/pci/if_iavf.c
index c484cc68c61..4e1a1baef9e 100644
--- a/sys/dev/pci/if_iavf.c
+++ b/sys/dev/pci/if_iavf.c
@@ -2574,35 +2574,24 @@ static int
 iavf_intr(void *xsc)
 {
 	struct iavf_softc *sc = xsc;
-	struct ifnet *ifp = &sc->sc_ac.ac_if;
-	uint32_t icr, ena;
-	int i, rv = 0;
+	uint32_t icr, ena, arq_len;
 
 	ena = iavf_rd(sc, I40E_VFINT_ICR0_ENA1);
 	iavf_intr_enable(sc);
 	icr = iavf_rd(sc, I40E_VFINT_ICR01);
+	arq_len = iavf_rd(sc, sc->sc_aq_regs->arq_len);
 
-	if (icr == IAVF_REG_VFR) {
+	if (arq_len == IAVF_REG_VFR) {
 		printf("%s: VF reset in progress\n", DEVNAME(sc));
 		sc->sc_resetting = 1;
 		task_add(systq, &sc->sc_reset_task);
 		return (1);
 	}
 
-	if (ISSET(icr, I40E_VFINT_ICR01_ADMINQ_MASK)) {
-		iavf_atq_done(sc);
-		iavf_process_arq(sc);
-		rv = 1;
-	}
+	iavf_atq_done(sc);
+	iavf_process_arq(sc);
 
-	if (ISSET(icr, I40E_VFINT_ICR01_QUEUE_0_MASK)) {
-		for (i = 0; i < iavf_nqueues(sc); i++) {
-			rv |= iavf_rxeof(sc, ifp->if_iqs[i]);
-			rv |= iavf_txeof(sc, ifp->if_ifqs[i]);
-		}
-	}
-
-	return (rv);
+	return (1);
 }
 
 static void
-- 
2.53.0

From a6acd7f705605dbef55add5dc37367bff1fcefc9 Mon Sep 17 00:00:00 2001
From: Carsten Beckmann <cbeck@genua.de>
Date: Mon, 21 Sep 2026 16:50:44 +0200
Subject: [PATCH 8/8] iavf: rework RSS capability configuration

Currently the RSS capabilites are configured depending on the PCI ID of
the device. Only NICs of the 700 series are considered though, with an
Intel E810-C (and presumably other NICs of the 800 series)
iavf_config_hena fails. We could add another case for them and hardcode
their capabilities, which at least for the E810-C would be
ICE_DEFAULT_RSS_HENA. It seems better to just make it generic though, so
rework it to query the supported capabilities from the PF and then
configure them afterwards.
---
 sys/dev/pci/if_iavf.c | 43 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 39 insertions(+), 4 deletions(-)

diff --git a/sys/dev/pci/if_iavf.c b/sys/dev/pci/if_iavf.c
index 4e1a1baef9e..f2d7963062b 100644
--- a/sys/dev/pci/if_iavf.c
+++ b/sys/dev/pci/if_iavf.c
@@ -341,6 +341,10 @@ struct iavf_vc_rss_lut {
         uint8_t         lut[1];
 }__packed;
 
+struct iavf_vc_rss_hena_caps {
+	uint64_t	caps;
+}__packed;
+
 #define IAVF_RSS_VSI_LUT_ENTRY_MASK     0x3F
 
 /* aq response codes */
@@ -646,6 +650,7 @@ struct iavf_softc {
 	unsigned int		 sc_base_queue;
 	uint32_t		 sc_rss_key_size;
 	uint32_t		 sc_rss_lut_size;
+	uint64_t		 sc_rss_hena_caps;
 
 	struct cond		 sc_admin_cond;
 	int			 sc_admin_result;
@@ -1320,6 +1325,27 @@ iavf_config_vsi_queues(struct iavf_softc *sc)
 	return (0);
 }
 
+static int
+iavf_config_get_rss_hena_caps(struct iavf_softc *sc)
+{
+	struct iavf_aq_desc iaq;
+	int rv;
+
+	memset(&iaq, 0, sizeof(iaq));
+	iaq.iaq_opcode = htole16(IAVF_AQ_OP_SEND_TO_PF);
+	iaq.iaq_vc_opcode = htole32(IAVF_VC_OP_GET_RSS_HENA_CAPS);
+	iavf_aq_dva(&iaq, IAVF_DMA_DVA(&sc->sc_scratch));
+
+	iavf_atq_post(sc, &iaq);
+	rv = iavf_arq_wait(sc, IAVF_EXEC_TIMEOUT);
+	if (rv != IAVF_VC_RC_SUCCESS) {
+		printf("%s: GET_RSS_HENA_CAPS failed: %d\n", DEVNAME(sc), rv);
+		return (1);
+	}
+
+	return (0);
+}
+
 static int
 iavf_config_hena(struct iavf_softc *sc)
 {
@@ -1335,8 +1361,7 @@ iavf_config_hena(struct iavf_softc *sc)
 	iavf_aq_dva(&iaq, IAVF_DMA_DVA(&sc->sc_scratch));
 
 	caps = IAVF_DMA_KVA(&sc->sc_scratch);
-	*caps = (sc->sc_mac_type == I40E_MAC_X722_VF) ? IXL_RSS_HENA_BASE_722 :
-		IXL_RSS_HENA_BASE_710;
+	*caps = sc->sc_rss_hena_caps;
 
 	iavf_atq_post(sc, &iaq);
 	rv = iavf_arq_wait(sc, IAVF_EXEC_TIMEOUT);
@@ -1488,7 +1513,8 @@ iavf_up(struct iavf_softc *sc)
 	if (iavf_config_vsi_queues(sc) != 0)
 		goto down;
 
-	if (iavf_config_hena(sc) != 0 ||
+	if (iavf_config_get_rss_hena_caps(sc) != 0 ||
+	    iavf_config_hena(sc) != 0 ||
 	    iavf_config_rss_key(sc) != 0 ||
 	    iavf_config_rss_lut(sc) != 0)
 		goto down;
@@ -2731,6 +2757,7 @@ iavf_process_arq(struct iavf_softc *sc)
 	struct iavf_aq_desc *arq, *iaq;
 	struct iavf_aq_buf *aqb;
 	struct iavf_vc_version_info *ver;
+	struct iavf_vc_rss_hena_caps *caps;
 	unsigned int cons = sc->sc_arq_cons;
 	unsigned int prod;
 	int done = 0;
@@ -2782,12 +2809,20 @@ iavf_process_arq(struct iavf_softc *sc)
 			iavf_process_irq_map(sc, iaq);
 			break;
 
+		case IAVF_VC_OP_GET_RSS_HENA_CAPS:
+			sc->sc_admin_result = letoh32(iaq->iaq_vc_retval);
+			if (sc->sc_admin_result == IAVF_VC_RC_SUCCESS) {
+				caps = aqb->aqb_data;
+				sc->sc_rss_hena_caps = caps->caps;
+			}
+			cond_signal(&sc->sc_admin_cond);
+			break;
+
 		case IAVF_VC_OP_CONFIG_TX_QUEUE:
 		case IAVF_VC_OP_CONFIG_RX_QUEUE:
 		case IAVF_VC_OP_CONFIG_VSI_QUEUES:
 		case IAVF_VC_OP_ENABLE_QUEUES:
 		case IAVF_VC_OP_DISABLE_QUEUES:
-		case IAVF_VC_OP_GET_RSS_HENA_CAPS:
 		case IAVF_VC_OP_SET_RSS_HENA:
 		case IAVF_VC_OP_ADD_ETH_ADDR:
 		case IAVF_VC_OP_DEL_ETH_ADDR:
-- 
2.53.0