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
This commit is contained in:
Kevin Bowling committed 2026-07-29 21:52:39 -07:00
1 parent f879d1cd7d
commit 316f6f2f7f
7 files changed
+676 -254

No files matched your search

+2
View File
@@ -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 \
+83 -1
View File
@@ -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_ */
+5
View File
@@ -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++) {
+361 -252
View File
@@ -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,22 +1121,97 @@ 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)
/*
* 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_hw *hw;
struct sysctl_oid_list *child;
struct sysctl_ctx_list *ctx_list;
hw = &sc->hw;
ctx_list = device_get_sysctl_ctx(sc->dev);
child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
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, "debug",
CTLTYPE_INT | CTLFLAG_RW, sc, 0,
em_sysctl_debug_info, "I", "Debug Information");
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 (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");
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, "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");
if (hw->mac.type >= e1000_i350) {
SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "dmac",
CTLTYPE_INT | CTLFLAG_RW, sc, 0,
igb_sysctl_dmac, "I", "DMA Coalesce");
}
SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO,
"tso_tcp_flags_mask_first_segment",
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
@@ -1109,7 +1222,7 @@ em_set_num_queues(if_ctx_t ctx)
*
* return 0 on success, positive on failure
*********************************************************************/
static int
int
em_if_attach_pre(if_ctx_t ctx)
{
struct e1000_softc *sc;
@@ -1136,67 +1249,18 @@ em_if_attach_pre(if_ctx_t ctx)
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);
/* SYSCTL stuff */
/* 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));
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,
igb_sysctl_dmac, "I", "DMA Coalesce");
}
SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO,
"tso_tcp_flags_mask_first_segment",
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");
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;
/* Use extended rx descriptor formats */
rfctl = E1000_READ_REG(hw, E1000_RFCTL);
@@ -4231,40 +4371,36 @@ em_initialize_receive_unit(if_ctx_t ctx)
E1000_WRITE_REG(hw, E1000_RFCTL, rfctl);
}
/*
* Set up L3 and L4 csum Rx descriptor offloads only on Physical
* Functions. Virtual Functions have no access to this register.
*/
if (!sc->vf_ifp) {
rxcsum = E1000_READ_REG(hw, E1000_RXCSUM);
if (if_getcapenable(ifp) & IFCAP_RXCSUM) {
rxcsum |= E1000_RXCSUM_TUOFL | E1000_RXCSUM_IPOFL;
if (hw->mac.type > e1000_82575)
rxcsum |= E1000_RXCSUM_CRCOFL;
else if (hw->mac.type < em_mac_min &&
if_getcapenable(ifp) & IFCAP_HWCSUM_IPV6)
rxcsum |= E1000_RXCSUM_IPV6OFL;
} else {
rxcsum &= ~(E1000_RXCSUM_IPOFL | E1000_RXCSUM_TUOFL);
if (hw->mac.type > e1000_82575)
rxcsum &= ~E1000_RXCSUM_CRCOFL;
else if (hw->mac.type < em_mac_min)
rxcsum &= ~E1000_RXCSUM_IPV6OFL;
}
if (sc->rx_num_queues > 1) {
/* RSS hash needed in the Rx descriptor */
rxcsum |= E1000_RXCSUM_PCSD;
if (hw->mac.type >= igb_mac_min)
igb_initialize_rss_mapping(sc);
else
em_initialize_rss_mapping(sc);
}
E1000_WRITE_REG(hw, E1000_RXCSUM, rxcsum);
rxcsum = E1000_READ_REG(hw, E1000_RXCSUM);
if (if_getcapenable(ifp) & IFCAP_RXCSUM) {
rxcsum |= E1000_RXCSUM_TUOFL | E1000_RXCSUM_IPOFL;
if (hw->mac.type > e1000_82575)
rxcsum |= E1000_RXCSUM_CRCOFL;
else if (hw->mac.type < em_mac_min &&
if_getcapenable(ifp) & IFCAP_HWCSUM_IPV6)
rxcsum |= E1000_RXCSUM_IPV6OFL;
} else {
rxcsum &= ~(E1000_RXCSUM_IPOFL | E1000_RXCSUM_TUOFL);
if (hw->mac.type > e1000_82575)
rxcsum &= ~E1000_RXCSUM_CRCOFL;
else if (hw->mac.type < em_mac_min)
rxcsum &= ~E1000_RXCSUM_IPV6OFL;
}
for (i = 0, que = sc->rx_queues; i < sc->rx_num_queues; i++, que++) {
if (sc->rx_num_queues > 1) {
/* RSS hash needed in the Rx descriptor */
rxcsum |= E1000_RXCSUM_PCSD;
if (hw->mac.type >= igb_mac_min)
igb_initialize_rss_mapping(sc);
else
em_initialize_rss_mapping(sc);
}
E1000_WRITE_REG(hw, E1000_RXCSUM, rxcsum);
for (i = 0, que = sc->rx_queues;
hw->mac.type < igb_mac_min && i < sc->rx_num_queues;
i++, que++) {
struct rx_ring *rxr = &que->rxr;
/* Setup the Base and Length of the Rx Descriptor Ring */
u64 bus_addr = rxr->rx_paddr;
@@ -4304,12 +4440,13 @@ em_initialize_receive_unit(if_ctx_t ctx)
E1000_WRITE_REG(hw, E1000_RXDCTL(i), rxdctl);
}
} else if (hw->mac.type >= igb_mac_min) {
u32 psize, srrctl = 0;
bool drop;
u32 psize;
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) {
} else if (if_getmtu(ifp) > ETHERMTU) {
psize = scctx->isc_max_frame_size;
/* are we on a vlan? */
if (if_vlantrunkinuse(ifp))
@@ -4318,10 +4455,6 @@ em_initialize_receive_unit(if_ctx_t ctx)
E1000_WRITE_REG(hw, E1000_RLPML, psize);
}
/* Set maximum packet buffer len */
srrctl |= (sc->rx_mbuf_sz + BSIZEPKT_ROUNDUP) >>
E1000_SRRCTL_BSIZEPKT_SHIFT;
/*
* If TX flow control is disabled and there's >1 queue
* defined, enable DROP.
@@ -4329,49 +4462,11 @@ em_initialize_receive_unit(if_ctx_t ctx)
* This drops frames rather than hanging the RX MAC for all
* queues.
*/
if (igb_iov_enabled(sc) ||
drop = igb_iov_enabled(sc) ||
((sc->rx_num_queues > 1) &&
(sc->fc == e1000_fc_none ||
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++) {
struct rx_ring *rxr = &que->rxr;
u64 bus_addr = rxr->rx_paddr;
u32 rxdctl;
uint32_t qid = rxr->me;
#ifdef notyet
/* Configure for header split? -- ignore for now */
rxr->hdr_split = igb_header_split;
#else
srrctl |= E1000_SRRCTL_DESCTYPE_ADV_ONEBUF;
#endif
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);
/* Enable this Queue */
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);
}
sc->fc == e1000_fc_rx_pause));
igb_initialize_receive_rings(ctx, drop);
} else if (hw->mac.type >= e1000_pch2lan) {
if (if_getmtu(ifp) > ETHERMTU)
e1000_lv_jumbo_workaround_ich8lan(hw, true);
@@ -4870,8 +4965,6 @@ em_get_wakeup(if_ctx_t ctx)
switch (sc->hw.mac.type) {
case e1000_82542:
case e1000_82543:
case e1000_vfadapt:
case e1000_vfadapt_i350:
break;
case e1000_82544:
e1000_read_nvm(&sc->hw,
@@ -4987,6 +5080,8 @@ em_enable_wakeup(if_ctx_t ctx)
int error = 0;
u32 ctrl, ctrl_ext, rctl;
if (sc->vf_ifp)
return;
if (!pci_has_pm(dev))
return;
@@ -5502,20 +5597,22 @@ em_add_hw_stats(struct e1000_softc *sc)
SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "watchdog_timeouts",
CTLFLAG_RD, &sc->watchdog_events,
"Watchdog timeouts");
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "device_control",
CTLTYPE_UINT | CTLFLAG_RD,
sc, E1000_CTRL, em_sysctl_reg_handler, "IU",
"Device Control Register");
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rx_control",
CTLTYPE_UINT | CTLFLAG_RD,
sc, E1000_RCTL, em_sysctl_reg_handler, "IU",
"Receiver Control Register");
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "fc_high_water",
CTLFLAG_RD, &sc->hw.fc.high_water, 0,
"Flow Control High Watermark");
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "fc_low_water",
CTLFLAG_RD, &sc->hw.fc.low_water, 0,
"Flow Control Low Watermark");
if (!sc->vf_ifp) {
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "device_control",
CTLTYPE_UINT | CTLFLAG_RD,
sc, E1000_CTRL, em_sysctl_reg_handler, "IU",
"Device Control Register");
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rx_control",
CTLTYPE_UINT | CTLFLAG_RD,
sc, E1000_RCTL, em_sysctl_reg_handler, "IU",
"Receiver Control Register");
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "fc_high_water",
CTLFLAG_RD, &sc->hw.fc.high_water, 0,
"Flow Control High Watermark");
SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "fc_low_water",
CTLFLAG_RD, &sc->hw.fc.low_water, 0,
"Flow Control Low Watermark");
}
for (int i = 0; i < sc->tx_num_queues; i++, tx_que++) {
struct tx_ring *txr = &tx_que->txr;
@@ -5968,7 +6065,12 @@ em_print_nvm_info(struct e1000_softc *sc)
j = 0; ++row;
printf("\n0x00%x0 ",row);
}
e1000_read_nvm(hw, i, 1, &eeprom_data);
eeprom_data = 0;
if (e1000_read_nvm(hw, i, 1, &eeprom_data) !=
E1000_SUCCESS) {
printf("\nNVM read failed at offset %#x\n", i);
break;
}
printf("%04x ", eeprom_data);
}
sx_xunlock(iflib_ctx_lock);
@@ -6235,8 +6337,15 @@ em_print_debug_info(struct e1000_softc *sc)
{
device_t dev = iflib_get_dev(sc->ctx);
if_t ifp = iflib_get_ifp(sc->ctx);
struct tx_ring *txr = &sc->tx_queues->txr;
struct rx_ring *rxr = &sc->rx_queues->rxr;
struct tx_ring *txr;
struct rx_ring *rxr;
if (sc->tx_queues == NULL || sc->rx_queues == NULL) {
device_printf(dev, "queue state is unavailable\n");
return;
}
txr = &sc->tx_queues->txr;
rxr = &sc->rx_queues->rxr;
if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
printf("Interface is RUNNING ");
+19
View File
@@ -645,6 +645,25 @@ struct e1000_softc {
bool vf_reset_pending;
};
/*
* Shared PF/VF mechanisms and VF policy entry points. The latter live in
* if_igbv.c so the VF method table cannot accidentally select PF policy.
*/
int em_if_attach_pre(if_ctx_t);
int em_if_attach_post(if_ctx_t);
void em_add_device_sysctls(struct e1000_softc *);
void em_initialize_transmit_rings(if_ctx_t);
void igb_initialize_receive_rings(if_ctx_t, bool);
int igbv_get_regs(SYSCTL_HANDLER_ARGS);
int igbv_if_attach_pre(if_ctx_t);
int igbv_if_attach_post(if_ctx_t);
void igbv_if_intr_enable(if_ctx_t);
void igbv_if_intr_disable(if_ctx_t);
void igbv_initialize_receive_unit(if_ctx_t);
void igbv_initialize_transmit_unit(if_ctx_t);
bool igbv_reset(if_ctx_t);
/********************************************************************************
* vendor_info_array
*
+205
View File
@@ -0,0 +1,205 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2001-2024, Intel Corporation
* Copyright (c) 2026 Kevin Bowling <kbowling@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "if_em.h"
#include <sys/sbuf.h>
int
igbv_if_attach_pre(if_ctx_t ctx)
{
device_t dev;
int error;
dev = iflib_get_dev(ctx);
if (pci_msix_count(dev) < 2) {
device_printf(dev, "VF operation requires two MSI-X vectors\n");
return (ENXIO);
}
error = em_if_attach_pre(ctx);
if (error != 0)
return (error);
KASSERT(((struct e1000_softc *)iflib_get_softc(ctx))->vf_ifp &&
(iflib_get_sctx(ctx)->isc_flags & IFLIB_IS_VF) != 0,
("%s: igbv attached without VF policy", __func__));
return (0);
}
int
igbv_if_attach_post(if_ctx_t ctx)
{
struct e1000_softc *sc;
int error;
sc = iflib_get_softc(ctx);
KASSERT(sc->vf_ifp, ("%s called for a PF", __func__));
if (sc->intr_type != IFLIB_INTR_MSIX) {
device_printf(sc->dev, "VF operation requires MSI-X\n");
return (ENXIO);
}
error = em_if_attach_post(ctx);
if (error != 0)
return (error);
/*
* Attach failures can leave the device sysctl tree registered when
* hw.bus.disable_failed_devices is set. Do not publish handlers with
* softc arguments until iflib has successfully allocated MSI-X.
*/
em_add_device_sysctls(sc);
return (0);
}
bool
igbv_reset(if_ctx_t ctx)
{
struct e1000_softc *sc;
struct e1000_hw *hw;
sc = iflib_get_softc(ctx);
hw = &sc->hw;
KASSERT(sc->vf_ifp, ("%s called for a PF", __func__));
/*
* Receive-buffer allocation and flow control are port resources owned
* by the PF. Zero is an unavailable PBA sentinel, not a per-VF size.
*/
sc->pba = 0;
hw->fc = (struct e1000_fc_info){
.current_mode = e1000_fc_none,
.requested_mode = e1000_fc_none,
};
if (e1000_reset_hw(hw) != E1000_SUCCESS) {
e1000_check_for_link(hw);
return (false);
}
if (e1000_init_hw(hw) < 0) {
device_printf(sc->dev, "Hardware Initialization Failed\n");
return (false);
}
e1000_check_for_link(hw);
return (true);
}
void
igbv_initialize_transmit_unit(if_ctx_t ctx)
{
KASSERT(((struct e1000_softc *)iflib_get_softc(ctx))->vf_ifp,
("%s called for a PF", __func__));
em_initialize_transmit_rings(ctx);
}
void
igbv_initialize_receive_unit(if_ctx_t ctx)
{
KASSERT(((struct e1000_softc *)iflib_get_softc(ctx))->vf_ifp,
("%s called for a PF", __func__));
igb_initialize_receive_rings(ctx, true);
}
void
igbv_if_intr_enable(if_ctx_t ctx)
{
struct e1000_softc *sc;
struct e1000_hw *hw;
u32 mask;
sc = iflib_get_softc(ctx);
hw = &sc->hw;
KASSERT(sc->vf_ifp, ("%s called for a PF", __func__));
mask = sc->que_mask | sc->link_mask;
E1000_WRITE_REG(hw, E1000_EIAC, mask);
E1000_WRITE_REG(hw, E1000_EIAM, mask);
E1000_WRITE_REG(hw, E1000_EIMS, mask);
E1000_WRITE_FLUSH(hw);
}
void
igbv_if_intr_disable(if_ctx_t ctx)
{
struct e1000_softc *sc;
struct e1000_hw *hw;
sc = iflib_get_softc(ctx);
hw = &sc->hw;
KASSERT(sc->vf_ifp, ("%s called for a PF", __func__));
E1000_WRITE_REG(hw, E1000_EIMC, 0xffffffff);
E1000_WRITE_REG(hw, E1000_EIAC, 0);
E1000_WRITE_FLUSH(hw);
}
int
igbv_get_regs(SYSCTL_HANDLER_ARGS)
{
struct e1000_softc *sc;
struct e1000_hw *hw;
struct sbuf *sb;
int error;
sc = (struct e1000_softc *)arg1;
hw = &sc->hw;
KASSERT(sc->vf_ifp, ("%s called for a PF", __func__));
sb = sbuf_new_for_sysctl(NULL, NULL, 512, req);
if (sb == NULL)
return (ENOMEM);
/*
* Limited VF register set:
* Don't read EICR here because it is clear-on-read. The VF register
* file exposes its queue pair at index zero, so this diagnostic does
* not depend on the narrower lifetime of iflib's queue arrays.
*/
sbuf_printf(sb, "VF Registers\n");
sbuf_printf(sb, "\tVTCTRL\t %08x\n",
E1000_READ_REG(hw, E1000_CTRL));
sbuf_printf(sb, "\tSTATUS\t %08x\n",
E1000_READ_REG(hw, E1000_STATUS));
sbuf_printf(sb, "\tRDLEN\t %08x\n",
E1000_READ_REG(hw, E1000_RDLEN(0)));
sbuf_printf(sb, "\tRDH\t %08x\n",
E1000_READ_REG(hw, E1000_RDH(0)));
sbuf_printf(sb, "\tRDT\t %08x\n",
E1000_READ_REG(hw, E1000_RDT(0)));
sbuf_printf(sb, "\tTDLEN\t %08x\n",
E1000_READ_REG(hw, E1000_TDLEN(0)));
sbuf_printf(sb, "\tTDH\t %08x\n",
E1000_READ_REG(hw, E1000_TDH(0)));
sbuf_printf(sb, "\tTDT\t %08x\n",
E1000_READ_REG(hw, E1000_TDT(0)));
error = sbuf_finish(sb);
sbuf_delete(sb);
return (error);
}
+1 -1
View File
@@ -4,7 +4,7 @@ SRCS = device_if.h bus_if.h pci_if.h pci_iov_if.h opt_ddb.h opt_inet.h \
opt_inet6.h opt_rss.h ifdi_if.h
SRCS += $(CORE_SRC) $(LEGACY_SRC)
SRCS += $(COMMON_SHARED) $(LEGACY_SHARED) $(PCIE_SHARED)
CORE_SRC = if_em.c if_igb_iov.c em_txrx.c e1000_osdep.c
CORE_SRC = if_em.c if_igbv.c if_igb_iov.c em_txrx.c e1000_osdep.c
CORE_SRC += igb_txrx.c
COMMON_SHARED = e1000_api.c e1000_base.c e1000_phy.c e1000_nvm.c e1000_mac.c \
e1000_manage.c e1000_vf.c e1000_mbx.c e1000_i210.c