Index | Thread | Search

From:
Jan Klemkow <jan@openbsd.org>
Subject:
Re: iavf: multiple bugfixes and support for Intel 800 series
To:
Carsten Beckmann <carsten_beckmann@genua.de>
Cc:
"tech@openbsd.org" <tech@openbsd.org>
Date:
Thu, 8 Oct 2026 09:26:29 +0200

Download raw body.

Thread
On Wed, Oct 07, 2026 at 02:10:09PM +0000, Carsten Beckmann wrote:
> 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.

I don't looked into the diff in detail yet.  But, I give it a try and
run it on my KVM setup with an Intel x710 network card.  It doen't break
the iavf(4) on x710 based interface.

Thanks,
jan

> 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
>