From: Jan Klemkow Subject: Re: iavf: multiple bugfixes and support for Intel 800 series To: Carsten Beckmann Cc: "tech@openbsd.org" Date: Thu, 8 Oct 2026 09:26:29 +0200 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 > 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 > 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 > 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 > 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 > 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 > 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 > 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 > 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 >