Date: Thu, 30 Jul 2026 04:52:59 +0000 From: Kevin Bowling <kbowling@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: 316f6f2f7fa6 - main - igbv: Isolate VF policy and validate its registers Message-ID: <6a6ad8ab.3235a.3d9f3300@gitrepo.freebsd.org>
index | next in thread | raw e-mail
The branch main has been updated by kbowling: URL: https://cgit.FreeBSD.org/src/commit/?id=316f6f2f7fa613d13316ea966a34ee5285b987ec commit 316f6f2f7fa613d13316ea966a34ee5285b987ec Author: Kevin Bowling <kbowling@FreeBSD.org> AuthorDate: 2026-07-30 04:05:47 +0000 Commit: Kevin Bowling <kbowling@FreeBSD.org> CommitDate: 2026-07-30 04:52:39 +0000 igbv: Isolate VF policy and validate its registers Give igb virtual functions a separate ifdi method table and move VF-specific attach, reset, queue, interrupt, and diagnostic policy to if_igbv.c. Keep shared descriptor-ring mechanisms in if_em.c. Derive VF identity from IFLIB_IS_VF and assert that hardware identification agrees. Under INVARIANTS, validate normal VF CSR accesses against the sparse 82576 and I350 VF register maps. Stop shared setup from accessing PF-only controls. Require MSI-X and defer VF sysctls until attach succeeds so failed attachment cannot leave handlers pointing at freed driver state. Advertise only VF capabilities, run adaptive moderation without the PF receive-buffer guard, enable SRRCTL.DROP_EN, and provide a VF-safe diagnostic register view. The moved implementation is the existing FreeBSD code. Register model was cross-checked against the Intel datasheets and other Intel drivers. Sponsored by: BBOX.io --- sys/conf/files | 2 + sys/dev/e1000/e1000_osdep.h | 84 ++++++- sys/dev/e1000/em_txrx.c | 5 + sys/dev/e1000/if_em.c | 601 ++++++++++++++++++++++++++------------------ sys/dev/e1000/if_em.h | 19 ++ sys/dev/e1000/if_igbv.c | 205 +++++++++++++++ sys/modules/em/Makefile | 2 +- 7 files changed, 670 insertions(+), 248 deletions(-) diff --git a/sys/conf/files b/sys/conf/files index a408917484bd..1c52b432442f 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_igbv.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 \ diff --git a/sys/dev/e1000/e1000_osdep.h b/sys/dev/e1000/e1000_osdep.h index ba1c8a16fad1..c7e938f5c93c 100644 --- a/sys/dev/e1000/e1000_osdep.h +++ b/sys/dev/e1000/e1000_osdep.h @@ -161,8 +161,83 @@ struct e1000_osdep bus_space_handle_t flash_bus_space_handle; device_t dev; if_ctx_t ctx; + bool vf; }; +#ifdef INVARIANTS +/* + * 82576 and I350 VFs expose a sparse register file. Keep this list local to + * the OS accessors so a PF-only register that leaks into the shared VF path + * fails at its first access instead of returning reserved-register garbage. + * + * The driver intentionally uses only queue pair zero. Expanding VF queue + * support must extend this predicate from the applicable device CSR map. + */ +static __inline bool +e1000_vf_reg_valid(uint32_t reg, bool write) +{ + /* VF mailbox memory: 16 dwords beginning at 0x800. */ + if (reg >= 0x00800 && reg <= 0x0083c && (reg & 3) == 0) + return (true); + + /* The three VF MSI-X interrupt-throttling registers. */ + if (reg >= 0x01680 && reg <= 0x01688 && (reg & 3) == 0) + return (true); + + /* Receive queue zero. */ + switch (reg) { + case 0x02800: /* RDBAL */ + case 0x02804: /* RDBAH */ + case 0x02808: /* RDLEN */ + case 0x0280c: /* SRRCTL */ + case 0x02810: /* RDH */ + case 0x02818: /* RDT */ + case 0x02828: /* RXDCTL */ + return (true); + } + + /* Transmit queue zero. */ + switch (reg) { + case 0x03800: /* TDBAL */ + case 0x03804: /* TDBAH */ + case 0x03808: /* TDLEN */ + case 0x03810: /* TDH */ + case 0x03818: /* TDT */ + case 0x03828: /* TXDCTL */ + return (true); + } + + switch (reg) { + case 0x00000: /* CTRL */ + case 0x000c4: /* Legacy ITR, listed but unused by igb VFs */ + case 0x00c40: /* V2PMAILBOX(0) */ + case 0x01524: /* EIMS */ + case 0x0152c: /* EIAC */ + case 0x01530: /* EIAM */ + case 0x01700: /* IVAR0 */ + case 0x01740: /* IVAR_MISC */ + return (true); + case 0x01520: /* EICS */ + case 0x01528: /* EIMC */ + return (write); + case 0x00008: /* STATUS */ + case 0x00f10: /* VFGPRC */ + case 0x00f14: /* VFGPTC */ + case 0x00f18: /* VFGORC */ + case 0x00f34: /* VFGOTC */ + case 0x00f3c: /* VFMPRC */ + case 0x00f40: /* VFGPRLBC */ + case 0x00f44: /* VFGPTLBC */ + case 0x00f48: /* VFGORLBC */ + case 0x00f50: /* VFGOTLBC */ + case 0x01580: /* EICR */ + return (!write); + default: + return (false); + } +} +#endif + #define E1000_REGISTER(hw, reg) (((hw)->mac.type >= e1000_82543) \ ? reg : e1000_translate_register_82542(reg)) @@ -185,6 +260,10 @@ e1000_rd32(struct e1000_osdep *osdep, uint32_t reg) KASSERT(reg < osdep->mem_bus_space_size, ("e1000: register offset %#jx too large (max is %#jx)", (uintmax_t)reg, (uintmax_t)osdep->mem_bus_space_size)); +#ifdef INVARIANTS + KASSERT(!osdep->vf || e1000_vf_reg_valid(reg, false), + ("e1000: invalid VF register read at %#x", reg)); +#endif return (bus_space_read_4(osdep->mem_bus_space_tag, osdep->mem_bus_space_handle, reg)); @@ -198,6 +277,10 @@ e1000_wr32(struct e1000_osdep *osdep, uint32_t reg, uint32_t value) KASSERT(reg < osdep->mem_bus_space_size, ("e1000: register offset %#jx too large (max is %#jx)", (uintmax_t)reg, (uintmax_t)osdep->mem_bus_space_size)); +#ifdef INVARIANTS + KASSERT(!osdep->vf || e1000_vf_reg_valid(reg, true), + ("e1000: invalid VF register write at %#x", reg)); +#endif bus_space_write_4(osdep->mem_bus_space_tag, osdep->mem_bus_space_handle, reg, value); @@ -277,4 +360,3 @@ e1000_wr32(struct e1000_osdep *osdep, uint32_t reg, uint32_t value) #endif #endif /* _FREEBSD_OS_H_ */ - diff --git a/sys/dev/e1000/em_txrx.c b/sys/dev/e1000/em_txrx.c index 564eda9a0f00..dced12770c40 100644 --- a/sys/dev/e1000/em_txrx.c +++ b/sys/dev/e1000/em_txrx.c @@ -96,6 +96,11 @@ em_dump_rs(struct e1000_softc *sc) int16_t rs_cidx; uint8_t status; + if (sc->tx_queues == NULL) { + device_printf(sc->dev, "queue state is unavailable\n"); + return; + } + printf("\n"); ntxd = scctx->isc_ntxd[0]; for (qid = 0; qid < sc->tx_num_queues; qid++) { diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c index 6d534bb54486..0e4671296339 100644 --- a/sys/dev/e1000/if_em.c +++ b/sys/dev/e1000/if_em.c @@ -410,8 +410,6 @@ static int igb_device_attach(device_t); 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); static int em_if_shutdown(if_ctx_t); static int em_if_suspend(if_ctx_t); @@ -457,8 +455,8 @@ static int igb_if_tx_queue_intr_enable(if_ctx_t, uint16_t); static void em_if_multi_set(if_ctx_t); static void em_if_update_admin_status(if_ctx_t); static void em_if_debug(if_ctx_t); -static void em_update_vf_stats_counters(struct e1000_softc *); static void em_update_stats_counters(struct e1000_softc *); +static void em_update_vf_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); @@ -511,7 +509,6 @@ static int igb_sysctl_dmac(SYSCTL_HANDLER_ARGS); static void em_if_led_func(if_ctx_t, int); static int em_get_regs(SYSCTL_HANDLER_ARGS); - static void lem_smartspeed(struct e1000_softc *); static void igb_configure_queues(struct e1000_softc *); static void igb_initialize_interrupt_rate(struct e1000_softc *); @@ -680,6 +677,43 @@ static driver_t igb_if_driver = { "igb_if", igb_if_methods, sizeof(struct e1000_softc) }; +static device_method_t igbv_if_methods[] = { + DEVMETHOD(ifdi_attach_pre, igbv_if_attach_pre), + DEVMETHOD(ifdi_attach_post, igbv_if_attach_post), + DEVMETHOD(ifdi_detach, em_if_detach), + DEVMETHOD(ifdi_shutdown, em_if_shutdown), + DEVMETHOD(ifdi_suspend, em_if_suspend), + DEVMETHOD(ifdi_resume, em_if_resume), + DEVMETHOD(ifdi_init, em_if_init), + DEVMETHOD(ifdi_stop, em_if_stop), + DEVMETHOD(ifdi_msix_intr_assign, em_if_msix_intr_assign), + DEVMETHOD(ifdi_intr_enable, igbv_if_intr_enable), + DEVMETHOD(ifdi_intr_disable, igbv_if_intr_disable), + DEVMETHOD(ifdi_tx_queues_alloc, em_if_tx_queues_alloc), + DEVMETHOD(ifdi_rx_queues_alloc, em_if_rx_queues_alloc), + DEVMETHOD(ifdi_queues_free, em_if_queues_free), + DEVMETHOD(ifdi_update_admin_status, em_if_update_admin_status), + DEVMETHOD(ifdi_multi_set, em_if_multi_set), + DEVMETHOD(ifdi_media_status, em_if_media_status), + DEVMETHOD(ifdi_media_change, em_if_media_change), + DEVMETHOD(ifdi_mtu_set, em_if_mtu_set), + DEVMETHOD(ifdi_promisc_set, em_if_set_promisc), + DEVMETHOD(ifdi_timer, em_if_timer), + DEVMETHOD(ifdi_watchdog_reset, em_if_watchdog_reset), + DEVMETHOD(ifdi_vlan_register, em_if_vlan_register), + DEVMETHOD(ifdi_vlan_unregister, em_if_vlan_unregister), + DEVMETHOD(ifdi_get_counter, em_if_get_counter), + DEVMETHOD(ifdi_rx_queue_intr_enable, igb_if_rx_queue_intr_enable), + 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), + DEVMETHOD_END +}; + +static driver_t igbv_if_driver = { + "igbv_if", igbv_if_methods, sizeof(struct e1000_softc) +}; + /********************************************************************* * Tunable default values. *********************************************************************/ @@ -814,9 +848,9 @@ static struct if_shared_ctx igb_sctx_init = { }; /* - * igb PFs and igbv VFs share the same ifdi implementation, but iflib must - * know which instances are VFs so that detaching a VF does not invoke the - * PF-only PCI IOV detach guard. + * igb PFs and igbv VFs share the common datapath implementation. Keep a + * separate ifdi policy for VFs so they cannot inherit PF-only callbacks or + * interrupt modes. */ static struct if_shared_ctx igbv_sctx_init = { .isc_magic = IFLIB_MAGIC, @@ -834,7 +868,7 @@ static struct if_shared_ctx igbv_sctx_init = { .isc_admin_intrcnt = 1, .isc_vendor_info = igbv_vendor_info_array, .isc_driver_version = igb_driver_version, - .isc_driver = &igb_if_driver, + .isc_driver = &igbv_if_driver, .isc_flags = IFLIB_NEED_SCRATCH | IFLIB_TSO_INIT_IP | IFLIB_NEED_ZERO_CSUM | IFLIB_IS_VF, @@ -1075,6 +1109,10 @@ em_set_num_queues(if_ctx_t ctx) case e1000_82574: maxqueues = 2; break; + case e1000_vfadapt: + case e1000_vfadapt_i350: + maxqueues = 1; + break; default: maxqueues = 1; break; @@ -1083,97 +1121,75 @@ em_set_num_queues(if_ctx_t ctx) return (maxqueues); } -#define LEM_CAPS \ +#define LEM_CAPS ( \ IFCAP_HWCSUM | IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING | \ IFCAP_VLAN_HWCSUM | IFCAP_WOL | IFCAP_VLAN_HWFILTER | IFCAP_TSO4 | \ - IFCAP_LRO | IFCAP_VLAN_HWTSO | IFCAP_JUMBO_MTU | IFCAP_HWCSUM_IPV6 + IFCAP_LRO | IFCAP_VLAN_HWTSO | IFCAP_JUMBO_MTU | IFCAP_HWCSUM_IPV6) -#define EM_CAPS \ +#define EM_CAPS ( \ IFCAP_HWCSUM | IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING | \ IFCAP_VLAN_HWCSUM | IFCAP_WOL | IFCAP_VLAN_HWFILTER | IFCAP_TSO4 | \ IFCAP_LRO | IFCAP_VLAN_HWTSO | IFCAP_JUMBO_MTU | IFCAP_HWCSUM_IPV6 | \ - IFCAP_TSO6 + IFCAP_TSO6) -#define IGB_CAPS \ +#define IGB_CAPS ( \ IFCAP_HWCSUM | IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING | \ IFCAP_VLAN_HWCSUM | IFCAP_WOL | IFCAP_VLAN_HWFILTER | IFCAP_TSO4 | \ IFCAP_LRO | IFCAP_VLAN_HWTSO | IFCAP_JUMBO_MTU | IFCAP_HWCSUM_IPV6 | \ - IFCAP_TSO6 + IFCAP_TSO6) -/********************************************************************* - * Device initialization routine - * - * The attach entry point is called when the driver is being loaded. - * This routine identifies the type of hardware, allocates all resources - * and initializes the hardware. - * - * return 0 on success, positive on failure - *********************************************************************/ -static int -em_if_attach_pre(if_ctx_t ctx) +/* + * VLAN filtering is an effective VF capability, but its policy is owned by + * the PF and cannot be disabled from the VF. vlan(4) registration callbacks + * are independent of this capability bit. + */ +#define IGBV_CAPS (IGB_CAPS & ~IFCAP_WOL) + +void +em_add_device_sysctls(struct e1000_softc *sc) { - struct e1000_softc *sc; - if_softc_ctx_t scctx; - device_t dev; struct e1000_hw *hw; struct sysctl_oid_list *child; struct sysctl_ctx_list *ctx_list; - int error = 0; - INIT_DEBUGOUT("em_if_attach_pre: begin"); - dev = iflib_get_dev(ctx); - sc = iflib_get_softc(ctx); + hw = &sc->hw; + ctx_list = device_get_sysctl_ctx(sc->dev); + child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); - if (em_max_interrupt_rate <= 0) { - device_printf(dev, - "Invalid max_interrupt_rate %d; using default %d\n", - em_max_interrupt_rate, EM_INTS_DEFAULT); - em_max_interrupt_rate = EM_INTS_DEFAULT; - } + sc->enable_aim = em_enable_aim; + SYSCTL_ADD_INT(ctx_list, child, OID_AUTO, "enable_aim", + CTLFLAG_RW, &sc->enable_aim, 0, + "Interrupt Moderation (1=normal, 2=lowlatency)"); - sc->ctx = sc->osdep.ctx = ctx; - sc->dev = sc->osdep.dev = dev; - scctx = sc->shared = iflib_get_softc_ctx(ctx); - sc->media = iflib_get_media(ctx); - hw = &sc->hw; + SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "debug", + CTLTYPE_INT | CTLFLAG_RW, sc, 0, + em_sysctl_debug_info, "I", "Debug Information"); - /* Determine hardware and mac info */ - em_identify_hardware(ctx); + SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "rs_dump", + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, + em_get_rs, "I", "Dump RS indexes"); - /* SYSCTL stuff */ - ctx_list = device_get_sysctl_ctx(dev); - child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); + if (sc->vf_ifp) { + SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "reg_dump", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0, + igbv_get_regs, "A", "Dump VF registers"); + return; + } SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "nvm", CTLTYPE_INT | CTLFLAG_RW, sc, 0, em_sysctl_nvm_info, "I", "NVM Information"); - - sc->enable_aim = em_enable_aim; - SYSCTL_ADD_INT(ctx_list, child, OID_AUTO, "enable_aim", - CTLFLAG_RW, &sc->enable_aim, 0, - "Interrupt Moderation (1=normal, 2=lowlatency)"); - SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "fw_version", CTLTYPE_STRING | CTLFLAG_RD, sc, 0, em_sysctl_print_fw_version, "A", "Prints FW/NVM Versions"); - - SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "debug", - CTLTYPE_INT | CTLFLAG_RW, sc, 0, - em_sysctl_debug_info, "I", "Debug Information"); - SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "fc", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, em_set_flowcntl, "I", "Flow Control"); - SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "reg_dump", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0, em_get_regs, "A", "Dump Registers"); - SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "rs_dump", - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, - em_get_rs, "I", "Dump RS indexes"); - if (hw->mac.type >= e1000_i350) { SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "dmac", CTLTYPE_INT | CTLFLAG_RW, sc, 0, @@ -1185,18 +1201,66 @@ em_if_attach_pre(if_ctx_t ctx) CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, em_sysctl_tso_tcp_flags_mask, "IU", "TSO TCP flags mask for first segment"); - SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "tso_tcp_flags_mask_middle_segment", CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 1, em_sysctl_tso_tcp_flags_mask, "IU", "TSO TCP flags mask for middle segment"); - SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "tso_tcp_flags_mask_last_segment", CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 2, em_sysctl_tso_tcp_flags_mask, "IU", "TSO TCP flags mask for last segment"); +} + +/********************************************************************* + * Device initialization routine + * + * The attach entry point is called when the driver is being loaded. + * This routine identifies the type of hardware, allocates all resources + * and initializes the hardware. + * + * return 0 on success, positive on failure + *********************************************************************/ +int +em_if_attach_pre(if_ctx_t ctx) +{ + struct e1000_softc *sc; + if_softc_ctx_t scctx; + device_t dev; + struct e1000_hw *hw; + struct sysctl_oid_list *child; + struct sysctl_ctx_list *ctx_list; + int error = 0; + + INIT_DEBUGOUT("em_if_attach_pre: begin"); + dev = iflib_get_dev(ctx); + sc = iflib_get_softc(ctx); + + if (em_max_interrupt_rate <= 0) { + device_printf(dev, + "Invalid max_interrupt_rate %d; using default %d\n", + em_max_interrupt_rate, EM_INTS_DEFAULT); + em_max_interrupt_rate = EM_INTS_DEFAULT; + } + + sc->ctx = sc->osdep.ctx = ctx; + sc->dev = sc->osdep.dev = dev; + scctx = sc->shared = iflib_get_softc_ctx(ctx); + sc->media = iflib_get_media(ctx); + hw = &sc->hw; + sc->vf_ifp = + (iflib_get_sctx(ctx)->isc_flags & IFLIB_IS_VF) != 0; + sc->osdep.vf = sc->vf_ifp; + + /* Determine hardware and mac info */ + em_identify_hardware(ctx); + + /* VF sysctls are deferred until attach-post confirms MSI-X. */ + ctx_list = device_get_sysctl_ctx(dev); + child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); + if (!sc->vf_ifp) + em_add_device_sysctls(sc); scctx->isc_tx_nsegments = EM_MAX_SCATTER; scctx->isc_nrxqsets_max = @@ -1216,7 +1280,8 @@ em_if_attach_pre(if_ctx_t ctx) scctx->isc_tx_tso_segments_max = EM_MAX_SCATTER; scctx->isc_tx_tso_size_max = EM_TSO_SIZE; scctx->isc_tx_tso_segsize_max = EM_TSO_SEG_SIZE; - scctx->isc_capabilities = scctx->isc_capenable = IGB_CAPS; + scctx->isc_capabilities = scctx->isc_capenable = + sc->vf_ifp ? IGBV_CAPS : IGB_CAPS; scctx->isc_tx_csum_flags = CSUM_TCP | CSUM_UDP | CSUM_TSO | CSUM_IP6_TCP | CSUM_IP6_UDP; if (hw->mac.type != e1000_82575) @@ -1442,19 +1507,21 @@ em_if_attach_pre(if_ctx_t ctx) /* Clear the IFCAP_TSO auto mask */ sc->tso_automasked = 0; - /* Check SOL/IDER usage */ - if (e1000_check_reset_block(hw)) + /* Check SOL/IDER usage on physical functions. */ + if (!sc->vf_ifp && e1000_check_reset_block(hw)) device_printf(dev, "PHY reset is blocked due to SOL/IDER session.\n"); /* Sysctl for setting Energy Efficient Ethernet */ - if (hw->mac.type < igb_mac_min) - hw->dev_spec.ich8lan.eee_disable = eee_setting; - else - hw->dev_spec._82575.eee_disable = eee_setting; - SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "eee_control", - CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, - em_sysctl_eee, "I", "Disable Energy Efficient Ethernet"); + if (!sc->vf_ifp) { + if (hw->mac.type < igb_mac_min) + hw->dev_spec.ich8lan.eee_disable = eee_setting; + else + hw->dev_spec._82575.eee_disable = eee_setting; + SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "eee_control", + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, + em_sysctl_eee, "I", "Disable Energy Efficient Ethernet"); + } /* ** Start from a known state, this is @@ -1463,8 +1530,8 @@ em_if_attach_pre(if_ctx_t ctx) */ e1000_reset_hw(hw); - /* Make sure we have a good EEPROM before we read from it */ - if (e1000_validate_nvm_checksum(hw) < 0) { + /* Make sure a PF has a good EEPROM before we read from it. */ + if (!sc->vf_ifp && e1000_validate_nvm_checksum(hw) < 0) { /* ** Some PCI-E parts fail the first check due to ** the link being in sleep state, call it again, @@ -1497,20 +1564,23 @@ em_if_attach_pre(if_ctx_t ctx) } } - /* Save the EEPROM/NVM versions, must be done under IFLIB_CTX_LOCK */ - em_fw_version_locked(ctx); - - em_print_fw_version(sc); + if (!sc->vf_ifp) { + /* Save NVM versions while holding the IFLIB context lock. */ + em_fw_version_locked(ctx); + em_print_fw_version(sc); + } /* * Get Wake-on-Lan and Management info for later use */ - em_get_wakeup(ctx); + if (!sc->vf_ifp) { + em_get_wakeup(ctx); - /* Enable only WOL MAGIC by default */ - scctx->isc_capenable &= ~IFCAP_WOL; - if (sc->wol != 0) - scctx->isc_capenable |= IFCAP_WOL_MAGIC; + /* Enable only WOL MAGIC by default. */ + scctx->isc_capenable &= ~IFCAP_WOL; + if (sc->wol != 0) + scctx->isc_capenable |= IFCAP_WOL_MAGIC; + } iflib_set_mac(ctx, hw->mac.addr); @@ -1521,11 +1591,12 @@ err_late: err_pci: em_free_pci_resources(ctx); free(sc->mta, M_DEVBUF); + sc->mta = NULL; return (error); } -static int +int em_if_attach_post(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); @@ -1539,7 +1610,10 @@ em_if_attach_post(if_ctx_t ctx) goto err_late; } - em_reset(ctx); + if (sc->vf_ifp) + (void)igbv_reset(ctx); + else + em_reset(ctx); /* Initialize statistics */ if (sc->vf_ifp) @@ -1585,7 +1659,8 @@ em_if_detach(if_ctx_t ctx) INIT_DEBUGOUT("em_if_detach: begin"); igb_iov_detach(sc); - e1000_phy_hw_reset(&sc->hw); + if (!sc->vf_ifp) + e1000_phy_hw_reset(&sc->hw); em_release_manageability(sc); em_release_hw_control(sc); @@ -1728,7 +1803,10 @@ em_if_init(if_ctx_t ctx) /* Initialize the hardware */ igb_iov_reset_prepare(sc); - em_reset(ctx); + if (sc->vf_ifp) + (void)igbv_reset(ctx); + else + em_reset(ctx); /* Re-arm a link-up transition deferred for this reset. */ if (sc->link_state == EM_LINK_STATE_DOWN_RESET_PENDING || sc->link_state == EM_LINK_STATE_UP_RESET_PENDING) @@ -1749,8 +1827,9 @@ em_if_init(if_ctx_t ctx) txr->tx_cidx_processed = scctx->isc_ntxd[0] - 1; } - /* Setup VLAN support, basic and offload if available */ - E1000_WRITE_REG(&sc->hw, E1000_VET, ETHERTYPE_VLAN); + /* The VF VLAN EtherType is fixed and has no VET register. */ + if (!sc->vf_ifp) + E1000_WRITE_REG(&sc->hw, E1000_VET, ETHERTYPE_VLAN); /* Clear bad data from Rx FIFOs */ if (sc->hw.mac.type >= igb_mac_min && !sc->vf_ifp) @@ -1760,13 +1839,19 @@ em_if_init(if_ctx_t ctx) em_init_manageability(sc); /* Prepare transmit descriptors and buffers */ - em_initialize_transmit_unit(ctx); + if (sc->vf_ifp) + igbv_initialize_transmit_unit(ctx); + else + em_initialize_transmit_unit(ctx); /* Setup Multicast table */ em_if_multi_set(ctx); sc->rx_mbuf_sz = iflib_get_rx_mbuf_sz(ctx); - em_initialize_receive_unit(ctx); + if (sc->vf_ifp) + igbv_initialize_receive_unit(ctx); + else + em_initialize_receive_unit(ctx); /* Set up VLAN support and filter */ em_setup_vlan_hw_support(ctx); @@ -1796,9 +1881,11 @@ em_if_init(if_ctx_t ctx) if (sc->hw.mac.type >= igb_mac_min) igb_initialize_interrupt_rate(sc); - /* this clears any pending interrupts */ - E1000_READ_REG(&sc->hw, E1000_ICR); - E1000_WRITE_REG(&sc->hw, E1000_ICS, E1000_ICS_LSC); + if (!sc->vf_ifp) { + /* Clear pending PF interrupts and request a link check. */ + E1000_READ_REG(&sc->hw, E1000_ICR); + E1000_WRITE_REG(&sc->hw, E1000_ICS, E1000_ICS_LSC); + } /* AMT based hardware can now take control from firmware */ if (sc->has_manage && sc->has_amt) @@ -1952,7 +2039,8 @@ em_newitr(struct e1000_softc *sc, struct em_rx_queue *que, } else if (sc->link_speed < SPEED_1000) { /* Use half default (4K) ITR if sub-gig */ newitr = EM_INTS_4K; - } else if (sc->shared->isc_max_frame_size * 2 > (sc->pba << 10)) { + } else if (!sc->vf_ifp && + sc->shared->isc_max_frame_size * 2 > (sc->pba << 10)) { /* Want at least enough packet buffer for two frames to AIM */ newitr = em_max_interrupt_rate; } else { @@ -2522,16 +2610,6 @@ em_if_update_admin_status(if_ctx_t ctx) e1000_check_for_link(hw); link_check = hw->mac.serdes_has_link; break; - /* VF device is type_unknown */ - case e1000_media_type_unknown: - 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: break; } @@ -2568,7 +2646,8 @@ em_if_update_admin_status(if_ctx_t ctx) "Full Duplex" : "Half Duplex")); sc->link_state = EM_LINK_STATE_UP; sc->smartspeed = 0; - if ((ctrl & E1000_CTRL_EXT_LINK_MODE_MASK) == + if (hw->mac.type == e1000_i350 && + (ctrl & E1000_CTRL_EXT_LINK_MODE_MASK) == E1000_CTRL_EXT_LINK_MODE_GMII && (thstat & E1000_THSTAT_LINK_THROTTLE)) device_printf(dev, "Link: thermal downshift\n"); @@ -2665,8 +2744,10 @@ em_if_stop(if_ctx_t ctx) if (sc->hw.mac.type >= e1000_82544 && !sc->vf_ifp) E1000_WRITE_REG(&sc->hw, E1000_WUFC, 0); - e1000_led_off(&sc->hw); - e1000_cleanup_led(&sc->hw); + if (!sc->vf_ifp) { + e1000_led_off(&sc->hw); + e1000_cleanup_led(&sc->hw); + } } /********************************************************************* @@ -2696,12 +2777,15 @@ em_identify_hardware(if_ctx_t ctx) return; } - /* Are we a VF device? */ - if ((sc->hw.mac.type == e1000_vfadapt) || - (sc->hw.mac.type == e1000_vfadapt_i350)) - sc->vf_ifp = 1; - else - sc->vf_ifp = 0; + /* + * Function type comes from the selected iflib shared context, not from + * enum ordering. Keep the detected MAC type as an independent check + * that the igb/igbv probe tables selected the right policy. + */ + KASSERT(sc->vf_ifp == + (sc->hw.mac.type == e1000_vfadapt || + sc->hw.mac.type == e1000_vfadapt_i350), + ("%s: iflib function type and MAC type disagree", __func__)); } static int @@ -2879,8 +2963,8 @@ igb_configure_queues(struct e1000_softc *sc) sc->que_mask = 0; sc->link_mask = 0; - /* First turn on RSS capability */ - if (hw->mac.type != e1000_82575) + /* GPIE controls the PF interrupt block and is not in the VF BAR. */ + if (!sc->vf_ifp && hw->mac.type != e1000_82575) E1000_WRITE_REG(hw, E1000_GPIE, E1000_GPIE_MSIX_MODE | E1000_GPIE_EIAME | E1000_GPIE_PBA | E1000_GPIE_NSICR); @@ -3171,6 +3255,9 @@ igb_init_dmac(struct e1000_softc *sc, u32 pba) u16 hwm; u16 max_frame_size; + KASSERT(!sc->vf_ifp, ("%s: DMA coalescing requested for a VF", + __func__)); + if (hw->mac.type == e1000_i211) return; @@ -3401,6 +3488,8 @@ em_reset(if_ctx_t ctx) u32 pba; INIT_DEBUGOUT("em_reset: begin"); + KASSERT(!sc->vf_ifp, ("%s called for a VF", __func__)); + /* Let the firmware know the OS is in control */ em_get_hw_control(sc); @@ -3473,14 +3562,12 @@ em_reset(if_ctx_t ctx) pba = E1000_PBA_32K; break; case e1000_82576: - case e1000_vfadapt: pba = E1000_READ_REG(hw, E1000_RXPBS); pba &= E1000_RXPBS_SIZE_MASK_82576; break; case e1000_82580: case e1000_i350: case e1000_i354: - case e1000_vfadapt_i350: pba = E1000_READ_REG(hw, E1000_RXPBS); pba = e1000_rxpbs_adjust_82580(pba); break; @@ -3525,7 +3612,7 @@ em_reset(if_ctx_t ctx) if (hw->mac.type < igb_mac_min) E1000_WRITE_REG(hw, E1000_PBA, pba); - INIT_DEBUGOUT1("em_reset: pba=%dK",pba); + INIT_DEBUGOUT1("em_reset: pba=%dK", pba); /* * These parameters control the automatic generation (Tx) and @@ -3603,8 +3690,6 @@ em_reset(if_ctx_t ctx) case e1000_i354: case e1000_i210: case e1000_i211: - case e1000_vfadapt: - case e1000_vfadapt_i350: /* 16-byte granularity */ hw->fc.low_water = hw->fc.high_water - 16; break; @@ -3628,13 +3713,11 @@ em_reset(if_ctx_t ctx) /* Issue a global reset */ e1000_reset_hw(hw); - if (!sc->vf_ifp) { - if (hw->mac.type >= igb_mac_min) { - E1000_WRITE_REG(hw, E1000_WUC, 0); - } else { - E1000_WRITE_REG(hw, E1000_WUFC, 0); - em_disable_aspm(sc); - } + if (hw->mac.type >= igb_mac_min) { + E1000_WRITE_REG(hw, E1000_WUC, 0); + } else { + E1000_WRITE_REG(hw, E1000_WUFC, 0); + em_disable_aspm(sc); } if (sc->flags & IGB_MEDIA_RESET) { e1000_setup_init_funcs(hw, true); @@ -3649,7 +3732,7 @@ em_reset(if_ctx_t ctx) if (hw->mac.type >= igb_mac_min) igb_init_dmac(sc, pba); - /* Save the final PBA off if it needs to be used elsewhere i.e. AIM */ + /* Save the receive packet-buffer allocation for AIM. */ sc->pba = pba; E1000_WRITE_REG(hw, E1000_VET, ETHERTYPE_VLAN); @@ -3983,19 +4066,17 @@ em_if_queues_free(if_ctx_t ctx) * Enable transmit unit. * **********************************************************************/ -static void -em_initialize_transmit_unit(if_ctx_t ctx) +void +em_initialize_transmit_rings(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); if_softc_ctx_t scctx = sc->shared; struct em_tx_queue *que; struct tx_ring *txr; struct e1000_hw *hw = &sc->hw; - u32 tctl, txdctl = 0, tarc, tipg = 0; + u32 txdctl; - INIT_DEBUGOUT("em_initialize_transmit_unit: begin"); - - for (int i = 0; i < sc->tx_num_queues; i++, txr++) { + for (int i = 0; i < sc->tx_num_queues; i++) { u64 bus_addr; caddr_t offp, endp; uint32_t qid; @@ -4043,6 +4124,19 @@ em_initialize_transmit_unit(if_ctx_t ctx) E1000_WRITE_REG(hw, E1000_TXDCTL(qid), txdctl); } +} + +static void +em_initialize_transmit_unit(if_ctx_t ctx) +{ + struct e1000_softc *sc = iflib_get_softc(ctx); + struct e1000_hw *hw = &sc->hw; + u32 tctl, tarc, tipg = 0; + + INIT_DEBUGOUT("em_initialize_transmit_unit: begin"); + KASSERT(!sc->vf_ifp, ("%s called for a VF", __func__)); + + em_initialize_transmit_rings(ctx); /* Set the default values for the Tx Inter Packet Gap timer */ switch (hw->mac.type) { @@ -4133,6 +4227,55 @@ em_initialize_transmit_unit(if_ctx_t ctx) **********************************************************************/ #define BSIZEPKT_ROUNDUP ((1<<E1000_SRRCTL_BSIZEPKT_SHIFT)-1) +void +igb_initialize_receive_rings(if_ctx_t ctx, bool drop) +{ + struct e1000_softc *sc = iflib_get_softc(ctx); + if_softc_ctx_t scctx = sc->shared; + struct e1000_hw *hw = &sc->hw; + struct em_rx_queue *que; + u32 srrctl; + + srrctl = (sc->rx_mbuf_sz + BSIZEPKT_ROUNDUP) >> + E1000_SRRCTL_BSIZEPKT_SHIFT; + srrctl |= E1000_SRRCTL_DESCTYPE_ADV_ONEBUF; + if (drop) + srrctl |= E1000_SRRCTL_DROP_EN; + + for (int i = 0; i < sc->rx_num_queues; i++) { + struct rx_ring *rxr; + u64 bus_addr; + u32 rxdctl; + uint32_t qid; + + que = &sc->rx_queues[i]; + rxr = &que->rxr; + bus_addr = rxr->rx_paddr; + qid = rxr->me; + + rxdctl = E1000_READ_REG(hw, E1000_RXDCTL(qid)); + E1000_WRITE_REG(hw, E1000_RXDCTL(qid), + rxdctl & ~E1000_RXDCTL_QUEUE_ENABLE); + E1000_WRITE_FLUSH(hw); + + E1000_WRITE_REG(hw, E1000_RDLEN(qid), + scctx->isc_nrxd[0] * sizeof(struct e1000_rx_desc)); + E1000_WRITE_REG(hw, E1000_RDBAH(qid), + (uint32_t)(bus_addr >> 32)); + E1000_WRITE_REG(hw, E1000_RDBAL(qid), (uint32_t)bus_addr); + E1000_WRITE_REG(hw, E1000_RDH(qid), 0); + E1000_WRITE_REG(hw, E1000_RDT(qid), 0); + E1000_WRITE_REG(hw, E1000_SRRCTL(qid), srrctl); + + rxdctl |= E1000_RXDCTL_QUEUE_ENABLE; + rxdctl &= 0xFFF00000; + rxdctl |= IGB_RX_PTHRESH; + rxdctl |= IGB_RX_HTHRESH << 8; + rxdctl |= IGB_RX_WTHRESH << 16; + E1000_WRITE_REG(hw, E1000_RXDCTL(qid), rxdctl); + } +} + static void em_initialize_receive_unit(if_ctx_t ctx) { @@ -4145,32 +4288,29 @@ em_initialize_receive_unit(if_ctx_t ctx) uint32_t rctl, rxcsum; INIT_DEBUGOUT("em_initialize_receive_units: begin"); + KASSERT(!sc->vf_ifp, ("%s called for a VF", __func__)); /* - * Make sure receives are disabled while setting - * up the descriptor ring + * Make sure receives are disabled while setting up the descriptor + * ring. */ rctl = E1000_READ_REG(hw, E1000_RCTL); - /* Do not disable if ever enabled on this hardware */ - if ((hw->mac.type != e1000_82574) && (hw->mac.type != e1000_82583)) + /* Do not disable if ever enabled on this hardware. */ + if (hw->mac.type != e1000_82574 && + hw->mac.type != e1000_82583) E1000_WRITE_REG(hw, E1000_RCTL, rctl & ~E1000_RCTL_EN); - /* Setup the Receive Control Register */ + /* Setup the Receive Control Register. */ rctl &= ~(3 << E1000_RCTL_MO_SHIFT); rctl |= E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF | (hw->mac.mc_filter_type << E1000_RCTL_MO_SHIFT); - - /* Do not store bad packets */ rctl &= ~E1000_RCTL_SBP; - /* Enable Long Packet receive */ if (igb_iov_enabled(sc) || if_getmtu(ifp) > ETHERMTU) rctl |= E1000_RCTL_LPE; else rctl &= ~E1000_RCTL_LPE; - - /* Strip the CRC */ if (!em_disable_crc_stripping) rctl |= E1000_RCTL_SECRC; @@ -4211,7 +4351,7 @@ em_initialize_receive_unit(if_ctx_t ctx) sc->rx_int_delay.value); } - if (hw->mac.type >= em_mac_min && !sc->vf_ifp) { + if (hw->mac.type >= em_mac_min) { uint32_t rfctl; *** 490 LINES SKIPPED ***home | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a6ad8ab.3235a.3d9f3300>
