Download raw body.
iavf: multiple bugfixes and support for Intel 800 series
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
>
iavf: multiple bugfixes and support for Intel 800 series