From nobody Thu Jul 30 03:09:53 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4h9Z1y6WMbz6mS84 for ; Thu, 30 Jul 2026 03:09:58 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4h9Z1y64M3z3K5T for ; Thu, 30 Jul 2026 03:09:58 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1785380998; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Vs22d4GfY94n3WaxTwIlBJTFwt1ByM0OXCgjnt99Lwo=; b=WW2uEPAp53dGqsuhPUvb0bj0mnEYVRDfc/VtlnR0SpOJfyiLVBwuG6qoOx9scFBDnKWbn0 7W4djdLIjTFTwANbfimBpchijmuSUMjZYgRKN916TDLQf5BHWTVFW557icb1SGE++eXlRr FOV1xHJOsx3ZXbJ5fRWolwcGKEO8y4WFtAvDawLRv1kUkm1O9IjM3BKI4cBr+ELUzN+bGL QoREIjXHEa4TOe3ynxFgilfnMYlSEm8iSk20lLIESbHGI5ynAVrgEkTBOo7xSVvVRdp1qb 6g+Vf/bnO9uLdOPzr5nS2wVgmt2ECnjJco6mcuCKDG99ZKOoDMj3aHiAiho1PQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1785380998; a=rsa-sha256; cv=none; b=kyz7qizxkm3M3zdI6f0FNQVAu3xhCnxX7ac808/IOQUF1n8f8BNGsbYTIv1UuUNdDhNDEK RgiXBTpocasjBlgeFMNJrLxY7yPEU0THn3J280+UW1T3gVUqfquQXlBcv4Vc8H2TOcPmBd Nwz3Im2lUGGbeLdMkCjvqpoShhIlKr0PN6Pm4Ya9Y86mwBEK8cFU6o+MxAuOO+W2ST27wr gfrV1mPnJAcE3I1Ew5yaJTkmpjRVXlXRC4OsJa6lyFsmpH1LTcgMuerDhKjVu9NB8nK4EY aeUmvVs16pZc4lBhbk8AmaiUXbOvBui27yOqX0VLBbtennLW+UFTEVdI3r214A== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1785380998; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Vs22d4GfY94n3WaxTwIlBJTFwt1ByM0OXCgjnt99Lwo=; b=n3dh8/sefb8EqyOrM+9W/ZvKlJgJmRfdvt7+d6g7Lz1oyN7jYy/1X1cQbvYvBn0caJRk7o ehS7cVX1/g6bu+/sa+IP4OCIiud/CKF8wbA6kbG25VqxiYGg1XRtj0cJ2g5QTxOHU482+e dZ2ouw3+dxIWjPPYlw7kCoT0lSC1M8agcUvjueqVPQ29w3prJjN1u2xUKfB99l5E3Sn+9G c/XrNioIRbiwhNTsbgjvRrwEMlAT3/hT783QnUyLf5zgk1xyWZCnzBxL++cRk3m2JpOpTF yIYX48y7GkH4ZntB99B1NqhRTtTIR9f+UsLrqVW5tOMr0VxxuLNOu99SqvU4WQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4h9Z1y4Z6NzvgX for ; Thu, 30 Jul 2026 03:09:58 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 1eb07 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Thu, 30 Jul 2026 03:09:53 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kevin Bowling Subject: git: a2ed165f0049 - main - igb: Add SR-IOV PF support for 82576 and I350 List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kbowling X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: a2ed165f0049595f2e52ec545095240e362f27c7 Auto-Submitted: auto-generated Date: Thu, 30 Jul 2026 03:09:53 +0000 Message-Id: <6a6ac081.1eb07.3f2a71e1@gitrepo.freebsd.org> The branch main has been updated by kbowling: URL: https://cgit.FreeBSD.org/src/commit/?id=a2ed165f0049595f2e52ec545095240e362f27c7 commit a2ed165f0049595f2e52ec545095240e362f27c7 Author: Kevin Bowling AuthorDate: 2026-07-28 21:56:33 +0000 Commit: Kevin Bowling CommitDate: 2026-07-30 03:06:46 +0000 igb: Add SR-IOV PF support for 82576 and I350 Add the PCI IOV schema and PF control plane for up to seven VFs with one hardware queue per pool. Implement VF mailbox handling, MAC and VLAN assignment, multicast filtering, promiscuity policy, anti-spoofing, malicious-driver recovery, reset replay, and queue lifecycle management. The basic SR-IOV and VMDq PF implementation follows DPDK Intel e1000 code, including PF pool selection, one queue per pool, mailbox dispatch, and VF enablement. Intel FreeBSD igb-2.5.31 supplies the older driver baseline. Linux igb and the Intel SDMs clear up lifecycle, isolation, reset, and family-specific details absent from DPDK. Enabling IOV requires the PF to attach with one TX and RX queue. Systems whose defaults select RSS queues must set the documented iflib queue override tunables before attach. Only 82576 and I350 support SR-IOV in silicon. The series has been extensively tested on I350, including thowing boundaries at the PCI BAR that shipping drivers will never. Still, think carefully before reaching for this in critical environments. Relnotes: yes Sponsored by: BBOX.io --- sys/conf/files | 2 + sys/dev/e1000/if_em.c | 378 +++++++-- sys/dev/e1000/if_em.h | 27 + sys/dev/e1000/if_igb_iov.c | 1819 ++++++++++++++++++++++++++++++++++++++++++++ sys/dev/e1000/if_igb_iov.h | 57 ++ sys/dev/e1000/igb_txrx.c | 25 +- sys/modules/em/Makefile | 4 +- 7 files changed, 2261 insertions(+), 51 deletions(-) diff --git a/sys/conf/files b/sys/conf/files index 57da06f27883..a408917484bd 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1638,6 +1638,8 @@ dev/efidev/efirt.c optional efirt dev/efidev/efirtc.c optional efirt efirtc dev/e1000/if_em.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" +dev/e1000/if_igb_iov.c optional em \ + compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/em_txrx.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/igb_txrx.c optional em \ diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c index 42e7113bff48..385406a12890 100644 --- a/sys/dev/e1000/if_em.c +++ b/sys/dev/e1000/if_em.c @@ -28,6 +28,7 @@ */ #include "if_em.h" +#include "if_igb_iov.h" #include #include @@ -404,6 +405,11 @@ static const pci_vendor_info_t igbv_vendor_info_array[] = { static void *em_register(device_t); static void *igb_register(device_t); static void *igbv_register(device_t); +static int igb_device_attach(device_t); +#ifdef PCI_IOV +static int igb_device_iov_init(device_t, uint16_t, const nvlist_t *); +static void igb_device_iov_uninit(device_t); +#endif static int em_if_attach_pre(if_ctx_t); static int em_if_attach_post(if_ctx_t); static int em_if_detach(if_ctx_t); @@ -455,6 +461,8 @@ static void em_update_vf_stats_counters(struct e1000_softc *); static void em_update_stats_counters(struct e1000_softc *); static void em_add_hw_stats(struct e1000_softc *); static int em_if_set_promisc(if_ctx_t, int); +static int em_if_set_promisc_impl(if_ctx_t, int); +static bool em_if_defer_promisc(struct e1000_softc *); static bool em_if_vlan_filter_capable(if_ctx_t); static bool em_if_vlan_filter_used(if_ctx_t); static void em_if_vlan_filter_enable(struct e1000_softc *); @@ -529,11 +537,16 @@ static device_method_t igb_methods[] = { /* Device interface */ DEVMETHOD(device_register, igb_register), DEVMETHOD(device_probe, iflib_device_probe), - DEVMETHOD(device_attach, iflib_device_attach), + DEVMETHOD(device_attach, igb_device_attach), DEVMETHOD(device_detach, iflib_device_detach), DEVMETHOD(device_shutdown, iflib_device_shutdown), DEVMETHOD(device_suspend, iflib_device_suspend), DEVMETHOD(device_resume, iflib_device_resume), +#ifdef PCI_IOV + DEVMETHOD(pci_iov_init, igb_device_iov_init), + DEVMETHOD(pci_iov_uninit, igb_device_iov_uninit), + DEVMETHOD(pci_iov_add_vf, iflib_device_iov_add_vf), +#endif DEVMETHOD_END }; @@ -655,6 +668,11 @@ static device_method_t igb_if_methods[] = { DEVMETHOD(ifdi_tx_queue_intr_enable, igb_if_tx_queue_intr_enable), DEVMETHOD(ifdi_debug, em_if_debug), DEVMETHOD(ifdi_needs_restart, em_if_needs_restart), +#ifdef PCI_IOV + DEVMETHOD(ifdi_iov_init, igb_if_iov_init), + DEVMETHOD(ifdi_iov_uninit, igb_if_iov_uninit), + DEVMETHOD(ifdi_iov_vf_add, igb_if_iov_vf_add), +#endif DEVMETHOD_END }; @@ -982,6 +1000,59 @@ igbv_register(device_t dev) return (&igbv_sctx_init); } +static int +igb_device_attach(device_t dev) +{ + struct e1000_softc *sc; + if_ctx_t ctx; + int error; + + error = iflib_device_attach(dev); + if (error != 0) + return (error); + + ctx = device_get_softc(dev); + sc = iflib_get_softc(ctx); + (void)igb_iov_attach(sc); + return (0); +} + +#ifdef PCI_IOV +static int +igb_device_iov_init(device_t dev, uint16_t num_vfs, + const nvlist_t *params) +{ + struct e1000_softc *sc; + if_ctx_t ctx; + int error; + + ctx = device_get_softc(dev); + sc = iflib_get_softc(ctx); + error = igb_iov_validate(sc, num_vfs); + if (error != 0) + return (error); + return (iflib_device_iov_init_restart(dev, num_vfs, params)); +} + +static void +igb_device_iov_uninit(device_t dev) +{ + struct e1000_softc *sc; + if_ctx_t ctx; + + ctx = device_get_softc(dev); + sc = iflib_get_softc(ctx); + /* + * pci_iov(4) has already detached the VF devices. Tell the stop + * half of iflib's restart transaction not to wait for acknowledgements + * from VFs which can no longer service their mailbox vectors. + */ + atomic_store_rel_32(&sc->iov_teardown, 1); + iflib_device_iov_uninit_restart(dev); +} + +#endif + static int em_set_num_queues(if_ctx_t ctx) { @@ -1513,6 +1584,7 @@ em_if_detach(if_ctx_t ctx) INIT_DEBUGOUT("em_if_detach: begin"); + igb_iov_detach(sc); e1000_phy_hw_reset(&sc->hw); em_release_manageability(sc); @@ -1601,7 +1673,7 @@ em_if_mtu_set(if_ctx_t ctx, uint32_t mtu) break; default: if (sc->hw.mac.type >= igb_mac_min) - max_frame_size = 9234; + max_frame_size = IGB_MAX_FRAME_SIZE; else /* lem */ max_frame_size = MAX_JUMBO_FRAME_SIZE; } @@ -1633,6 +1705,8 @@ em_if_init(if_ctx_t ctx) int i; INIT_DEBUGOUT("em_if_init: begin"); + if (sc->vf_ifp) + sc->vf_reset_pending = true; /* Get the latest mac address, User can use a LAA */ bcopy(if_getlladdr(ifp), sc->hw.mac.addr, ETHER_ADDR_LEN); @@ -1653,6 +1727,7 @@ em_if_init(if_ctx_t ctx) } /* Initialize the hardware */ + igb_iov_reset_prepare(sc); em_reset(ctx); /* Re-arm a link-up transition deferred for this reset. */ if (sc->link_state == EM_LINK_STATE_DOWN_RESET_PENDING || @@ -1697,7 +1772,11 @@ em_if_init(if_ctx_t ctx) em_setup_vlan_hw_support(ctx); /* Don't lose promiscuous settings */ - em_if_set_promisc(ctx, if_getflags(ifp)); + em_if_set_promisc_impl(ctx, if_getflags(ifp)); + atomic_readandclear_32(&sc->promisc_pending); + + /* Restore PF/VF pool configuration after the global reset. */ + igb_iov_initialize(sc); if (sc->hw.mac.ops.clear_hw_cntrs != NULL) sc->hw.mac.ops.clear_hw_cntrs(&sc->hw); @@ -1733,6 +1812,8 @@ em_if_init(if_ctx_t ctx) else e1000_set_eee_i350(&sc->hw, true, true); } + if (sc->vf_ifp) + sc->vf_reset_pending = false; } /* @@ -2039,17 +2120,43 @@ em_msix_link(void *arg) ++sc->link_irq; MPASS(sc->hw.back != NULL); + /* + * The VF's admin vector represents mailbox and link activity. It has + * no PF ICR at E1000_ICR, so process every admin-vector interrupt, + * matching the igbvf misc-vector model. + */ + if (sc->vf_ifp) { + sc->hw.mac.get_link_status = true; + iflib_admin_intr_deferred(sc->ctx); + E1000_WRITE_REG(&sc->hw, E1000_EIMS, sc->link_mask); + return (FILTER_HANDLED); + } + reg_icr = E1000_READ_REG(&sc->hw, E1000_ICR); + /* + * Enabling or disabling SR-IOV can briefly make PF MMIO reads return + * all ones. This is not an interrupt cause; in particular, do not + * turn it into a malicious-driver event. + */ + if (__predict_false(reg_icr == 0xffffffff)) + goto rearm; + if (reg_icr & E1000_ICR_RXO) sc->rx_overruns++; if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) em_handle_link(sc->ctx); + if (reg_icr & E1000_ICR_MDDET) + igb_iov_mdd_event(sc); + if (reg_icr & E1000_ICR_VMMB) + iflib_admin_intr_deferred(sc->ctx); +rearm: /* Re-arm unconditionally */ if (sc->hw.mac.type >= igb_mac_min) { - E1000_WRITE_REG(&sc->hw, E1000_IMS, E1000_IMS_LSC); + E1000_WRITE_REG(&sc->hw, E1000_IMS, + E1000_IMS_LSC | igb_iov_intr_mask(sc)); E1000_WRITE_REG(&sc->hw, E1000_EIMS, sc->link_mask); } else if (sc->hw.mac.type == e1000_82574) { E1000_WRITE_REG(&sc->hw, E1000_IMS, @@ -2186,12 +2293,63 @@ em_if_media_change(if_ctx_t ctx) static int em_if_set_promisc(if_ctx_t ctx, int flags) +{ + struct e1000_softc *sc; + + sc = iflib_get_softc(ctx); + if (em_if_defer_promisc(sc)) + return (0); + return (em_if_set_promisc_impl(ctx, flags)); +} + +static bool +em_if_defer_promisc(struct e1000_softc *sc) +{ + switch (sc->hw.mac.type) { + case e1000_82576: + case e1000_i350: + case e1000_vfadapt: + case e1000_vfadapt_i350: + break; + default: + return (false); + } + + /* + * iflib drops its context lock around IFDI_PROMISC_SET. Run mailbox + * and IOV register operations later from the locked admin task. + * A deferred VF mailbox rejection cannot be returned to ifconfig; the + * admin task logs it instead. + */ + atomic_set_32(&sc->promisc_pending, 1); + iflib_admin_intr_deferred(sc->ctx); + return (true); +} + +static int +em_if_set_promisc_impl(if_ctx_t ctx, int flags) { struct e1000_softc *sc = iflib_get_softc(ctx); if_t ifp = iflib_get_ifp(ctx); + enum e1000_promisc_type type; u32 reg_rctl; int mcnt = 0; + if (sc->vf_ifp) { + if (flags & IFF_PROMISC) + type = e1000_promisc_enabled; + else if (flags & IFF_ALLMULTI) + type = e1000_promisc_multicast; + else + type = e1000_promisc_disabled; + if (e1000_promisc_set_vf(&sc->hw, type) != E1000_SUCCESS) { + device_printf(sc->dev, + "VF promiscuous-mode request failed\n"); + return (EPERM); + } + return (0); + } + reg_rctl = E1000_READ_REG(&sc->hw, E1000_RCTL); reg_rctl &= ~(E1000_RCTL_SBP | E1000_RCTL_UPE); if (flags & IFF_ALLMULTI) @@ -2210,16 +2368,21 @@ em_if_set_promisc(if_ctx_t ctx, int flags) if (em_debug_sbp) reg_rctl |= E1000_RCTL_SBP; E1000_WRITE_REG(&sc->hw, E1000_RCTL, reg_rctl); - em_if_vlan_filter_disable(sc); + if (igb_iov_enabled(sc)) + em_if_vlan_filter_enable(sc); + else + em_if_vlan_filter_disable(sc); } else { if (flags & IFF_ALLMULTI) { reg_rctl |= E1000_RCTL_MPE; reg_rctl &= ~E1000_RCTL_UPE; E1000_WRITE_REG(&sc->hw, E1000_RCTL, reg_rctl); } - if (em_if_vlan_filter_used(ctx)) + if (igb_iov_enabled(sc) || em_if_vlan_filter_used(ctx)) em_if_vlan_filter_enable(sc); } + igb_iov_update_pf_vmolr(sc); + igb_iov_rebuild_vlan(sc); return (0); } @@ -2268,6 +2431,11 @@ em_if_multi_set(if_ctx_t ctx) mcnt = if_foreach_llmaddr(ifp, em_copy_maddr, mta); + if (sc->vf_ifp) { + e1000_update_mc_addr_list(&sc->hw, mta, mcnt); + return; + } + if (mcnt < MAX_NUM_MULTICAST_ADDRESSES) e1000_update_mc_addr_list(&sc->hw, mta, mcnt); @@ -2293,6 +2461,8 @@ em_if_multi_set(if_ctx_t ctx) if (sc->hw.bus.pci_cmd_word & CMD_MEM_WRT_INVALIDATE) e1000_pci_set_mwi(&sc->hw); } + igb_iov_rebuild_mta(sc); + igb_iov_update_pf_vmolr(sc); } /********************************************************************* @@ -2321,6 +2491,12 @@ em_if_update_admin_status(if_ctx_t ctx) u32 link_check, thstat, ctrl; bool reset_requested = false; + if (atomic_readandclear_32(&sc->promisc_pending) != 0) + (void)em_if_set_promisc_impl(ctx, + if_getflags(iflib_get_ifp(ctx))); + igb_iov_handle_mdd(sc); + igb_iov_handle_mbx(sc); + link_check = thstat = ctrl = 0; /* Get the cached link value or read phy for real */ switch (hw->phy.media_type) { @@ -2348,7 +2524,12 @@ em_if_update_admin_status(if_ctx_t ctx) break; /* VF device is type_unknown */ case e1000_media_type_unknown: - e1000_check_for_link(hw); + if (e1000_check_for_link(hw) != E1000_SUCCESS && + sc->vf_ifp && !sc->vf_reset_pending) { + sc->vf_reset_pending = true; + iflib_request_reset(ctx); + iflib_admin_intr_deferred(ctx); + } link_check = !hw->mac.get_link_status; /* FALLTHROUGH */ default: @@ -2401,7 +2582,12 @@ em_if_update_admin_status(if_ctx_t ctx) hw->mac.type >= igb_mac_min) { hw->dev_spec._82575.media_changed = false; sc->flags |= IGB_MEDIA_RESET; - em_reset(ctx); + if (igb_iov_enabled(sc)) { + iflib_request_reset(ctx); + iflib_admin_intr_deferred(ctx); + reset_requested = true; + } else + em_reset(ctx); } /* Only do TSO on gigabit for older chips due to errata */ if (hw->mac.type < igb_mac_min) @@ -2417,6 +2603,7 @@ em_if_update_admin_status(if_ctx_t ctx) iflib_link_state_change(ctx, LINK_STATE_UP, IF_Mbps(sc->link_speed)); } + igb_iov_ping_all_vfs(sc); } else if (!link_check && (sc->link_state == EM_LINK_STATE_UP || sc->link_state == EM_LINK_STATE_UP_RESET_PENDING)) { @@ -2432,6 +2619,7 @@ em_if_update_admin_status(if_ctx_t ctx) EM_LINK_STATE_DOWN_RESET_PENDING : EM_LINK_STATE_DOWN; if (link_was_published) iflib_link_state_change(ctx, LINK_STATE_DOWN, 0); + igb_iov_ping_all_vfs(sc); } em_update_stats_counters(sc); @@ -2472,6 +2660,7 @@ em_if_stop(if_ctx_t ctx) if (sc->hw.mac.type >= e1000_pch_spt && sc->hw.mac.type < igb_mac_min) em_flush_desc_rings(sc); + igb_iov_reset_prepare(sc); e1000_reset_hw(&sc->hw); if (sc->hw.mac.type >= e1000_82544 && !sc->vf_ifp) E1000_WRITE_REG(&sc->hw, E1000_WUFC, 0); @@ -2682,6 +2871,14 @@ igb_configure_queues(struct e1000_softc *sc) struct em_tx_queue *tx_que; u32 tmp, ivar = 0; + /* + * Queue ownership can change when SR-IOV is enabled or disabled. + * Rebuild the interrupt mask for the current layout instead of + * retaining vectors from a previous initialization. + */ + sc->que_mask = 0; + sc->link_mask = 0; + /* First turn on RSS capability */ if (hw->mac.type != e1000_82575) E1000_WRITE_REG(hw, E1000_GPIE, @@ -2714,6 +2911,7 @@ igb_configure_queues(struct e1000_softc *sc) ivar |= rx_que->msix | E1000_IVAR_VALID; } E1000_WRITE_REG_ARRAY(hw, E1000_IVAR0, index, ivar); + sc->que_mask |= rx_que->eims; } /* TX entries */ for (int i = 0; i < sc->tx_num_queues; i++) { @@ -2845,6 +3043,8 @@ igb_initialize_interrupt_rate(struct e1000_softc *sc) E1000_WRITE_REG(hw, E1000_EITR(rx_que->msix), rx_que->itr_setting); } + if (sc->intr_type == IFLIB_INTR_MSIX) + E1000_WRITE_REG(hw, E1000_EITR(sc->linkvec), newitr); } static void @@ -2974,6 +3174,16 @@ igb_init_dmac(struct e1000_softc *sc, u32 pba) if (hw->mac.type == e1000_i211) return; + /* + * I350 DMA coalescing and SR-IOV are mutually exclusive. Preserve + * the configured value so it can be restored after IOV is disabled. + */ + if (igb_iov_enabled(sc)) { + if (hw->mac.type > e1000_82580) + E1000_WRITE_REG(hw, E1000_DMACR, 0); + return; + } + max_frame_size = sc->shared->isc_max_frame_size; if (hw->mac.type > e1000_82580) { @@ -3945,7 +4155,7 @@ em_initialize_receive_unit(if_ctx_t ctx) rctl &= ~E1000_RCTL_SBP; /* Enable Long Packet receive */ - if (if_getmtu(ifp) > ETHERMTU) + if (igb_iov_enabled(sc) || if_getmtu(ifp) > ETHERMTU) rctl |= E1000_RCTL_LPE; else rctl &= ~E1000_RCTL_LPE; @@ -4086,16 +4296,16 @@ em_initialize_receive_unit(if_ctx_t ctx) } else if (hw->mac.type >= igb_mac_min) { u32 psize, srrctl = 0; - if (if_getmtu(ifp) > ETHERMTU) { + if (igb_iov_enabled(sc)) { + E1000_WRITE_REG(hw, E1000_RLPML, + IGB_IOV_MAX_FRAME_SIZE); + } else if (!sc->vf_ifp && if_getmtu(ifp) > ETHERMTU) { psize = scctx->isc_max_frame_size; /* are we on a vlan? */ if (if_vlantrunkinuse(ifp)) psize += VLAN_TAG_SIZE; - if (sc->vf_ifp) - e1000_rlpml_set_vf(hw, psize); - else - E1000_WRITE_REG(hw, E1000_RLPML, psize); + E1000_WRITE_REG(hw, E1000_RLPML, psize); } /* Set maximum packet buffer len */ @@ -4109,11 +4319,11 @@ em_initialize_receive_unit(if_ctx_t ctx) * This drops frames rather than hanging the RX MAC for all * queues. */ - if ((sc->rx_num_queues > 1) && + if (igb_iov_enabled(sc) || + ((sc->rx_num_queues > 1) && (sc->fc == e1000_fc_none || - sc->fc == e1000_fc_rx_pause)) { + sc->fc == e1000_fc_rx_pause))) srrctl |= E1000_SRRCTL_DROP_EN; - } /* Setup the Base and Length of the Rx Descriptor Rings */ for (i = 0, que = sc->rx_queues; i < sc->rx_num_queues; i++, que++) { @@ -4190,37 +4400,62 @@ static void em_if_vlan_register(if_ctx_t ctx, u16 vtag) { struct e1000_softc *sc = iflib_get_softc(ctx); - u32 index, bit; + bool present; + u32 index, mask; index = (vtag >> 5) & 0x7F; - bit = vtag & 0x1F; - sc->shadow_vfta[index] |= (1 << bit); - ++sc->num_vlans; - if (!sc->vf_ifp) - em_if_vlan_filter_write(sc); - else - /* - * Physical funtion may reject registering VLAN - * but we have no way to inform the stack - * about that. - */ - e1000_vfta_set_vf(&sc->hw, vtag, true); + mask = 1U << (vtag & 0x1F); + present = (sc->shadow_vfta[index] & mask) != 0; + /* + * On a VF, record registration intent for replay even if the PF is not + * ready to accept it yet. + */ + sc->shadow_vfta[index] |= mask; + if (!present) + ++sc->num_vlans; + if (sc->vf_ifp && + e1000_vfta_set_vf(&sc->hw, vtag, true) != E1000_SUCCESS) { + device_printf(sc->dev, + "VF VLAN %u add request failed\n", vtag); + } + if (!sc->vf_ifp) { + if (igb_iov_enabled(sc)) + igb_iov_rebuild_vlan(sc); + else + em_if_vlan_filter_write(sc); + } } static void em_if_vlan_unregister(if_ctx_t ctx, u16 vtag) { struct e1000_softc *sc = iflib_get_softc(ctx); - u32 index, bit; + bool present; + u32 index, mask; index = (vtag >> 5) & 0x7F; - bit = vtag & 0x1F; - sc->shadow_vfta[index] &= ~(1 << bit); - --sc->num_vlans; - if (!sc->vf_ifp) - em_if_vlan_filter_write(sc); - else - e1000_vfta_set_vf(&sc->hw, vtag, false); + mask = 1U << (vtag & 0x1F); + present = (sc->shadow_vfta[index] & mask) != 0; + if (sc->vf_ifp && + e1000_vfta_set_vf(&sc->hw, vtag, false) != E1000_SUCCESS) { + device_printf(sc->dev, + "VF VLAN %u remove request failed\n", vtag); + /* + * The PF can still admit this VID. Retain the bit so its + * stripped frames remain tagged and are dropped by vlan(4) + * after the child disappears, rather than reaching the parent. + */ + return; + } + sc->shadow_vfta[index] &= ~mask; + if (present) + --sc->num_vlans; + if (!sc->vf_ifp) { + if (igb_iov_enabled(sc)) + igb_iov_rebuild_vlan(sc); + else + em_if_vlan_filter_write(sc); + } } static bool @@ -4299,15 +4534,29 @@ em_setup_vlan_hw_support(if_ctx_t ctx) struct e1000_softc *sc = iflib_get_softc(ctx); struct e1000_hw *hw = &sc->hw; if_t ifp = iflib_get_ifp(ctx); - u32 reg; + u32 max_frame_size, reg; + u16 vid; /* * Only PFs have control over VLAN HW filtering * configuration. VFs have to act as if it's always * enabled. */ - if (sc->vf_ifp) + if (sc->vf_ifp) { + max_frame_size = min(sc->shared->isc_max_frame_size + + VLAN_TAG_SIZE, IGB_IOV_MAX_FRAME_SIZE); + e1000_rlpml_set_vf(hw, max_frame_size); + for (vid = 0; vid < 4096; vid++) { + if ((sc->shadow_vfta[vid >> 5] & + (1U << (vid & 0x1f))) == 0) + continue; + if (e1000_vfta_set_vf(hw, vid, true) != E1000_SUCCESS) + device_printf(sc->dev, + "VF VLAN %u restore request failed\n", + vid); + } return; + } if (if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING && !em_disable_crc_stripping) { @@ -4320,11 +4569,25 @@ em_setup_vlan_hw_support(if_ctx_t ctx) E1000_WRITE_REG(hw, E1000_CTRL, reg); } - /* If we aren't doing HW filtering, we're done */ + /* + * SR-IOV always needs VFE for VF isolation. When PF hardware VLAN + * filtering is disabled, the IOV VLAN rebuild instead makes the PF + * VLAN-promiscuous without disabling the global filter. + */ if (!em_if_vlan_filter_capable(ctx)) { - em_if_vlan_filter_disable(sc); + if (igb_iov_enabled(sc)) { +#ifdef PCI_IOV + sc->iov_pf_vlan_promisc = true; +#endif + em_if_vlan_filter_enable(sc); + } else + em_if_vlan_filter_disable(sc); return; } +#ifdef PCI_IOV + if (igb_iov_enabled(sc)) + sc->iov_pf_vlan_promisc = false; +#endif /* * A soft reset zero's out the VFTA, so @@ -4371,14 +4634,21 @@ igb_if_intr_enable(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); struct e1000_hw *hw = &sc->hw; - u32 mask; + u32 mask, reg; if (__predict_true(sc->intr_type == IFLIB_INTR_MSIX)) { mask = (sc->que_mask | sc->link_mask); - E1000_WRITE_REG(hw, E1000_EIAC, mask); - E1000_WRITE_REG(hw, E1000_EIAM, mask); + /* + * VF interrupt controls are also mapped into these registers. + * Preserve them and change only the PF vectors we own. + */ + reg = E1000_READ_REG(hw, E1000_EIAC); + E1000_WRITE_REG(hw, E1000_EIAC, reg | mask); + reg = E1000_READ_REG(hw, E1000_EIAM); + E1000_WRITE_REG(hw, E1000_EIAM, reg | mask); E1000_WRITE_REG(hw, E1000_EIMS, mask); - E1000_WRITE_REG(hw, E1000_IMS, E1000_IMS_LSC); + E1000_WRITE_REG(hw, E1000_IMS, + E1000_IMS_LSC | igb_iov_intr_mask(sc)); } else E1000_WRITE_REG(hw, E1000_IMS, IMS_ENABLE_MASK); E1000_WRITE_FLUSH(hw); @@ -4389,10 +4659,22 @@ igb_if_intr_disable(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); struct e1000_hw *hw = &sc->hw; + u32 mask, reg; if (__predict_true(sc->intr_type == IFLIB_INTR_MSIX)) { - E1000_WRITE_REG(hw, E1000_EIMC, 0xffffffff); - E1000_WRITE_REG(hw, E1000_EIAC, 0); + /* + * Do not use a blanket EIMC write here. VF interrupt controls + * are mapped into the same PF register space, so clearing bits + * we do not own can leave running VFs with interrupts masked. + * Before initial queue configuration the owned mask is zero + * because this driver has not enabled a vector yet. + */ + mask = (sc->que_mask | sc->link_mask); + reg = E1000_READ_REG(hw, E1000_EIAM); + E1000_WRITE_REG(hw, E1000_EIAM, reg & ~mask); + E1000_WRITE_REG(hw, E1000_EIMC, mask); + reg = E1000_READ_REG(hw, E1000_EIAC); + E1000_WRITE_REG(hw, E1000_EIAC, reg & ~mask); } E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); E1000_WRITE_FLUSH(hw); diff --git a/sys/dev/e1000/if_em.h b/sys/dev/e1000/if_em.h index 9dfe23453428..e3979f79974d 100644 --- a/sys/dev/e1000/if_em.h +++ b/sys/dev/e1000/if_em.h @@ -48,6 +48,7 @@ #endif #include #include +#include #include #include #include @@ -95,6 +96,9 @@ #include "e1000_82571.h" #include "ifdi_if.h" +struct igb_vf; +struct igb_vf_mac_filter; + /* Tunables */ /* @@ -133,6 +137,7 @@ #define EM_DEFAULT_RXD 1024 #define EM_DEFAULT_MULTI_RXD 4096 #define IGB_MAX_RXD 4096 +#define IGB_MAX_FRAME_SIZE 9234 /* * EM_TIDV - Transmit Interrupt Delay Value @@ -592,6 +597,27 @@ struct e1000_softc { u32 pba; int link_mask; int tso_automasked; + u32 promisc_pending; + +#ifdef PCI_IOV + struct igb_vf *vfs; + struct igb_vf_mac_filter *vf_mac_filters; + struct callout iov_mbx_retry; + u32 iov_vfta[EM_VFTA_SIZE]; + u32 iov_mdd_cause; + u32 iov_pending; + u32 iov_spoof_pending; + u32 iov_teardown; + struct timeval iov_last_mdd_log; + u16 num_vfs; + u16 num_vf_mac_filters; + u16 pool; + bool iov_hw_active; + bool iov_mbx_retry_initialized; + bool iov_pf_mdd_blocked; + bool iov_pf_vlan_promisc; + bool iov_vfta_valid; +#endif u64 que_mask; @@ -616,6 +642,7 @@ struct e1000_softc { } ustats; u16 vf_ifp; + bool vf_reset_pending; }; /******************************************************************************** diff --git a/sys/dev/e1000/if_igb_iov.c b/sys/dev/e1000/if_igb_iov.c new file mode 100644 index 000000000000..1f66f9084e1a --- /dev/null +++ b/sys/dev/e1000/if_igb_iov.c @@ -0,0 +1,1819 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2010-2016, Intel Corporation + * Copyright (c) 2026 Kevin Bowling + */ + +#include "if_em.h" +#include "if_igb_iov.h" + +#ifdef PCI_IOV + +#include +#include + +#define IGB_IOV_RAH_POOLSEL_SHIFT 18 +#define IGB_IOV_RAH_POOLSEL_MASK (0xffU << IGB_IOV_RAH_POOLSEL_SHIFT) +#define IGB_IOV_MAX_MAC_FILTERS 3 +#define IGB_IOV_MAX_MC_HASHES 30 +#define IGB_IOV_MBX_RETRY_COUNT 6 +/* 82576 Datasheet rev. 2.0, Section 8.14.16: VMOLR[31] must be one. */ +#define IGB_82576_VMOLR_RSV (1U << 31) +#define IGB_82576_LVMMC_BLOCK_MASK 0x1c +#define IGB_82576_QUEUE_MASK 0xffff +#define IGB_82576_STAGGERED_QUEUE_SHIFT 8 +#define IGB_I350_DTXCTL_ENABLE_SPOOF_QUEUE (1U << 2) +#define IGB_I350_LVMMC_MAC_VLAN_SPOOF (1U << 25) +#define IGB_I350_LVMMC_LAST_Q_SHIFT 29 +#define IGB_I350_LVMMC_LAST_Q_MASK 0x7 +#define IGB_I350_QUEUE_MASK 0xff +#define IGB_I350_RESET_ACK_TIMEOUT (100 * SBT_1MS) + +#define IGB_VF_CTS (1U << 0) +#define IGB_VF_CAP_MAC (1U << 1) +#define IGB_VF_ACTIVE (1U << 2) +#define IGB_VF_MAC_ANTI_SPOOF (1U << 3) +#define IGB_VF_ALLOW_PROMISC (1U << 4) +#define IGB_VF_UCAST_PROMISC (1U << 5) +#define IGB_VF_MCAST_PROMISC (1U << 6) +#define IGB_VF_MCAST_OVERFLOW (1U << 7) +#define IGB_VF_MCAST_OVERFLOW_WARNED (1U << 8) +#define IGB_VF_MDD_BLOCKED (1U << 9) +#define IGB_VF_MBX_PENDING (1U << 10) +/* + * After bounded PFU retries, suppress future or overlapping VF requests until + * RST/VFLR starts a new mailbox epoch. Intel VF drivers assert CTRL.RST + * before sending their mailbox reset request. + */ +#define IGB_VF_MBX_GAVE_UP (1U << 11) +#define IGB_VF_MDD_NOTIFY_PENDING (1U << 12) + +struct igb_vf { + u32 flags; + struct timeval last_nack; + struct timeval last_mbx_log; + struct timeval last_spoof_log; + struct timeval last_mdd_log; + sbintime_t mbx_retry_at; + sbintime_t mdd_notify_at; + u16 pool; + u16 rar_index; + u16 max_frame_size; + u16 mc_count; + u16 vlan_count; + u16 default_vlan; + u8 mbx_retry_count; + u8 mac[ETHER_ADDR_LEN]; + u16 mc_hashes[IGB_IOV_MAX_MC_HASHES]; + u32 vlans[EM_VFTA_SIZE]; +}; + +struct igb_vf_mac_filter { + bool active; + u16 pool; + u16 rar_index; + u8 mac[ETHER_ADDR_LEN]; +}; + +MALLOC_DEFINE(M_IGB_IOV, "igb_iov", "igb SR-IOV allocations"); + +static const struct timeval igb_iov_nack_interval = { 2, 0 }; +static const struct timeval igb_iov_mbx_log_interval = { 2, 0 }; +static const struct timeval igb_iov_spoof_log_interval = { 2, 0 }; +static const struct timeval igb_iov_mdd_log_interval = { 2, 0 }; +static const sbintime_t igb_iov_mdd_notify_retry = SBT_1S / 2; +static const sbintime_t igb_iov_mbx_retry_delay[IGB_IOV_MBX_RETRY_COUNT] = { + SBT_1MS, + 2 * SBT_1MS, + 4 * SBT_1MS, + 8 * SBT_1MS, + 16 * SBT_1MS, + 32 * SBT_1MS, +}; + +static void igb_iov_clear_mac_filters(struct e1000_softc *, + const struct igb_vf *); +static bool igb_iov_mac_in_use(struct e1000_softc *, const u8 *, + const struct igb_vf *); +static bool igb_iov_vlan_present(struct e1000_softc *, u16, bool); +static int igb_iov_vlan_unique_count(struct e1000_softc *, bool); + +static void +igb_iov_mbx_retry_callout(void *arg) +{ + struct e1000_softc *sc; + + sc = arg; + /* + * Mailbox service is serialized by iflib's context lock. The + * callout only re-enters through the ordinary admin task. + */ + iflib_admin_intr_deferred(sc->ctx); +} + +static u_int +igb_iov_copy_maddr(void *arg, struct sockaddr_dl *sdl, u_int idx) +{ + u8 *mta; + + if (idx == MAX_NUM_MULTICAST_ADDRESSES) + return (0); + mta = arg; + memcpy(&mta[idx * ETHER_ADDR_LEN], LLADDR(sdl), ETHER_ADDR_LEN); + return (1); +} + +static bool +igb_iov_pf_vlan_promisc(struct e1000_softc *sc) +{ + if_t ifp; + + ifp = iflib_get_ifp(sc->ctx); + return (sc->iov_pf_vlan_promisc || + (if_getflags(ifp) & IFF_PROMISC) != 0); +} + +static bool +igb_iov_mac_valid(const u8 *mac) +{ + static const u8 zero[ETHER_ADDR_LEN]; + + return (!ETHER_IS_MULTICAST(mac) && + memcmp(mac, zero, ETHER_ADDR_LEN) != 0); +} + +static bool +igb_iov_nack_allowed(struct igb_vf *vf) +{ + return (ratecheck(&vf->last_nack, &igb_iov_nack_interval) != 0); +} + +static u32 +igb_iov_reply_header(u32 request, bool cts, bool ack) +{ + u32 reply, type; + + type = request & 0xffff; + if (type == E1000_VF_SET_MAC_ADDR && + (request & E1000_VT_MSGINFO_MASK) != 0) + reply = request; + else + reply = type; + reply &= ~(E1000_VT_MSGTYPE_ACK | E1000_VT_MSGTYPE_NACK | + E1000_VT_MSGTYPE_CTS); + if (cts) + reply |= E1000_VT_MSGTYPE_CTS; + reply |= ack ? E1000_VT_MSGTYPE_ACK : E1000_VT_MSGTYPE_NACK; + return (reply); +} *** 1773 LINES SKIPPED ***