Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ixl/if_ixlv.c
/****************************************************************************** | /****************************************************************************** | ||||
Copyright (c) 2013-2015, Intel Corporation | Copyright (c) 2013-2017, Intel Corporation | ||||
All rights reserved. | All rights reserved. | ||||
Redistribution and use in source and binary forms, with or without | Redistribution and use in source and binary forms, with or without | ||||
modification, are permitted provided that the following conditions are met: | modification, are permitted provided that the following conditions are met: | ||||
1. Redistributions of source code must retain the above copyright notice, | 1. Redistributions of source code must retain the above copyright notice, | ||||
this list of conditions and the following disclaimer. | this list of conditions and the following disclaimer. | ||||
Show All 27 Lines | |||||
* Driver version | * Driver version | ||||
*********************************************************************/ | *********************************************************************/ | ||||
char ixlv_driver_version[] = "1.4.12-k"; | char ixlv_driver_version[] = "1.4.12-k"; | ||||
/********************************************************************* | /********************************************************************* | ||||
* PCI Device ID Table | * PCI Device ID Table | ||||
* | * | ||||
* Used by probe to select devices to load on | * Used by probe to select devices to load on | ||||
* Last field stores an index into ixlv_strings | |||||
* Last entry must be all 0s | |||||
* | * | ||||
* { Vendor ID, Device ID, SubVendor ID, SubDevice ID, String Index } | * ( Vendor ID, Device ID, Branding String ) | ||||
*********************************************************************/ | *********************************************************************/ | ||||
static ixl_vendor_info_t ixlv_vendor_info_array[] = | static pci_vendor_info_t ixlv_vendor_info_array[] = | ||||
{ | { | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_VF, 0, 0, 0}, | PVID(I40E_INTEL_VENDOR_ID, I40E_DEV_ID_VF, "Intel(R) Ethernet Connection 700 Series VF Driver"), | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_X722_VF, 0, 0, 0}, | PVID(I40E_INTEL_VENDOR_ID, I40E_DEV_ID_X722_VF, "Intel(R) Ethernet Connection 700 Series VF Driver"), | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_X722_A0_VF, 0, 0, 0}, | |||||
/* required last entry */ | /* required last entry */ | ||||
{0, 0, 0, 0, 0} | PVID_END | ||||
}; | }; | ||||
/********************************************************************* | /********************************************************************* | ||||
* Table of branding strings | |||||
*********************************************************************/ | |||||
static char *ixlv_strings[] = { | |||||
"Intel(R) Ethernet Connection XL710/X722 VF Driver" | |||||
}; | |||||
/********************************************************************* | |||||
* Function prototypes | * Function prototypes | ||||
*********************************************************************/ | *********************************************************************/ | ||||
static int ixlv_probe(device_t); | static void *ixlv_register(device_t dev); | ||||
static int ixlv_attach(device_t); | static int ixlv_if_attach_pre(if_ctx_t ctx); | ||||
static int ixlv_detach(device_t); | static int ixlv_if_attach_post(if_ctx_t ctx); | ||||
static int ixlv_shutdown(device_t); | static int ixlv_if_detach(if_ctx_t ctx); | ||||
static void ixlv_init_locked(struct ixlv_sc *); | static int ixlv_if_shutdown(if_ctx_t ctx); | ||||
static int ixlv_if_suspend(if_ctx_t ctx); | |||||
static int ixlv_if_resume(if_ctx_t ctx); | |||||
static int ixlv_if_msix_intr_assign(if_ctx_t ctx, int msix); | |||||
static void ixlv_if_enable_intr(if_ctx_t ctx); | |||||
static void ixlv_if_disable_intr(if_ctx_t ctx); | |||||
static int ixlv_if_queue_intr_enable(if_ctx_t ctx, uint16_t rxqid); | |||||
static int ixlv_if_tx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int ntxqs, int ntxqsets); | |||||
static int ixlv_if_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int nqs, int nqsets); | |||||
static void ixlv_if_queues_free(if_ctx_t ctx); | |||||
static void ixlv_if_update_admin_status(if_ctx_t ctx); | |||||
static void ixlv_if_multi_set(if_ctx_t ctx); | |||||
static int ixlv_if_mtu_set(if_ctx_t ctx, uint32_t mtu); | |||||
static void ixlv_if_media_status(if_ctx_t ctx, struct ifmediareq *ifmr); | |||||
static int ixlv_if_media_change(if_ctx_t ctx); | |||||
static int ixlv_if_promisc_set(if_ctx_t ctx, int flags); | |||||
static void ixlv_if_timer(if_ctx_t ctx, uint16_t qid); | |||||
static void ixlv_if_vlan_register(if_ctx_t ctx, u16 vtag); | |||||
static void ixlv_if_vlan_unregister(if_ctx_t ctx, u16 vtag); | |||||
static uint64_t ixlv_if_get_counter(if_ctx_t ctx, ift_counter cnt); | |||||
static void ixlv_if_stop(if_ctx_t ctx); | |||||
static int ixlv_allocate_pci_resources(struct ixlv_sc *); | static int ixlv_allocate_pci_resources(struct ixlv_sc *); | ||||
static int ixlv_reset_complete(struct i40e_hw *); | |||||
static int ixlv_setup_vc(struct ixlv_sc *); | |||||
static int ixlv_reset(struct ixlv_sc *); | |||||
static int ixlv_vf_config(struct ixlv_sc *); | |||||
static void ixlv_init_filters(struct ixlv_sc *); | |||||
static void ixlv_free_pci_resources(struct ixlv_sc *); | static void ixlv_free_pci_resources(struct ixlv_sc *); | ||||
static int ixlv_assign_msix(struct ixlv_sc *); | static void ixlv_free_filters(struct ixlv_sc *); | ||||
static int ixlv_init_msix(struct ixlv_sc *); | static void ixlv_setup_interface(device_t, struct ixl_vsi *); | ||||
static int ixlv_init_taskqueue(struct ixlv_sc *); | static void ixlv_add_sysctls(struct ixlv_sc *); | ||||
static int ixlv_setup_queues(struct ixlv_sc *); | static void ixlv_enable_adminq_irq(struct i40e_hw *); | ||||
static void ixlv_disable_adminq_irq(struct i40e_hw *); | |||||
static void ixlv_enable_queue_irq(struct i40e_hw *, int); | |||||
static void ixlv_disable_queue_irq(struct i40e_hw *, int); | |||||
static void ixlv_config_rss(struct ixlv_sc *); | static void ixlv_config_rss(struct ixlv_sc *); | ||||
static void ixlv_stop(struct ixlv_sc *); | static void ixlv_stop(struct ixlv_sc *); | ||||
static void ixlv_add_multi(struct ixl_vsi *); | //static void ixlv_add_multi(struct ixl_vsi *); | ||||
static void ixlv_del_multi(struct ixl_vsi *); | //static void ixlv_del_multi(struct ixl_vsi *); | ||||
static void ixlv_free_queues(struct ixl_vsi *); | |||||
static int ixlv_setup_interface(device_t, struct ixlv_sc *); | |||||
static int ixlv_teardown_adminq_msix(struct ixlv_sc *); | |||||
static int ixlv_media_change(struct ifnet *); | |||||
static void ixlv_media_status(struct ifnet *, struct ifmediareq *); | |||||
static void ixlv_local_timer(void *); | |||||
static int ixlv_add_mac_filter(struct ixlv_sc *, u8 *, u16); | static int ixlv_add_mac_filter(struct ixlv_sc *, u8 *, u16); | ||||
static int ixlv_del_mac_filter(struct ixlv_sc *sc, u8 *macaddr); | static int ixlv_del_mac_filter(struct ixlv_sc *sc, u8 *macaddr); | ||||
static void ixlv_init_filters(struct ixlv_sc *); | |||||
static void ixlv_free_filters(struct ixlv_sc *); | |||||
static void ixlv_msix_que(void *); | static int ixlv_msix_que(void *); | ||||
static void ixlv_msix_adminq(void *); | static int ixlv_msix_adminq(void *); | ||||
static void ixlv_do_adminq(void *, int); | //static void ixlv_do_adminq(void *, int); | ||||
static void ixlv_do_adminq_locked(struct ixlv_sc *sc); | static void ixlv_do_adminq_locked(struct ixlv_sc *sc); | ||||
static void ixlv_handle_que(void *, int); | //static void ixlv_handle_que(void *, int); | ||||
static int ixlv_reset(struct ixlv_sc *); | // TODO: Call this! | ||||
static int ixlv_reset_complete(struct i40e_hw *); | //static void ixlv_set_queue_rx_itr(struct ixl_rx_queue *); | ||||
static void ixlv_set_queue_rx_itr(struct ixl_queue *); | |||||
static void ixlv_set_queue_tx_itr(struct ixl_queue *); | |||||
static void ixl_init_cmd_complete(struct ixl_vc_cmd *, void *, | static void ixl_init_cmd_complete(struct ixl_vc_cmd *, void *, | ||||
enum i40e_status_code); | enum i40e_status_code); | ||||
static void ixlv_configure_itr(struct ixlv_sc *); | static void ixlv_configure_itr(struct ixlv_sc *); | ||||
static void ixlv_enable_adminq_irq(struct i40e_hw *); | |||||
static void ixlv_disable_adminq_irq(struct i40e_hw *); | |||||
static void ixlv_enable_queue_irq(struct i40e_hw *, int); | |||||
static void ixlv_disable_queue_irq(struct i40e_hw *, int); | |||||
static void ixlv_setup_vlan_filters(struct ixlv_sc *); | static void ixlv_setup_vlan_filters(struct ixlv_sc *); | ||||
static void ixlv_register_vlan(void *, struct ifnet *, u16); | |||||
static void ixlv_unregister_vlan(void *, struct ifnet *, u16); | |||||
static void ixlv_init_hw(struct ixlv_sc *); | // static void ixlv_init_hw(struct ixlv_sc *); | ||||
static int ixlv_setup_vc(struct ixlv_sc *); | |||||
static int ixlv_vf_config(struct ixlv_sc *); | |||||
static void ixlv_cap_txcsum_tso(struct ixl_vsi *, | |||||
struct ifnet *, int); | |||||
static void ixlv_add_sysctls(struct ixlv_sc *); | |||||
#ifdef IXL_DEBUG | #ifdef IXL_DEBUG | ||||
static int ixlv_sysctl_qtx_tail_handler(SYSCTL_HANDLER_ARGS); | static int ixlv_sysctl_qtx_tail_handler(SYSCTL_HANDLER_ARGS); | ||||
static int ixlv_sysctl_qrx_tail_handler(SYSCTL_HANDLER_ARGS); | static int ixlv_sysctl_qrx_tail_handler(SYSCTL_HANDLER_ARGS); | ||||
#endif | #endif | ||||
/********************************************************************* | /********************************************************************* | ||||
* FreeBSD Device Interface Entry Points | * FreeBSD Device Interface Entry Points | ||||
*********************************************************************/ | *********************************************************************/ | ||||
static device_method_t ixlv_methods[] = { | static device_method_t ixlv_methods[] = { | ||||
/* Device interface */ | /* Device interface */ | ||||
DEVMETHOD(device_probe, ixlv_probe), | DEVMETHOD(device_register, ixlv_register), | ||||
DEVMETHOD(device_attach, ixlv_attach), | DEVMETHOD(device_probe, iflib_device_probe), | ||||
DEVMETHOD(device_detach, ixlv_detach), | DEVMETHOD(device_attach, iflib_device_attach), | ||||
DEVMETHOD(device_shutdown, ixlv_shutdown), | DEVMETHOD(device_detach, iflib_device_detach), | ||||
{0, 0} | DEVMETHOD(device_shutdown, iflib_device_shutdown), | ||||
DEVMETHOD_END | |||||
}; | }; | ||||
static driver_t ixlv_driver = { | static driver_t ixlv_driver = { | ||||
"ixlv", ixlv_methods, sizeof(struct ixlv_sc), | "ixlv", ixlv_methods, sizeof(struct ixlv_sc), | ||||
}; | }; | ||||
devclass_t ixlv_devclass; | devclass_t ixlv_devclass; | ||||
DRIVER_MODULE(ixlv, pci, ixlv_driver, ixlv_devclass, 0, 0); | DRIVER_MODULE(ixlv, pci, ixlv_driver, ixlv_devclass, 0, 0); | ||||
MODULE_DEPEND(ixlv, pci, 1, 1, 1); | MODULE_DEPEND(ixlv, pci, 1, 1, 1); | ||||
MODULE_DEPEND(ixlv, ether, 1, 1, 1); | MODULE_DEPEND(ixlv, ether, 1, 1, 1); | ||||
MODULE_DEPEND(ixlv, iflib, 1, 1, 1); | |||||
/* | static device_method_t ixlv_if_methods[] = { | ||||
DEVMETHOD(ifdi_attach_pre, ixlv_if_attach_pre), | |||||
DEVMETHOD(ifdi_attach_post, ixlv_if_attach_post), | |||||
DEVMETHOD(ifdi_detach, ixlv_if_detach), | |||||
DEVMETHOD(ifdi_shutdown, ixlv_if_shutdown), | |||||
DEVMETHOD(ifdi_suspend, ixlv_if_suspend), | |||||
DEVMETHOD(ifdi_resume, ixlv_if_resume), | |||||
DEVMETHOD(ifdi_init, ixlv_if_init), | |||||
DEVMETHOD(ifdi_stop, ixlv_if_stop), | |||||
DEVMETHOD(ifdi_msix_intr_assign, ixlv_if_msix_intr_assign), | |||||
DEVMETHOD(ifdi_intr_enable, ixlv_if_enable_intr), | |||||
DEVMETHOD(ifdi_intr_disable, ixlv_if_disable_intr), | |||||
DEVMETHOD(ifdi_queue_intr_enable, ixlv_if_queue_intr_enable), | |||||
DEVMETHOD(ifdi_tx_queues_alloc, ixlv_if_tx_queues_alloc), | |||||
DEVMETHOD(ifdi_rx_queues_alloc, ixlv_if_rx_queues_alloc), | |||||
DEVMETHOD(ifdi_queues_free, ixlv_if_queues_free), | |||||
DEVMETHOD(ifdi_update_admin_status, ixlv_if_update_admin_status), | |||||
DEVMETHOD(ifdi_multi_set, ixlv_if_multi_set), | |||||
DEVMETHOD(ifdi_mtu_set, ixlv_if_mtu_set), | |||||
// DEVMETHOD(ifdi_crcstrip_set, ixlv_if_crcstrip_set), | |||||
DEVMETHOD(ifdi_media_status, ixlv_if_media_status), | |||||
DEVMETHOD(ifdi_media_change, ixlv_if_media_change), | |||||
DEVMETHOD(ifdi_promisc_set, ixlv_if_promisc_set), | |||||
DEVMETHOD(ifdi_timer, ixlv_if_timer), | |||||
DEVMETHOD(ifdi_vlan_register, ixlv_if_vlan_register), | |||||
DEVMETHOD(ifdi_vlan_unregister, ixlv_if_vlan_unregister), | |||||
DEVMETHOD(ifdi_get_counter, ixlv_if_get_counter), | |||||
DEVMETHOD_END | |||||
}; | |||||
static driver_t ixlv_if_driver = { | |||||
"ixlv_if", ixlv_if_methods, sizeof(struct ixlv_sc) | |||||
}; | |||||
/***************************************************************************** | |||||
** TUNEABLE PARAMETERS: | ** TUNEABLE PARAMETERS: | ||||
*/ | *****************************************************************************/ | ||||
static SYSCTL_NODE(_hw, OID_AUTO, ixlv, CTLFLAG_RD, 0, | static SYSCTL_NODE(_hw, OID_AUTO, ixlv, CTLFLAG_RD, 0, | ||||
"IXLV driver parameters"); | "IXLV driver parameters"); | ||||
/* | /* | ||||
** Number of descriptors per ring: | ** Number of descriptors per ring: | ||||
** - TX and RX are the same size | ** - TX and RX are the same size | ||||
*/ | */ | ||||
static int ixlv_ringsz = IXL_DEFAULT_RING; | static int ixlv_ring_size = IXL_DEFAULT_RING; | ||||
TUNABLE_INT("hw.ixlv.ringsz", &ixlv_ringsz); | TUNABLE_INT("hw.ixlv.ring_size", &ixlv_ring_size); | ||||
SYSCTL_INT(_hw_ixlv, OID_AUTO, ring_size, CTLFLAG_RDTUN, | SYSCTL_INT(_hw_ixlv, OID_AUTO, ring_size, CTLFLAG_RDTUN, | ||||
&ixlv_ringsz, 0, "Descriptor Ring Size"); | &ixlv_ring_size, 0, "Descriptor Ring Size"); | ||||
/* Set to zero to auto calculate */ | /* Set to zero to auto calculate */ | ||||
int ixlv_max_queues = 0; | int ixlv_max_queues = 0; | ||||
TUNABLE_INT("hw.ixlv.max_queues", &ixlv_max_queues); | TUNABLE_INT("hw.ixlv.max_queues", &ixlv_max_queues); | ||||
SYSCTL_INT(_hw_ixlv, OID_AUTO, max_queues, CTLFLAG_RDTUN, | SYSCTL_INT(_hw_ixlv, OID_AUTO, max_queues, CTLFLAG_RDTUN, | ||||
&ixlv_max_queues, 0, "Number of Queues"); | &ixlv_max_queues, 0, "Number of Queues"); | ||||
/* | /* | ||||
** Number of entries in Tx queue buf_ring. | |||||
** Increasing this will reduce the number of | |||||
** errors when transmitting fragmented UDP | |||||
** packets. | |||||
*/ | |||||
static int ixlv_txbrsz = DEFAULT_TXBRSZ; | |||||
TUNABLE_INT("hw.ixlv.txbrsz", &ixlv_txbrsz); | |||||
SYSCTL_INT(_hw_ixlv, OID_AUTO, txbr_size, CTLFLAG_RDTUN, | |||||
&ixlv_txbrsz, 0, "TX Buf Ring Size"); | |||||
/* | |||||
** Controls for Interrupt Throttling | ** Controls for Interrupt Throttling | ||||
** - true/false for dynamic adjustment | ** - true/false for dynamic adjustment | ||||
** - default values for static ITR | ** - default values for static ITR | ||||
*/ | */ | ||||
int ixlv_dynamic_rx_itr = 0; | int ixlv_dynamic_rx_itr = 0; | ||||
TUNABLE_INT("hw.ixlv.dynamic_rx_itr", &ixlv_dynamic_rx_itr); | TUNABLE_INT("hw.ixlv.dynamic_rx_itr", &ixlv_dynamic_rx_itr); | ||||
SYSCTL_INT(_hw_ixlv, OID_AUTO, dynamic_rx_itr, CTLFLAG_RDTUN, | SYSCTL_INT(_hw_ixlv, OID_AUTO, dynamic_rx_itr, CTLFLAG_RDTUN, | ||||
&ixlv_dynamic_rx_itr, 0, "Dynamic RX Interrupt Rate"); | &ixlv_dynamic_rx_itr, 0, "Dynamic RX Interrupt Rate"); | ||||
int ixlv_dynamic_tx_itr = 0; | |||||
TUNABLE_INT("hw.ixlv.dynamic_tx_itr", &ixlv_dynamic_tx_itr); | |||||
SYSCTL_INT(_hw_ixlv, OID_AUTO, dynamic_tx_itr, CTLFLAG_RDTUN, | |||||
&ixlv_dynamic_tx_itr, 0, "Dynamic TX Interrupt Rate"); | |||||
int ixlv_rx_itr = IXL_ITR_8K; | int ixlv_rx_itr = IXL_ITR_8K; | ||||
TUNABLE_INT("hw.ixlv.rx_itr", &ixlv_rx_itr); | TUNABLE_INT("hw.ixlv.rx_itr", &ixlv_rx_itr); | ||||
SYSCTL_INT(_hw_ixlv, OID_AUTO, rx_itr, CTLFLAG_RDTUN, | SYSCTL_INT(_hw_ixlv, OID_AUTO, rx_itr, CTLFLAG_RDTUN, | ||||
&ixlv_rx_itr, 0, "RX Interrupt Rate"); | &ixlv_rx_itr, 0, "RX Interrupt Rate"); | ||||
int ixlv_tx_itr = IXL_ITR_4K; | extern struct if_txrx ixl_txrx; | ||||
TUNABLE_INT("hw.ixlv.tx_itr", &ixlv_tx_itr); | |||||
SYSCTL_INT(_hw_ixlv, OID_AUTO, tx_itr, CTLFLAG_RDTUN, | |||||
&ixlv_tx_itr, 0, "TX Interrupt Rate"); | |||||
/********************************************************************* | static struct if_shared_ctx ixlv_sctx_init = { | ||||
* Device identification routine | .isc_magic = IFLIB_MAGIC, | ||||
* | .isc_q_align = PAGE_SIZE,/* max(DBA_ALIGN, PAGE_SIZE) */ | ||||
* ixlv_probe determines if the driver should be loaded on | .isc_tx_maxsize = IXL_TSO_SIZE, | ||||
* the hardware based on PCI vendor/device id of the device. | |||||
* | |||||
* return BUS_PROBE_DEFAULT on success, positive on failure | |||||
*********************************************************************/ | |||||
static int | .isc_tx_maxsegsize = PAGE_SIZE, | ||||
ixlv_probe(device_t dev) | |||||
{ | |||||
ixl_vendor_info_t *ent; | |||||
u16 pci_vendor_id, pci_device_id; | // TODO: Review the rx_maxsize and rx_maxsegsize params | ||||
u16 pci_subvendor_id, pci_subdevice_id; | // Where are they used in iflib? | ||||
char device_name[256]; | .isc_rx_maxsize = 16384, | ||||
.isc_rx_nsegments = 1, | |||||
.isc_rx_maxsegsize = 16384, | |||||
// TODO: What is isc_nfl for? | |||||
.isc_nfl = 1, | |||||
.isc_ntxqs = 1, | |||||
.isc_nrxqs = 1, | |||||
#if 0 | .isc_admin_intrcnt = 1, | ||||
INIT_DEBUGOUT("ixlv_probe: begin"); | .isc_vendor_info = ixlv_vendor_info_array, | ||||
#endif | .isc_driver_version = ixlv_driver_version, | ||||
.isc_driver = &ixlv_if_driver, | |||||
pci_vendor_id = pci_get_vendor(dev); | .isc_nrxd_min = {IXL_MIN_RING}, | ||||
if (pci_vendor_id != I40E_INTEL_VENDOR_ID) | .isc_ntxd_min = {IXL_MIN_RING}, | ||||
return (ENXIO); | .isc_nrxd_max = {IXL_MAX_RING}, | ||||
.isc_ntxd_max = {IXL_MAX_RING}, | |||||
.isc_nrxd_default = {IXL_DEFAULT_RING}, | |||||
.isc_ntxd_default = {IXL_DEFAULT_RING}, | |||||
}; | |||||
pci_device_id = pci_get_device(dev); | if_shared_ctx_t ixlv_sctx = &ixlv_sctx_init; | ||||
pci_subvendor_id = pci_get_subvendor(dev); | |||||
pci_subdevice_id = pci_get_subdevice(dev); | |||||
ent = ixlv_vendor_info_array; | /*** Functions ***/ | ||||
while (ent->vendor_id != 0) { | |||||
if ((pci_vendor_id == ent->vendor_id) && | |||||
(pci_device_id == ent->device_id) && | |||||
((pci_subvendor_id == ent->subvendor_id) || | static void * | ||||
(ent->subvendor_id == 0)) && | ixlv_register(device_t dev) | ||||
{ | |||||
((pci_subdevice_id == ent->subdevice_id) || | return (ixlv_sctx); | ||||
(ent->subdevice_id == 0))) { | |||||
sprintf(device_name, "%s, Version - %s", | |||||
ixlv_strings[ent->index], | |||||
ixlv_driver_version); | |||||
device_set_desc_copy(dev, device_name); | |||||
return (BUS_PROBE_DEFAULT); | |||||
} | } | ||||
ent++; | |||||
} | |||||
return (ENXIO); | |||||
} | |||||
/********************************************************************* | |||||
* 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 | static int | ||||
ixlv_attach(device_t dev) | ixlv_if_attach_pre(if_ctx_t ctx) | ||||
{ | { | ||||
device_t dev; | |||||
struct ixlv_sc *sc; | struct ixlv_sc *sc; | ||||
struct i40e_hw *hw; | struct i40e_hw *hw; | ||||
struct ixl_vsi *vsi; | struct ixl_vsi *vsi; | ||||
if_softc_ctx_t scctx; | |||||
int error = 0; | int error = 0; | ||||
INIT_DBG_DEV(dev, "begin"); | INIT_DBG_DEV(dev, "begin"); | ||||
/* Allocate, clear, and link in our primary soft structure */ | dev = iflib_get_dev(ctx); | ||||
sc = device_get_softc(dev); | sc = iflib_get_softc(ctx); | ||||
sc->dev = sc->osdep.dev = dev; | |||||
hw = &sc->hw; | hw = &sc->hw; | ||||
/* | |||||
** Note this assumes we have a single embedded VSI, | |||||
** this could be enhanced later to allocate multiple | |||||
*/ | |||||
vsi = &sc->vsi; | vsi = &sc->vsi; | ||||
vsi->dev = dev; | vsi->back = sc; | ||||
vsi->hw = &sc->hw; | |||||
// vsi->id = 0; | |||||
vsi->num_vlans = 0; | |||||
vsi->ctx = ctx; | |||||
vsi->media = iflib_get_media(ctx); | |||||
vsi->shared = scctx = iflib_get_softc_ctx(ctx); | |||||
sc->dev = dev; | |||||
/* Initialize hw struct */ | /* | ||||
ixlv_init_hw(sc); | * These are the same across all current ixl models | ||||
*/ | |||||
vsi->shared->isc_tx_nsegments = IXL_MAX_TX_SEGS; | |||||
vsi->shared->isc_msix_bar = PCIR_BAR(IXL_MSIX_BAR); | |||||
/* Allocate filter lists */ | vsi->shared->isc_tx_tso_segments_max = IXL_MAX_TSO_SEGS; | ||||
ixlv_init_filters(sc); | vsi->shared->isc_tx_tso_size_max = IXL_TSO_SIZE; | ||||
vsi->shared->isc_tx_tso_segsize_max = PAGE_SIZE; | |||||
/* Core Lock Init */ | #if 0 // TODO: Implement | ||||
mtx_init(&sc->mtx, device_get_nameunit(dev), | /* Save tunable values */ | ||||
"IXL SC Lock", MTX_DEF); | error = ixl_save_pf_tunables(pf); | ||||
if (error) | |||||
return (error); | |||||
#endif | |||||
/* Set up the timer callout */ | scctx->isc_txqsizes[0] = roundup2(scctx->isc_ntxd[0] | ||||
callout_init_mtx(&sc->timer, &sc->mtx, 0); | * sizeof(struct i40e_tx_desc) + sizeof(u32), DBA_ALIGN); | ||||
scctx->isc_rxqsizes[0] = roundup2(scctx->isc_nrxd[0] | |||||
* sizeof(union i40e_32byte_rx_desc), DBA_ALIGN); | |||||
/* XXX: No idea what this does */ | |||||
/* TODO: This value may depend on resources received */ | |||||
scctx->isc_max_txqsets = scctx->isc_max_rxqsets = 16; | |||||
/* Do PCI setup - map BAR0, etc */ | /* Do PCI setup - map BAR0, etc */ | ||||
if (ixlv_allocate_pci_resources(sc)) { | if (ixlv_allocate_pci_resources(sc)) { | ||||
device_printf(dev, "%s: Allocation of PCI resources failed\n", | device_printf(dev, "%s: Allocation of PCI resources failed\n", | ||||
__func__); | __func__); | ||||
error = ENXIO; | error = ENXIO; | ||||
goto err_early; | goto err_early; | ||||
} | } | ||||
INIT_DBG_DEV(dev, "Allocated PCI resources and MSIX vectors"); | INIT_DBG_DEV(dev, "Allocated PCI resources and MSIX vectors"); | ||||
/* XXX: This is called by init_shared_code in the PF driver */ | |||||
error = i40e_set_mac_type(hw); | error = i40e_set_mac_type(hw); | ||||
if (error) { | if (error) { | ||||
device_printf(dev, "%s: set_mac_type failed: %d\n", | device_printf(dev, "%s: set_mac_type failed: %d\n", | ||||
__func__, error); | __func__, error); | ||||
goto err_pci_res; | goto err_pci_res; | ||||
} | } | ||||
error = ixlv_reset_complete(hw); | error = ixlv_reset_complete(hw); | ||||
if (error) { | if (error) { | ||||
device_printf(dev, "%s: Device is still being reset\n", | device_printf(dev, "%s: Device is still being reset\n", | ||||
__func__); | __func__); | ||||
goto err_pci_res; | goto err_pci_res; | ||||
} | } | ||||
INIT_DBG_DEV(dev, "VF Device is ready for configuration"); | INIT_DBG_DEV(dev, "VF Device is ready for configuration"); | ||||
/* Sets up Admin Queue */ | |||||
error = ixlv_setup_vc(sc); | error = ixlv_setup_vc(sc); | ||||
if (error) { | if (error) { | ||||
device_printf(dev, "%s: Error setting up PF comms, %d\n", | device_printf(dev, "%s: Error setting up PF comms, %d\n", | ||||
__func__, error); | __func__, error); | ||||
goto err_pci_res; | goto err_pci_res; | ||||
} | } | ||||
INIT_DBG_DEV(dev, "PF API version verified"); | INIT_DBG_DEV(dev, "PF API version verified"); | ||||
Show All 31 Lines | for (int i = 0; i < sc->vf_res->num_vsis; i++) { | ||||
if (sc->vf_res->vsi_res[i].vsi_type == I40E_VSI_SRIOV) | if (sc->vf_res->vsi_res[i].vsi_type == I40E_VSI_SRIOV) | ||||
sc->vsi_res = &sc->vf_res->vsi_res[i]; | sc->vsi_res = &sc->vf_res->vsi_res[i]; | ||||
} | } | ||||
if (!sc->vsi_res) { | if (!sc->vsi_res) { | ||||
device_printf(dev, "%s: no LAN VSI found\n", __func__); | device_printf(dev, "%s: no LAN VSI found\n", __func__); | ||||
error = EIO; | error = EIO; | ||||
goto err_res_buf; | goto err_res_buf; | ||||
} | } | ||||
vsi->id = sc->vsi_res->vsi_id; | |||||
INIT_DBG_DEV(dev, "Resource Acquisition complete"); | INIT_DBG_DEV(dev, "Resource Acquisition complete"); | ||||
/* If no mac address was assigned just make a random one */ | /* If no mac address was assigned just make a random one */ | ||||
if (!ixlv_check_ether_addr(hw->mac.addr)) { | if (!ixlv_check_ether_addr(hw->mac.addr)) { | ||||
u8 addr[ETHER_ADDR_LEN]; | u8 addr[ETHER_ADDR_LEN]; | ||||
arc4rand(&addr, sizeof(addr), 0); | arc4rand(&addr, sizeof(addr), 0); | ||||
addr[0] &= 0xFE; | addr[0] &= 0xFE; | ||||
addr[0] |= 0x02; | addr[0] |= 0x02; | ||||
bcopy(addr, hw->mac.addr, sizeof(addr)); | bcopy(addr, hw->mac.addr, sizeof(addr)); | ||||
} | } | ||||
bcopy(hw->mac.addr, hw->mac.perm_addr, ETHER_ADDR_LEN); | |||||
iflib_set_mac(ctx, hw->mac.addr); | |||||
/* Now that the number of queues for this VF is known, set up interrupts */ | /* Allocate filter lists */ | ||||
sc->msix = ixlv_init_msix(sc); | ixlv_init_filters(sc); | ||||
/* We fail without MSIX support */ | |||||
if (sc->msix == 0) { | |||||
error = ENXIO; | |||||
goto err_res_buf; | |||||
} | |||||
vsi->id = sc->vsi_res->vsi_id; | /* Fill out more iflib parameters */ | ||||
vsi->back = (void *)sc; | scctx->isc_txrx = &ixl_txrx; | ||||
sc->link_up = TRUE; | // TODO: Probably needs changing | ||||
vsi->shared->isc_rss_table_size = sc->hw.func_caps.rss_table_size; | |||||
scctx->isc_tx_csum_flags = CSUM_OFFLOAD; | |||||
scctx->isc_capenable = IXL_CAPS; | |||||
/* This allocates the memory and early settings */ | INIT_DBG_DEV(dev, "end"); | ||||
if (ixlv_setup_queues(sc) != 0) { | return (0); | ||||
device_printf(dev, "%s: setup queues failed!\n", | |||||
__func__); | |||||
error = EIO; | |||||
goto out; | |||||
} | |||||
/* Setup the stack interface */ | err_res_buf: | ||||
if (ixlv_setup_interface(dev, sc) != 0) { | free(sc->vf_res, M_DEVBUF); | ||||
device_printf(dev, "%s: setup interface failed!\n", | err_aq: | ||||
__func__); | i40e_shutdown_adminq(hw); | ||||
error = EIO; | err_pci_res: | ||||
goto out; | ixlv_free_pci_resources(sc); | ||||
err_early: | |||||
ixlv_free_filters(sc); | |||||
INIT_DBG_DEV(dev, "end: error %d", error); | |||||
return (error); | |||||
} | } | ||||
INIT_DBG_DEV(dev, "Queue memory and interface setup"); | static int | ||||
ixlv_if_attach_post(if_ctx_t ctx) | |||||
{ | |||||
device_t dev; | |||||
struct ixlv_sc *sc; | |||||
struct i40e_hw *hw; | |||||
struct ixl_vsi *vsi; | |||||
int error = 0; | |||||
/* Do queue interrupt setup */ | INIT_DBG_DEV(dev, "begin"); | ||||
if (ixlv_assign_msix(sc) != 0) { | |||||
device_printf(dev, "%s: allocating queue interrupts failed!\n", | |||||
__func__); | |||||
error = ENXIO; | |||||
goto out; | |||||
} | |||||
/* Start AdminQ taskqueue */ | dev = iflib_get_dev(ctx); | ||||
ixlv_init_taskqueue(sc); | vsi = iflib_get_softc(ctx); | ||||
vsi->ifp = iflib_get_ifp(ctx); | |||||
sc = (struct ixlv_sc *)vsi->back; | |||||
hw = &sc->hw; | |||||
/* Initialize stats */ | /* Setup the stack interface */ | ||||
ixlv_setup_interface(dev, vsi); | |||||
INIT_DBG_DEV(dev, "OS network interface set up."); | |||||
/* Initialize statistics & add sysctls */ | |||||
bzero(&sc->vsi.eth_stats, sizeof(struct i40e_eth_stats)); | bzero(&sc->vsi.eth_stats, sizeof(struct i40e_eth_stats)); | ||||
ixlv_add_sysctls(sc); | ixlv_add_sysctls(sc); | ||||
/* TODO: Add device sysctls? Like what? */ | |||||
/* Register for VLAN events */ | |||||
vsi->vlan_attach = EVENTHANDLER_REGISTER(vlan_config, | |||||
ixlv_register_vlan, vsi, EVENTHANDLER_PRI_FIRST); | |||||
vsi->vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig, | |||||
ixlv_unregister_vlan, vsi, EVENTHANDLER_PRI_FIRST); | |||||
/* We want AQ enabled early */ | /* We want AQ enabled early */ | ||||
ixlv_enable_adminq_irq(hw); | ixlv_enable_adminq_irq(hw); | ||||
/* Set things up to run init */ | /* Set things up to run init */ | ||||
sc->init_state = IXLV_INIT_READY; | sc->init_state = IXLV_INIT_READY; | ||||
ixl_vc_init_mgr(sc, &sc->vc_mgr); | ixl_vc_init_mgr(sc, &sc->vc_mgr); | ||||
INIT_DBG_DEV(dev, "end"); | INIT_DBG_DEV(dev, "end"); | ||||
return (error); | return (error); | ||||
// TODO: Check if any failures can happen above | |||||
#if 0 | |||||
out: | out: | ||||
ixlv_free_queues(vsi); | |||||
err_res_buf: | |||||
free(sc->vf_res, M_DEVBUF); | free(sc->vf_res, M_DEVBUF); | ||||
err_aq: | |||||
i40e_shutdown_adminq(hw); | i40e_shutdown_adminq(hw); | ||||
err_pci_res: | |||||
ixlv_free_pci_resources(sc); | ixlv_free_pci_resources(sc); | ||||
err_early: | |||||
mtx_destroy(&sc->mtx); | |||||
ixlv_free_filters(sc); | ixlv_free_filters(sc); | ||||
INIT_DBG_DEV(dev, "end: error %d", error); | INIT_DBG_DEV(dev, "end: error %d", error); | ||||
return (error); | return (error); | ||||
#endif | |||||
} | } | ||||
/********************************************************************* | |||||
* Device removal routine | |||||
* | |||||
* The detach entry point is called when the driver is being removed. | |||||
* This routine stops the adapter and deallocates all the resources | |||||
* that were allocated for driver operation. | |||||
* | |||||
* return 0 on success, positive on failure | |||||
*********************************************************************/ | |||||
static int | static int | ||||
ixlv_detach(device_t dev) | ixlv_if_detach(if_ctx_t ctx) | ||||
{ | { | ||||
struct ixlv_sc *sc = device_get_softc(dev); | struct ixl_vsi *vsi = iflib_get_softc(ctx); | ||||
struct ixl_vsi *vsi = &sc->vsi; | struct ixlv_sc *sc = vsi->back; | ||||
struct i40e_hw *hw = &sc->hw; | struct i40e_hw *hw = &sc->hw; | ||||
device_t dev = sc->dev; | |||||
enum i40e_status_code status; | enum i40e_status_code status; | ||||
INIT_DBG_DEV(dev, "begin"); | INIT_DBG_DEV(dev, "begin"); | ||||
/* Make sure VLANS are not using driver */ | |||||
if (vsi->ifp->if_vlantrunk != NULL) { | |||||
if_printf(vsi->ifp, "Vlan in use, detach first\n"); | |||||
return (EBUSY); | |||||
} | |||||
/* Stop driver */ | |||||
ether_ifdetach(vsi->ifp); | |||||
if (vsi->ifp->if_drv_flags & IFF_DRV_RUNNING) { | |||||
mtx_lock(&sc->mtx); | |||||
ixlv_stop(sc); | |||||
mtx_unlock(&sc->mtx); | |||||
} | |||||
/* Unregister VLAN events */ | |||||
if (vsi->vlan_attach != NULL) | |||||
EVENTHANDLER_DEREGISTER(vlan_config, vsi->vlan_attach); | |||||
if (vsi->vlan_detach != NULL) | |||||
EVENTHANDLER_DEREGISTER(vlan_unconfig, vsi->vlan_detach); | |||||
/* Drain VC mgr */ | /* Drain VC mgr */ | ||||
callout_drain(&sc->vc_mgr.callout); | callout_drain(&sc->vc_mgr.callout); | ||||
ixlv_disable_adminq_irq(hw); | ixlv_disable_adminq_irq(hw); | ||||
ixlv_teardown_adminq_msix(sc); | |||||
/* Drain admin queue taskqueue */ | |||||
taskqueue_free(sc->tq); | |||||
status = i40e_shutdown_adminq(&sc->hw); | status = i40e_shutdown_adminq(&sc->hw); | ||||
if (status != I40E_SUCCESS) { | if (status != I40E_SUCCESS) { | ||||
device_printf(dev, | device_printf(dev, | ||||
"i40e_shutdown_adminq() failed with status %s\n", | "i40e_shutdown_adminq() failed with status %s\n", | ||||
i40e_stat_str(hw, status)); | i40e_stat_str(hw, status)); | ||||
} | } | ||||
if_free(vsi->ifp); | |||||
free(sc->vf_res, M_DEVBUF); | free(sc->vf_res, M_DEVBUF); | ||||
ixlv_free_pci_resources(sc); | ixlv_free_pci_resources(sc); | ||||
ixlv_free_queues(vsi); | |||||
ixlv_free_filters(sc); | ixlv_free_filters(sc); | ||||
bus_generic_detach(dev); | |||||
mtx_destroy(&sc->mtx); | |||||
INIT_DBG_DEV(dev, "end"); | INIT_DBG_DEV(dev, "end"); | ||||
return (0); | return (0); | ||||
} | } | ||||
/********************************************************************* | /* TODO: Do shutdown-specific stuff here */ | ||||
* | |||||
* Shutdown entry point | |||||
* | |||||
**********************************************************************/ | |||||
static int | static int | ||||
ixlv_shutdown(device_t dev) | ixlv_if_shutdown(if_ctx_t ctx) | ||||
{ | { | ||||
struct ixlv_sc *sc = device_get_softc(dev); | int error = 0; | ||||
INIT_DBG_DEV(dev, "begin"); | INIT_DBG_DEV(dev, "begin"); | ||||
mtx_lock(&sc->mtx); | /* TODO: Call ixl_if_stop()? */ | ||||
ixlv_stop(sc); | |||||
mtx_unlock(&sc->mtx); | |||||
INIT_DBG_DEV(dev, "end"); | return (error); | ||||
return (0); | |||||
} | } | ||||
/* | /* TODO: What is a VF supposed to do in suspend/resume? */ | ||||
* Configure TXCSUM(IPV6) and TSO(4/6) | static int | ||||
* - the hardware handles these together so we | ixlv_if_suspend(if_ctx_t ctx) | ||||
* need to tweak them | |||||
*/ | |||||
static void | |||||
ixlv_cap_txcsum_tso(struct ixl_vsi *vsi, struct ifnet *ifp, int mask) | |||||
{ | { | ||||
/* Enable/disable TXCSUM/TSO4 */ | int error = 0; | ||||
if (!(ifp->if_capenable & IFCAP_TXCSUM) | |||||
&& !(ifp->if_capenable & IFCAP_TSO4)) { | INIT_DBG_DEV(dev, "begin"); | ||||
if (mask & IFCAP_TXCSUM) { | |||||
ifp->if_capenable |= IFCAP_TXCSUM; | /* TODO: Call ixl_if_stop()? */ | ||||
/* enable TXCSUM, restore TSO if previously enabled */ | |||||
if (vsi->flags & IXL_FLAGS_KEEP_TSO4) { | return (error); | ||||
vsi->flags &= ~IXL_FLAGS_KEEP_TSO4; | |||||
ifp->if_capenable |= IFCAP_TSO4; | |||||
} | } | ||||
} | |||||
else if (mask & IFCAP_TSO4) { | |||||
ifp->if_capenable |= (IFCAP_TXCSUM | IFCAP_TSO4); | |||||
vsi->flags &= ~IXL_FLAGS_KEEP_TSO4; | |||||
if_printf(ifp, | |||||
"TSO4 requires txcsum, enabling both...\n"); | |||||
} | |||||
} else if((ifp->if_capenable & IFCAP_TXCSUM) | |||||
&& !(ifp->if_capenable & IFCAP_TSO4)) { | |||||
if (mask & IFCAP_TXCSUM) | |||||
ifp->if_capenable &= ~IFCAP_TXCSUM; | |||||
else if (mask & IFCAP_TSO4) | |||||
ifp->if_capenable |= IFCAP_TSO4; | |||||
} else if((ifp->if_capenable & IFCAP_TXCSUM) | |||||
&& (ifp->if_capenable & IFCAP_TSO4)) { | |||||
if (mask & IFCAP_TXCSUM) { | |||||
vsi->flags |= IXL_FLAGS_KEEP_TSO4; | |||||
ifp->if_capenable &= ~(IFCAP_TXCSUM | IFCAP_TSO4); | |||||
if_printf(ifp, | |||||
"TSO4 requires txcsum, disabling both...\n"); | |||||
} else if (mask & IFCAP_TSO4) | |||||
ifp->if_capenable &= ~IFCAP_TSO4; | |||||
} | |||||
/* Enable/disable TXCSUM_IPV6/TSO6 */ | static int | ||||
if (!(ifp->if_capenable & IFCAP_TXCSUM_IPV6) | ixlv_if_resume(if_ctx_t ctx) | ||||
&& !(ifp->if_capenable & IFCAP_TSO6)) { | { | ||||
if (mask & IFCAP_TXCSUM_IPV6) { | struct ifnet *ifp = iflib_get_ifp(ctx); | ||||
ifp->if_capenable |= IFCAP_TXCSUM_IPV6; | |||||
if (vsi->flags & IXL_FLAGS_KEEP_TSO6) { | INIT_DBG_DEV(dev, "begin"); | ||||
vsi->flags &= ~IXL_FLAGS_KEEP_TSO6; | |||||
ifp->if_capenable |= IFCAP_TSO6; | /* Read & clear wake-up registers */ | ||||
/* Required after D3->D0 transition */ | |||||
if (ifp->if_flags & IFF_UP) | |||||
ixlv_if_init(ctx); | |||||
return (0); | |||||
} | } | ||||
} else if (mask & IFCAP_TSO6) { | |||||
ifp->if_capenable |= (IFCAP_TXCSUM_IPV6 | IFCAP_TSO6); | |||||
vsi->flags &= ~IXL_FLAGS_KEEP_TSO6; | |||||
if_printf(ifp, | |||||
"TSO6 requires txcsum6, enabling both...\n"); | |||||
} | |||||
} else if((ifp->if_capenable & IFCAP_TXCSUM_IPV6) | |||||
&& !(ifp->if_capenable & IFCAP_TSO6)) { | |||||
if (mask & IFCAP_TXCSUM_IPV6) | |||||
ifp->if_capenable &= ~IFCAP_TXCSUM_IPV6; | |||||
else if (mask & IFCAP_TSO6) | |||||
ifp->if_capenable |= IFCAP_TSO6; | |||||
} else if ((ifp->if_capenable & IFCAP_TXCSUM_IPV6) | |||||
&& (ifp->if_capenable & IFCAP_TSO6)) { | |||||
if (mask & IFCAP_TXCSUM_IPV6) { | |||||
vsi->flags |= IXL_FLAGS_KEEP_TSO6; | |||||
ifp->if_capenable &= ~(IFCAP_TXCSUM_IPV6 | IFCAP_TSO6); | |||||
if_printf(ifp, | |||||
"TSO6 requires txcsum6, disabling both...\n"); | |||||
} else if (mask & IFCAP_TSO6) | |||||
ifp->if_capenable &= ~IFCAP_TSO6; | |||||
} | |||||
} | |||||
#if 0 | |||||
/********************************************************************* | /********************************************************************* | ||||
* Ioctl entry point | * Ioctl entry point | ||||
* | * | ||||
* ixlv_ioctl is called when the user wants to configure the | * ixlv_ioctl is called when the user wants to configure the | ||||
* interface. | * interface. | ||||
* | * | ||||
* return 0 on success, positive on failure | * return 0 on success, positive on failure | ||||
**********************************************************************/ | **********************************************************************/ | ||||
▲ Show 20 Lines • Show All 125 Lines • ▼ Show 20 Lines | #endif | ||||
default: | default: | ||||
IOCTL_DBG_IF2(ifp, "UNKNOWN (0x%X)", (int)command); | IOCTL_DBG_IF2(ifp, "UNKNOWN (0x%X)", (int)command); | ||||
error = ether_ioctl(ifp, command, data); | error = ether_ioctl(ifp, command, data); | ||||
break; | break; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
#endif | |||||
/* | /* | ||||
** To do a reinit on the VF is unfortunately more complicated | ** To do a reinit on the VF is unfortunately more complicated | ||||
** than a physical device, we must have the PF more or less | ** than a physical device, we must have the PF more or less | ||||
** completely recreate our memory, so many things that were | ** completely recreate our memory, so many things that were | ||||
** done only once at attach in traditional drivers now must be | ** done only once at attach in traditional drivers now must be | ||||
** redone at each reinitialization. This function does that | ** redone at each reinitialization. This function does that | ||||
** 'prelude' so we can then call the normal locked init code. | ** 'prelude' so we can then call the normal locked init code. | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | ixl_init_cmd_complete(struct ixl_vc_cmd *cmd, void *arg, | ||||
*/ | */ | ||||
if (code != I40E_SUCCESS && code != I40E_ERR_ADAPTER_STOPPED) { | if (code != I40E_SUCCESS && code != I40E_ERR_ADAPTER_STOPPED) { | ||||
if_printf(sc->vsi.ifp, | if_printf(sc->vsi.ifp, | ||||
"Error %s waiting for PF to complete operation %d\n", | "Error %s waiting for PF to complete operation %d\n", | ||||
i40e_stat_str(&sc->hw, code), cmd->request); | i40e_stat_str(&sc->hw, code), cmd->request); | ||||
} | } | ||||
} | } | ||||
static void | void | ||||
ixlv_init_locked(struct ixlv_sc *sc) | ixlv_if_init(if_ctx_t ctx) | ||||
{ | { | ||||
struct ixl_vsi *vsi = iflib_get_softc(ctx); | |||||
if_softc_ctx_t scctx = vsi->shared; | |||||
struct ixlv_sc *sc = vsi->back; | |||||
struct i40e_hw *hw = &sc->hw; | struct i40e_hw *hw = &sc->hw; | ||||
struct ixl_vsi *vsi = &sc->vsi; | struct ifnet *ifp = iflib_get_ifp(ctx); | ||||
struct ixl_queue *que = vsi->queues; | |||||
struct ifnet *ifp = vsi->ifp; | struct ixl_tx_queue *tx_que = vsi->tx_queues; | ||||
struct ixl_rx_queue *rx_que = vsi->rx_queues; | |||||
int error = 0; | int error = 0; | ||||
INIT_DBG_IF(ifp, "begin"); | INIT_DBG_IF(ifp, "begin"); | ||||
IXLV_CORE_LOCK_ASSERT(sc); | |||||
/* Do a reinit first if an init has already been done */ | /* Do a reinit first if an init has already been done */ | ||||
if ((sc->init_state == IXLV_RUNNING) || | if ((sc->init_state == IXLV_RUNNING) || | ||||
(sc->init_state == IXLV_RESET_REQUIRED) || | (sc->init_state == IXLV_RESET_REQUIRED) || | ||||
(sc->init_state == IXLV_RESET_PENDING)) | (sc->init_state == IXLV_RESET_PENDING)) | ||||
error = ixlv_reinit_locked(sc); | error = ixlv_reinit_locked(sc); | ||||
/* Don't bother with init if we failed reinit */ | /* Don't bother with init if we failed reinit */ | ||||
if (error) | if (error) | ||||
goto init_done; | goto init_done; | ||||
/* Remove existing MAC filter if new MAC addr is set */ | /* Remove existing MAC filter if new MAC addr is set */ | ||||
if (bcmp(IF_LLADDR(ifp), hw->mac.addr, ETHER_ADDR_LEN) != 0) { | if (bcmp(IF_LLADDR(ifp), hw->mac.addr, ETHER_ADDR_LEN) != 0) { | ||||
error = ixlv_del_mac_filter(sc, hw->mac.addr); | error = ixlv_del_mac_filter(sc, hw->mac.addr); | ||||
if (error == 0) | if (error == 0) | ||||
ixl_vc_enqueue(&sc->vc_mgr, &sc->del_mac_cmd, | ixl_vc_enqueue(&sc->vc_mgr, &sc->del_mac_cmd, | ||||
IXLV_FLAG_AQ_DEL_MAC_FILTER, ixl_init_cmd_complete, | IXLV_FLAG_AQ_DEL_MAC_FILTER, ixl_init_cmd_complete, | ||||
sc); | sc); | ||||
} | } | ||||
/* Check for an LAA mac address... */ | /* Check for an LAA mac address... */ | ||||
bcopy(IF_LLADDR(ifp), hw->mac.addr, ETHER_ADDR_LEN); | bcopy(IF_LLADDR(ifp), hw->mac.addr, ETHER_ADDR_LEN); | ||||
ifp->if_hwassist = 0; | |||||
if (ifp->if_capenable & IFCAP_TSO) | |||||
ifp->if_hwassist |= CSUM_TSO; | |||||
if (ifp->if_capenable & IFCAP_TXCSUM) | |||||
ifp->if_hwassist |= (CSUM_OFFLOAD_IPV4 & ~CSUM_IP); | |||||
if (ifp->if_capenable & IFCAP_TXCSUM_IPV6) | |||||
ifp->if_hwassist |= CSUM_OFFLOAD_IPV6; | |||||
/* Add mac filter for this VF to PF */ | /* Add mac filter for this VF to PF */ | ||||
if (i40e_validate_mac_addr(hw->mac.addr) == I40E_SUCCESS) { | if (i40e_validate_mac_addr(hw->mac.addr) == I40E_SUCCESS) { | ||||
error = ixlv_add_mac_filter(sc, hw->mac.addr, 0); | error = ixlv_add_mac_filter(sc, hw->mac.addr, 0); | ||||
if (!error || error == EEXIST) | if (!error || error == EEXIST) | ||||
ixl_vc_enqueue(&sc->vc_mgr, &sc->add_mac_cmd, | ixl_vc_enqueue(&sc->vc_mgr, &sc->add_mac_cmd, | ||||
IXLV_FLAG_AQ_ADD_MAC_FILTER, ixl_init_cmd_complete, | IXLV_FLAG_AQ_ADD_MAC_FILTER, ixl_init_cmd_complete, | ||||
sc); | sc); | ||||
} | } | ||||
/* Setup vlan's if needed */ | /* Setup vlan's if needed */ | ||||
ixlv_setup_vlan_filters(sc); | ixlv_setup_vlan_filters(sc); | ||||
// TODO: Functionize | |||||
/* Prepare the queues for operation */ | /* Prepare the queues for operation */ | ||||
for (int i = 0; i < vsi->num_queues; i++, que++) { | for (int i = 0; i < vsi->num_tx_queues; i++, tx_que++) { | ||||
struct rx_ring *rxr = &que->rxr; | // TODO: Necessary? Correct? | ||||
ixl_init_tx_ring(vsi, tx_que); | |||||
} | |||||
for (int i = 0; i < vsi->num_rx_queues; i++, rx_que++) { | |||||
struct rx_ring *rxr = &rx_que->rxr; | |||||
ixl_init_tx_ring(que); | if (scctx->isc_max_frame_size <= MCLBYTES) | ||||
if (vsi->max_frame_size <= MCLBYTES) | |||||
rxr->mbuf_sz = MCLBYTES; | rxr->mbuf_sz = MCLBYTES; | ||||
else | else | ||||
rxr->mbuf_sz = MJUMPAGESIZE; | rxr->mbuf_sz = MJUMPAGESIZE; | ||||
ixl_init_rx_ring(que); | |||||
} | } | ||||
/* Set initial ITR values */ | /* Set initial ITR values */ | ||||
ixlv_configure_itr(sc); | ixlv_configure_itr(sc); | ||||
/* Configure queues */ | /* Configure queues */ | ||||
ixl_vc_enqueue(&sc->vc_mgr, &sc->config_queues_cmd, | ixl_vc_enqueue(&sc->vc_mgr, &sc->config_queues_cmd, | ||||
IXLV_FLAG_AQ_CONFIGURE_QUEUES, ixl_init_cmd_complete, sc); | IXLV_FLAG_AQ_CONFIGURE_QUEUES, ixl_init_cmd_complete, sc); | ||||
/* Set up RSS */ | /* Set up RSS */ | ||||
ixlv_config_rss(sc); | ixlv_config_rss(sc); | ||||
/* Map vectors */ | /* Map vectors */ | ||||
ixl_vc_enqueue(&sc->vc_mgr, &sc->map_vectors_cmd, | ixl_vc_enqueue(&sc->vc_mgr, &sc->map_vectors_cmd, | ||||
IXLV_FLAG_AQ_MAP_VECTORS, ixl_init_cmd_complete, sc); | IXLV_FLAG_AQ_MAP_VECTORS, ixl_init_cmd_complete, sc); | ||||
/* Enable queues */ | /* Enable queues */ | ||||
ixl_vc_enqueue(&sc->vc_mgr, &sc->enable_queues_cmd, | ixl_vc_enqueue(&sc->vc_mgr, &sc->enable_queues_cmd, | ||||
IXLV_FLAG_AQ_ENABLE_QUEUES, ixl_init_cmd_complete, sc); | IXLV_FLAG_AQ_ENABLE_QUEUES, ixl_init_cmd_complete, sc); | ||||
/* Start the local timer */ | |||||
callout_reset(&sc->timer, hz, ixlv_local_timer, sc); | |||||
sc->init_state = IXLV_RUNNING; | sc->init_state = IXLV_RUNNING; | ||||
init_done: | init_done: | ||||
INIT_DBG_IF(ifp, "end"); | INIT_DBG_IF(ifp, "end"); | ||||
return; | return; | ||||
} | } | ||||
/* | #if 0 | ||||
** Init entry point for the stack | |||||
*/ | |||||
void | void | ||||
ixlv_init(void *arg) | ixlv_init(void *arg) | ||||
{ | { | ||||
struct ixl_vsi *vsi = (struct ixl_vsi *)arg; | struct ixl_vsi *vsi = (struct ixl_vsi *)arg; | ||||
struct ixlv_sc *sc = vsi->back; | struct ixlv_sc *sc = vsi->back; | ||||
int retries = 0; | int retries = 0; | ||||
/* Prevent init from running again while waiting for AQ calls | /* Prevent init from running again while waiting for AQ calls | ||||
Show All 17 Lines | if (retries >= IXLV_MAX_INIT_WAIT) { | ||||
if_printf(vsi->ifp, | if_printf(vsi->ifp, | ||||
"Init failed to complete in allotted time!\n"); | "Init failed to complete in allotted time!\n"); | ||||
} | } | ||||
mtx_lock(&sc->mtx); | mtx_lock(&sc->mtx); | ||||
sc->init_in_progress = false; | sc->init_in_progress = false; | ||||
mtx_unlock(&sc->mtx); | mtx_unlock(&sc->mtx); | ||||
} | } | ||||
#endif | |||||
/* | /* | ||||
* ixlv_attach() helper function; gathers information about | |||||
* the (virtual) hardware for use elsewhere in the driver. | |||||
*/ | |||||
static void | |||||
ixlv_init_hw(struct ixlv_sc *sc) | |||||
{ | |||||
struct i40e_hw *hw = &sc->hw; | |||||
device_t dev = sc->dev; | |||||
/* Save off the information about this board */ | |||||
hw->vendor_id = pci_get_vendor(dev); | |||||
hw->device_id = pci_get_device(dev); | |||||
hw->revision_id = pci_read_config(dev, PCIR_REVID, 1); | |||||
hw->subsystem_vendor_id = | |||||
pci_read_config(dev, PCIR_SUBVEND_0, 2); | |||||
hw->subsystem_device_id = | |||||
pci_read_config(dev, PCIR_SUBDEV_0, 2); | |||||
hw->bus.device = pci_get_slot(dev); | |||||
hw->bus.func = pci_get_function(dev); | |||||
} | |||||
/* | |||||
* ixlv_attach() helper function; initalizes the admin queue | * ixlv_attach() helper function; initalizes the admin queue | ||||
* and attempts to establish contact with the PF by | * and attempts to establish contact with the PF by | ||||
* retrying the initial "API version" message several times | * retrying the initial "API version" message several times | ||||
* or until the PF responds. | * or until the PF responds. | ||||
*/ | */ | ||||
static int | static int | ||||
ixlv_setup_vc(struct ixlv_sc *sc) | ixlv_setup_vc(struct ixlv_sc *sc) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 151 Lines • ▼ Show 20 Lines | retry_config: | ||||
goto done; | goto done; | ||||
fail: | fail: | ||||
free(sc->vf_res, M_DEVBUF); | free(sc->vf_res, M_DEVBUF); | ||||
done: | done: | ||||
return (ret_error); | return (ret_error); | ||||
} | } | ||||
/* | |||||
* Allocate MSI/X vectors, setup the AQ vector early | |||||
*/ | |||||
static int | static int | ||||
ixlv_init_msix(struct ixlv_sc *sc) | ixlv_if_msix_intr_assign(if_ctx_t ctx, int msix) | ||||
{ | { | ||||
device_t dev = sc->dev; | struct ixl_vsi *vsi = iflib_get_softc(ctx); | ||||
int rid, want, vectors, queues, available; | struct ixlv_sc *sc = vsi->back; | ||||
int auto_max_queues; | struct ixl_rx_queue *que = vsi->rx_queues; | ||||
struct ixl_tx_queue *tx_que = vsi->tx_queues; | |||||
int err, i, rid, vector = 0; | |||||
char buf[16]; | |||||
rid = PCIR_BAR(IXL_MSIX_BAR); | /* Admin Que is vector 0*/ | ||||
sc->msix_mem = bus_alloc_resource_any(dev, | rid = vector + 1; | ||||
SYS_RES_MEMORY, &rid, RF_ACTIVE); | |||||
if (!sc->msix_mem) { | err = iflib_irq_alloc_generic(ctx, &vsi->irq, rid, IFLIB_INTR_ADMIN, | ||||
/* May not be enabled */ | ixlv_msix_adminq, sc, 0, "aq"); | ||||
device_printf(sc->dev, | if (err) { | ||||
"Unable to map MSIX table\n"); | iflib_irq_free(ctx, &vsi->irq); | ||||
goto fail; | device_printf(iflib_get_dev(ctx), "Failed to register Admin que handler"); | ||||
return (err); | |||||
} | } | ||||
sc->admvec = vector; | |||||
++vector; | |||||
available = pci_msix_count(dev); | /* Now set up the stations */ | ||||
if (available == 0) { /* system has msix disabled */ | for (i = 0; i < vsi->num_rx_queues; i++, vector++, que++) { | ||||
bus_release_resource(dev, SYS_RES_MEMORY, | rid = vector + 1; | ||||
rid, sc->msix_mem); | |||||
sc->msix_mem = NULL; | snprintf(buf, sizeof(buf), "rxq%d", i); | ||||
err = iflib_irq_alloc_generic(ctx, &que->que_irq, rid, IFLIB_INTR_RX, | |||||
ixlv_msix_que, que, que->rxr.me, buf); | |||||
if (err) { | |||||
device_printf(iflib_get_dev(ctx), "Failed to allocate q int %d err: %d", i, err); | |||||
vsi->num_rx_queues = i + 1; | |||||
goto fail; | goto fail; | ||||
} | } | ||||
que->msix = vector; | |||||
} | |||||
/* Clamp queues to number of CPUs and # of MSI-X vectors available */ | for (i = 0, tx_que = vsi->tx_queues; i < vsi->num_tx_queues; i++, tx_que++) { | ||||
auto_max_queues = min(mp_ncpus, available - 1); | snprintf(buf, sizeof(buf), "txq%d", i); | ||||
/* Clamp queues to # assigned to VF by PF */ | rid = que->msix + 1; | ||||
auto_max_queues = min(auto_max_queues, sc->vf_res->num_queue_pairs); | iflib_softirq_alloc_generic(ctx, rid, IFLIB_INTR_TX, tx_que, tx_que->txr.me, buf); | ||||
} | |||||
/* Override with tunable value if tunable is less than autoconfig count */ | return (0); | ||||
if ((ixlv_max_queues != 0) && (ixlv_max_queues <= auto_max_queues)) | fail: | ||||
queues = ixlv_max_queues; | iflib_irq_free(ctx, &vsi->irq); | ||||
/* Use autoconfig amount if that's lower */ | que = vsi->rx_queues; | ||||
else if ((ixlv_max_queues != 0) && (ixlv_max_queues > auto_max_queues)) { | for (int i = 0; i < vsi->num_rx_queues; i++, que++) | ||||
device_printf(dev, "ixlv_max_queues (%d) is too large, using " | iflib_irq_free(ctx, &que->que_irq); | ||||
"autoconfig amount (%d)...\n", | return (err); | ||||
ixlv_max_queues, auto_max_queues); | |||||
queues = auto_max_queues; | |||||
} | } | ||||
/* Limit maximum auto-configured queues to 8 if no user value is set */ | |||||
else | |||||
queues = min(auto_max_queues, 8); | |||||
#ifdef RSS | /* Enable all interrupts */ | ||||
/* If we're doing RSS, clamp at the number of RSS buckets */ | static void | ||||
if (queues > rss_getnumbuckets()) | ixlv_if_enable_intr(if_ctx_t ctx) | ||||
queues = rss_getnumbuckets(); | { | ||||
struct ixl_vsi *vsi = iflib_get_softc(ctx); | |||||
ixlv_enable_intr(vsi); | |||||
} | |||||
/* Disable all interrupts */ | |||||
static void | |||||
ixlv_if_disable_intr(if_ctx_t ctx) | |||||
{ | |||||
struct ixl_vsi *vsi = iflib_get_softc(ctx); | |||||
ixlv_disable_intr(vsi); | |||||
} | |||||
/* Enable queue interrupt */ | |||||
static int | |||||
ixlv_if_queue_intr_enable(if_ctx_t ctx, uint16_t rxqid) | |||||
{ | |||||
struct ixl_vsi *vsi = iflib_get_softc(ctx); | |||||
struct i40e_hw *hw = vsi->hw; | |||||
struct ixl_rx_queue *que = &vsi->rx_queues[rxqid]; | |||||
ixlv_enable_queue_irq(hw, que->rxr.me); | |||||
return (0); | |||||
} | |||||
static int | |||||
ixlv_if_tx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int ntxqs, int ntxqsets) | |||||
{ | |||||
struct ixl_vsi *vsi = iflib_get_softc(ctx); | |||||
struct ixl_tx_queue *que; | |||||
int i; | |||||
MPASS(vsi->num_tx_queues > 0); | |||||
MPASS(ntxqs == 1); | |||||
MPASS(vsi->num_tx_queues == ntxqsets); | |||||
/* Allocate queue structure memory */ | |||||
if (!(vsi->tx_queues = | |||||
(struct ixl_tx_queue *) malloc(sizeof(struct ixl_tx_queue) *ntxqsets, M_IXLV, M_NOWAIT | M_ZERO))) { | |||||
device_printf(iflib_get_dev(ctx), "Unable to allocate TX ring memory\n"); | |||||
return (ENOMEM); | |||||
} | |||||
for (i = 0, que = vsi->tx_queues; i < ntxqsets; i++, que++) { | |||||
struct tx_ring *txr = &que->txr; | |||||
txr->me = i; | |||||
que->vsi = vsi; | |||||
/* get the virtual and physical address of the hardware queues */ | |||||
txr->tail = I40E_QTX_TAIL1(txr->me); | |||||
txr->tx_base = (struct i40e_tx_desc *)vaddrs[i]; | |||||
txr->tx_paddr = paddrs[i]; | |||||
txr->que = que; | |||||
} | |||||
// TODO: Do a config_gtask_init for admin queue here? | |||||
// iflib_config_gtask_init(ctx, &adapter->mod_task, ixgbe_handle_mod, "mod_task"); | |||||
device_printf(iflib_get_dev(ctx), "%s: allocated for %d txqs\n", __func__, vsi->num_tx_queues); | |||||
return (0); | |||||
} | |||||
static int | |||||
ixlv_if_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int nrxqs, int nrxqsets) | |||||
{ | |||||
struct ixl_vsi *vsi = iflib_get_softc(ctx); | |||||
struct ixl_rx_queue *que; | |||||
int i; | |||||
MPASS(vsi->num_rx_queues > 0); | |||||
MPASS(nrxqs == 1); | |||||
MPASS(vsi->num_rx_queues == nrxqsets); | |||||
/* Allocate queue structure memory */ | |||||
if (!(vsi->rx_queues = | |||||
(struct ixl_rx_queue *) malloc(sizeof(struct ixl_rx_queue) * | |||||
nrxqsets, M_IXLV, M_NOWAIT | M_ZERO))) { | |||||
device_printf(iflib_get_dev(ctx), "Unable to allocate RX ring memory\n"); | |||||
return (ENOMEM); | |||||
} | |||||
for (i = 0, que = vsi->rx_queues; i < nrxqsets; i++, que++) { | |||||
struct rx_ring *rxr = &que->rxr; | |||||
rxr->me = i; | |||||
que->vsi = vsi; | |||||
/* get the virtual and physical address of the hardware queues */ | |||||
rxr->tail = I40E_QRX_TAIL1(rxr->me); | |||||
rxr->rx_base = (union i40e_rx_desc *)vaddrs[i]; | |||||
rxr->rx_paddr = paddrs[i]; | |||||
rxr->que = que; | |||||
} | |||||
device_printf(iflib_get_dev(ctx), "%s: allocated for %d rxqs\n", __func__, vsi->num_rx_queues); | |||||
return (0); | |||||
} | |||||
static void | |||||
ixlv_if_queues_free(if_ctx_t ctx) | |||||
{ | |||||
struct ixl_vsi *vsi = iflib_get_softc(ctx); | |||||
if (vsi->tx_queues != NULL) { | |||||
free(vsi->tx_queues, M_IXLV); | |||||
vsi->tx_queues = NULL; | |||||
} | |||||
if (vsi->rx_queues != NULL) { | |||||
free(vsi->rx_queues, M_IXLV); | |||||
vsi->rx_queues = NULL; | |||||
} | |||||
} | |||||
// TODO: Implement | |||||
static void | |||||
ixlv_if_update_admin_status(if_ctx_t ctx) | |||||
{ | |||||
struct ixl_vsi *vsi = iflib_get_softc(ctx); | |||||
//struct ixlv_sc *sc = vsi->back; | |||||
//struct i40e_hw *hw = &sc->hw; | |||||
//struct i40e_arq_event_info event; | |||||
//i40e_status ret; | |||||
//u32 loop = 0; | |||||
//u16 opcode | |||||
u16 result = 0; | |||||
//u64 baudrate; | |||||
/* TODO: Split up | |||||
* - Update admin queue stuff | |||||
* - Update link status | |||||
* - Enqueue aq task | |||||
* - Re-enable admin intr | |||||
*/ | |||||
/* TODO: Does VF reset need to be handled here? */ | |||||
#if 0 | |||||
if (pf->state & IXL_PF_STATE_EMPR_RESETTING) { | |||||
/* Flag cleared at end of this function */ | |||||
ixl_handle_empr_reset(pf); | |||||
return; | |||||
} | |||||
#endif | #endif | ||||
#if 0 | |||||
event.buf_len = IXL_AQ_BUF_SZ; | |||||
event.msg_buf = malloc(event.buf_len, | |||||
M_IXLV, M_NOWAIT | M_ZERO); | |||||
if (!event.msg_buf) { | |||||
device_printf(pf->dev, "%s: Unable to allocate memory for Admin" | |||||
" Queue event!\n", __func__); | |||||
return; | |||||
} | |||||
/* clean and process any events */ | |||||
do { | |||||
ret = i40e_clean_arq_element(hw, &event, &result); | |||||
if (ret) | |||||
break; | |||||
opcode = LE16_TO_CPU(event.desc.opcode); | |||||
ixl_dbg(pf, IXL_DBG_AQ, | |||||
"Admin Queue event: %#06x\n", opcode); | |||||
switch (opcode) { | |||||
case i40e_aqc_opc_get_link_status: | |||||
ixl_link_event(pf, &event); | |||||
break; | |||||
case i40e_aqc_opc_send_msg_to_pf: | |||||
#ifdef PCI_IOV | |||||
ixl_handle_vf_msg(pf, &event); | |||||
#endif | |||||
break; | |||||
case i40e_aqc_opc_event_lan_overflow: | |||||
break; | |||||
default: | |||||
#ifdef IXL_DEBUG | |||||
printf("AdminQ unknown event %x\n", opcode); | |||||
#endif | |||||
break; | |||||
} | |||||
} while (result && (loop++ < IXL_ADM_LIMIT)); | |||||
free(event.msg_buf, M_IXLV); | |||||
#endif | |||||
#if 0 | |||||
/* XXX: This updates the link status */ | |||||
if (pf->link_up) { | |||||
if (vsi->link_active == FALSE) { | |||||
vsi->link_active = TRUE; | |||||
baudrate = ixl_max_aq_speed_to_value(pf->link_speed); | |||||
iflib_link_state_change(ctx, LINK_STATE_UP, baudrate); | |||||
ixl_link_up_msg(pf); | |||||
// ixl_ping_all_vfs(adapter); | |||||
} | |||||
} else { /* Link down */ | |||||
if (vsi->link_active == TRUE) { | |||||
vsi->link_active = FALSE; | |||||
iflib_link_state_change(ctx, LINK_STATE_DOWN, 0); | |||||
// ixl_ping_all_vfs(adapter); | |||||
} | |||||
} | |||||
#endif | |||||
/* | /* | ||||
** Want one vector (RX/TX pair) per queue | * If there are still messages to process, reschedule ourselves. | ||||
** plus an additional for the admin queue. | * Otherwise, re-enable our interrupt and go to sleep. | ||||
*/ | */ | ||||
want = queues + 1; | if (result > 0) | ||||
if (want <= available) /* Have enough */ | iflib_admin_intr_deferred(ctx); | ||||
vectors = want; | else | ||||
else { | /* TODO: Link/adminq interrupt should be re-enabled in IFDI_LINK_INTR_ENABLE */ | ||||
device_printf(sc->dev, | ixlv_enable_intr(vsi); | ||||
"MSIX Configuration Problem, " | |||||
"%d vectors available but %d wanted!\n", | |||||
available, want); | |||||
goto fail; | |||||
} | } | ||||
#ifdef RSS | static void | ||||
ixlv_if_multi_set(if_ctx_t ctx) | |||||
{ | |||||
// struct ixl_vsi *vsi = iflib_get_softc(ctx); | |||||
// struct i40e_hw *hw = vsi->hw; | |||||
// struct ixlv_sc *sc = vsi->back; | |||||
// int mcnt = 0, flags; | |||||
IOCTL_DEBUGOUT("ixl_if_multi_set: begin"); | |||||
// TODO: Implement | |||||
#if 0 | |||||
mcnt = if_multiaddr_count(iflib_get_ifp(ctx), MAX_MULTICAST_ADDR); | |||||
/* delete existing MC filters */ | |||||
ixlv_del_multi(vsi); | |||||
if (__predict_false(mcnt == MAX_MULTICAST_ADDR)) { | |||||
// Set promiscuous mode (multicast) | |||||
// TODO: This needs to get handled somehow | |||||
#if 0 | |||||
ixl_vc_enqueue(&sc->vc_mgr, &sc->add_vlan_cmd, | |||||
IXLV_FLAG_AQ_CONFIGURE_PROMISC, ixl_init_cmd_complete, sc); | |||||
#endif | |||||
return; | |||||
} | |||||
/* (re-)install filters for all mcast addresses */ | |||||
mcnt = if_multi_apply(iflib_get_ifp(ctx), ixl_mc_filter_apply, vsi); | |||||
if (mcnt > 0) { | |||||
flags = (IXL_FILTER_ADD | IXL_FILTER_USED | IXL_FILTER_MC); | |||||
ixlv_add_hw_filters(vsi, flags, mcnt); | |||||
} | |||||
#endif | |||||
IOCTL_DEBUGOUT("ixl_if_multi_set: end"); | |||||
} | |||||
static int | |||||
ixlv_if_mtu_set(if_ctx_t ctx, uint32_t mtu) | |||||
{ | |||||
struct ixl_vsi *vsi = iflib_get_softc(ctx); | |||||
IOCTL_DEBUGOUT("ioctl: SIOCSIFMTU (Set Interface MTU)"); | |||||
if (mtu > IXL_MAX_FRAME - ETHER_HDR_LEN - ETHER_CRC_LEN - | |||||
ETHER_VLAN_ENCAP_LEN) | |||||
return (EINVAL); | |||||
vsi->shared->isc_max_frame_size = mtu + ETHER_HDR_LEN + ETHER_CRC_LEN + | |||||
ETHER_VLAN_ENCAP_LEN; | |||||
return (0); | |||||
} | |||||
static void | |||||
ixlv_if_media_status(if_ctx_t ctx, struct ifmediareq *ifmr) | |||||
{ | |||||
struct ixl_vsi *vsi = iflib_get_softc(ctx); | |||||
struct ixlv_sc *sc = (struct ixlv_sc *)vsi->back; | |||||
struct i40e_hw *hw = &sc->hw; | |||||
INIT_DEBUGOUT("ixl_media_status: begin"); | |||||
hw->phy.get_link_info = TRUE; | |||||
i40e_get_link_status(hw, &sc->link_up); | |||||
ifmr->ifm_status = IFM_AVALID; | |||||
ifmr->ifm_active = IFM_ETHER; | |||||
if (!sc->link_up) { | |||||
return; | |||||
} | |||||
ifmr->ifm_status |= IFM_ACTIVE; | |||||
/* Hardware is always full-duplex */ | |||||
ifmr->ifm_active |= IFM_FDX; | |||||
// TODO: Check another variable to get link speed | |||||
#if 0 | |||||
switch (hw->phy.link_info.phy_type) { | |||||
/* 100 M */ | |||||
case I40E_PHY_TYPE_100BASE_TX: | |||||
ifmr->ifm_active |= IFM_100_TX; | |||||
break; | |||||
/* 1 G */ | |||||
case I40E_PHY_TYPE_1000BASE_T: | |||||
ifmr->ifm_active |= IFM_1000_T; | |||||
break; | |||||
case I40E_PHY_TYPE_1000BASE_SX: | |||||
ifmr->ifm_active |= IFM_1000_SX; | |||||
break; | |||||
case I40E_PHY_TYPE_1000BASE_LX: | |||||
ifmr->ifm_active |= IFM_1000_LX; | |||||
break; | |||||
case I40E_PHY_TYPE_1000BASE_T_OPTICAL: | |||||
ifmr->ifm_active |= IFM_OTHER; | |||||
break; | |||||
/* 10 G */ | |||||
case I40E_PHY_TYPE_10GBASE_SFPP_CU: | |||||
ifmr->ifm_active |= IFM_10G_TWINAX; | |||||
break; | |||||
case I40E_PHY_TYPE_10GBASE_SR: | |||||
ifmr->ifm_active |= IFM_10G_SR; | |||||
break; | |||||
case I40E_PHY_TYPE_10GBASE_LR: | |||||
ifmr->ifm_active |= IFM_10G_LR; | |||||
break; | |||||
case I40E_PHY_TYPE_10GBASE_T: | |||||
ifmr->ifm_active |= IFM_10G_T; | |||||
break; | |||||
case I40E_PHY_TYPE_XAUI: | |||||
case I40E_PHY_TYPE_XFI: | |||||
case I40E_PHY_TYPE_10GBASE_AOC: | |||||
ifmr->ifm_active |= IFM_OTHER; | |||||
break; | |||||
/* 25 G */ | |||||
case I40E_PHY_TYPE_25GBASE_KR: | |||||
ifmr->ifm_active |= IFM_25G_KR; | |||||
break; | |||||
case I40E_PHY_TYPE_25GBASE_CR: | |||||
ifmr->ifm_active |= IFM_25G_CR; | |||||
break; | |||||
case I40E_PHY_TYPE_25GBASE_SR: | |||||
ifmr->ifm_active |= IFM_25G_SR; | |||||
break; | |||||
case I40E_PHY_TYPE_25GBASE_LR: | |||||
ifmr->ifm_active |= IFM_UNKNOWN; | |||||
break; | |||||
/* 40 G */ | |||||
case I40E_PHY_TYPE_40GBASE_CR4: | |||||
case I40E_PHY_TYPE_40GBASE_CR4_CU: | |||||
ifmr->ifm_active |= IFM_40G_CR4; | |||||
break; | |||||
case I40E_PHY_TYPE_40GBASE_SR4: | |||||
ifmr->ifm_active |= IFM_40G_SR4; | |||||
break; | |||||
case I40E_PHY_TYPE_40GBASE_LR4: | |||||
ifmr->ifm_active |= IFM_40G_LR4; | |||||
break; | |||||
case I40E_PHY_TYPE_XLAUI: | |||||
ifmr->ifm_active |= IFM_OTHER; | |||||
break; | |||||
case I40E_PHY_TYPE_1000BASE_KX: | |||||
ifmr->ifm_active |= IFM_1000_KX; | |||||
break; | |||||
case I40E_PHY_TYPE_SGMII: | |||||
ifmr->ifm_active |= IFM_1000_SGMII; | |||||
break; | |||||
/* ERJ: What's the difference between these? */ | |||||
case I40E_PHY_TYPE_10GBASE_CR1_CU: | |||||
case I40E_PHY_TYPE_10GBASE_CR1: | |||||
ifmr->ifm_active |= IFM_10G_CR1; | |||||
break; | |||||
case I40E_PHY_TYPE_10GBASE_KX4: | |||||
ifmr->ifm_active |= IFM_10G_KX4; | |||||
break; | |||||
case I40E_PHY_TYPE_10GBASE_KR: | |||||
ifmr->ifm_active |= IFM_10G_KR; | |||||
break; | |||||
case I40E_PHY_TYPE_SFI: | |||||
ifmr->ifm_active |= IFM_10G_SFI; | |||||
break; | |||||
/* Our single 20G media type */ | |||||
case I40E_PHY_TYPE_20GBASE_KR2: | |||||
ifmr->ifm_active |= IFM_20G_KR2; | |||||
break; | |||||
case I40E_PHY_TYPE_40GBASE_KR4: | |||||
ifmr->ifm_active |= IFM_40G_KR4; | |||||
break; | |||||
case I40E_PHY_TYPE_XLPPI: | |||||
case I40E_PHY_TYPE_40GBASE_AOC: | |||||
ifmr->ifm_active |= IFM_40G_XLPPI; | |||||
break; | |||||
/* Unknown to driver */ | |||||
default: | |||||
ifmr->ifm_active |= IFM_UNKNOWN; | |||||
break; | |||||
} | |||||
/* Report flow control status as well */ | |||||
if (hw->phy.link_info.an_info & I40E_AQ_LINK_PAUSE_TX) | |||||
ifmr->ifm_active |= IFM_ETH_TXPAUSE; | |||||
if (hw->phy.link_info.an_info & I40E_AQ_LINK_PAUSE_RX) | |||||
ifmr->ifm_active |= IFM_ETH_RXPAUSE; | |||||
#endif | |||||
} | |||||
static int | |||||
ixlv_if_media_change(if_ctx_t ctx) | |||||
{ | |||||
struct ifmedia *ifm = iflib_get_media(ctx); | |||||
INIT_DEBUGOUT("ixl_media_change: begin"); | |||||
if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) | |||||
return (EINVAL); | |||||
if_printf(iflib_get_ifp(ctx), "Media change is not supported.\n"); | |||||
return (ENODEV); | |||||
} | |||||
// TODO: Rework | |||||
static int | |||||
ixlv_if_promisc_set(if_ctx_t ctx, int flags) | |||||
{ | |||||
struct ixl_vsi *vsi = iflib_get_softc(ctx); | |||||
struct ifnet *ifp = iflib_get_ifp(ctx); | |||||
struct i40e_hw *hw = vsi->hw; | |||||
int err; | |||||
bool uni = FALSE, multi = FALSE; | |||||
if (flags & IFF_ALLMULTI || | |||||
if_multiaddr_count(ifp, MAX_MULTICAST_ADDR) == MAX_MULTICAST_ADDR) | |||||
multi = TRUE; | |||||
if (flags & IFF_PROMISC) | |||||
uni = TRUE; | |||||
err = i40e_aq_set_vsi_unicast_promiscuous(hw, | |||||
vsi->seid, uni, NULL, false); | |||||
if (err) | |||||
return (err); | |||||
err = i40e_aq_set_vsi_multicast_promiscuous(hw, | |||||
vsi->seid, multi, NULL); | |||||
return (err); | |||||
} | |||||
static void | |||||
ixlv_if_timer(if_ctx_t ctx, uint16_t qid) | |||||
{ | |||||
struct ixl_vsi *vsi = iflib_get_softc(ctx); | |||||
struct ixlv_sc *sc = vsi->back; | |||||
//struct i40e_hw *hw = &sc->hw; | |||||
//struct ixl_tx_queue *que = &vsi->tx_queues[qid]; | |||||
//u32 mask; | |||||
#if 0 | |||||
/* | /* | ||||
* If we're doing RSS, the number of queues needs to | ** Check status of the queues | ||||
* match the number of RSS buckets that are configured. | |||||
* | |||||
* + If there's more queues than RSS buckets, we'll end | |||||
* up with queues that get no traffic. | |||||
* | |||||
* + If there's more RSS buckets than queues, we'll end | |||||
* up having multiple RSS buckets map to the same queue, | |||||
* so there'll be some contention. | |||||
*/ | */ | ||||
if (queues != rss_getnumbuckets()) { | mask = (I40E_PFINT_DYN_CTLN_INTENA_MASK | | ||||
device_printf(dev, | I40E_PFINT_DYN_CTLN_SWINT_TRIG_MASK); | ||||
"%s: queues (%d) != RSS buckets (%d)" | |||||
"; performance will be impacted.\n", | /* If queue param has outstanding work, trigger sw irq */ | ||||
__func__, queues, rss_getnumbuckets()); | // TODO: TX queues in iflib don't use HW interrupts; does this do anything? | ||||
} | if (que->busy) | ||||
wr32(hw, I40E_PFINT_DYN_CTLN(que->txr.me), mask); | |||||
#endif | #endif | ||||
if (pci_alloc_msix(dev, &vectors) == 0) { | // XXX: Is this timer per-queue? | ||||
device_printf(sc->dev, | if (qid != 0) | ||||
"Using MSIX interrupts with %d vectors\n", vectors); | return; | ||||
sc->msix = vectors; | |||||
sc->vsi.num_queues = queues; | /* Fire off the adminq task */ | ||||
iflib_admin_intr_deferred(ctx); | |||||
/* Update stats */ | |||||
ixlv_request_stats(sc); | |||||
} | } | ||||
/* Next we need to setup the vector for the Admin Queue */ | static void | ||||
rid = 1; /* zero vector + 1 */ | ixlv_if_vlan_register(if_ctx_t ctx, u16 vtag) | ||||
sc->res = bus_alloc_resource_any(dev, SYS_RES_IRQ, | { | ||||
&rid, RF_SHAREABLE | RF_ACTIVE); | struct ixl_vsi *vsi = iflib_get_softc(ctx); | ||||
if (sc->res == NULL) { | //struct i40e_hw *hw = vsi->hw; | ||||
device_printf(dev, "Unable to allocate" | |||||
" bus resource: AQ interrupt \n"); | if ((vtag == 0) || (vtag > 4095)) /* Invalid */ | ||||
goto fail; | return; | ||||
++vsi->num_vlans; | |||||
// TODO: Redo | |||||
// ixlv_add_filter(vsi, hw->mac.addr, vtag); | |||||
} | } | ||||
if (bus_setup_intr(dev, sc->res, | |||||
INTR_TYPE_NET | INTR_MPSAFE, NULL, | static void | ||||
ixlv_msix_adminq, sc, &sc->tag)) { | ixlv_if_vlan_unregister(if_ctx_t ctx, u16 vtag) | ||||
sc->res = NULL; | { | ||||
device_printf(dev, "Failed to register AQ handler"); | struct ixl_vsi *vsi = iflib_get_softc(ctx); | ||||
goto fail; | //struct i40e_hw *hw = vsi->hw; | ||||
if ((vtag == 0) || (vtag > 4095)) /* Invalid */ | |||||
return; | |||||
--vsi->num_vlans; | |||||
// TODO: Redo | |||||
// ixlv_del_filter(vsi, hw->mac.addr, vtag); | |||||
} | } | ||||
bus_describe_intr(dev, sc->res, sc->tag, "adminq"); | |||||
return (vectors); | static uint64_t | ||||
ixlv_if_get_counter(if_ctx_t ctx, ift_counter cnt) | |||||
{ | |||||
struct ixl_vsi *vsi = iflib_get_softc(ctx); | |||||
if_t ifp = iflib_get_ifp(ctx); | |||||
fail: | switch (cnt) { | ||||
/* The VF driver MUST use MSIX */ | case IFCOUNTER_IPACKETS: | ||||
return (vsi->ipackets); | |||||
case IFCOUNTER_IERRORS: | |||||
return (vsi->ierrors); | |||||
case IFCOUNTER_OPACKETS: | |||||
return (vsi->opackets); | |||||
case IFCOUNTER_OERRORS: | |||||
return (vsi->oerrors); | |||||
case IFCOUNTER_COLLISIONS: | |||||
/* Collisions are by standard impossible in 40G/10G Ethernet */ | |||||
return (0); | return (0); | ||||
case IFCOUNTER_IBYTES: | |||||
return (vsi->ibytes); | |||||
case IFCOUNTER_OBYTES: | |||||
return (vsi->obytes); | |||||
case IFCOUNTER_IMCASTS: | |||||
return (vsi->imcasts); | |||||
case IFCOUNTER_OMCASTS: | |||||
return (vsi->omcasts); | |||||
case IFCOUNTER_IQDROPS: | |||||
return (vsi->iqdrops); | |||||
case IFCOUNTER_OQDROPS: | |||||
return (vsi->oqdrops); | |||||
case IFCOUNTER_NOPROTO: | |||||
return (vsi->noproto); | |||||
default: | |||||
return (if_get_counter_default(ifp, cnt)); | |||||
} | } | ||||
} | |||||
static int | static int | ||||
ixlv_allocate_pci_resources(struct ixlv_sc *sc) | ixlv_allocate_pci_resources(struct ixlv_sc *sc) | ||||
{ | { | ||||
struct i40e_hw *hw = &sc->hw; | |||||
device_t dev = iflib_get_dev(sc->vsi.ctx); | |||||
int rid; | int rid; | ||||
device_t dev = sc->dev; | |||||
/* Map BAR0 */ | |||||
rid = PCIR_BAR(0); | rid = PCIR_BAR(0); | ||||
sc->pci_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, | sc->pci_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, | ||||
&rid, RF_ACTIVE); | &rid, RF_ACTIVE); | ||||
if (!(sc->pci_mem)) { | if (!(sc->pci_mem)) { | ||||
device_printf(dev, "Unable to allocate bus resource: memory\n"); | device_printf(dev, "Unable to allocate bus resource: PCI memory\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
/* Save off the PCI information */ | |||||
hw->vendor_id = pci_get_vendor(dev); | |||||
hw->device_id = pci_get_device(dev); | |||||
hw->revision_id = pci_read_config(dev, PCIR_REVID, 1); | |||||
hw->subsystem_vendor_id = | |||||
pci_read_config(dev, PCIR_SUBVEND_0, 2); | |||||
hw->subsystem_device_id = | |||||
pci_read_config(dev, PCIR_SUBDEV_0, 2); | |||||
hw->bus.device = pci_get_slot(dev); | |||||
hw->bus.func = pci_get_function(dev); | |||||
/* Save off register access information */ | |||||
sc->osdep.mem_bus_space_tag = | sc->osdep.mem_bus_space_tag = | ||||
rman_get_bustag(sc->pci_mem); | rman_get_bustag(sc->pci_mem); | ||||
sc->osdep.mem_bus_space_handle = | sc->osdep.mem_bus_space_handle = | ||||
rman_get_bushandle(sc->pci_mem); | rman_get_bushandle(sc->pci_mem); | ||||
sc->osdep.mem_bus_space_size = rman_get_size(sc->pci_mem); | sc->osdep.mem_bus_space_size = rman_get_size(sc->pci_mem); | ||||
sc->osdep.flush_reg = I40E_VFGEN_RSTAT; | sc->osdep.flush_reg = I40E_VFGEN_RSTAT; | ||||
sc->hw.hw_addr = (u8 *) &sc->osdep.mem_bus_space_handle; | sc->osdep.dev = dev; | ||||
sc->hw.hw_addr = (u8 *) &sc->osdep.mem_bus_space_handle; | |||||
sc->hw.back = &sc->osdep; | sc->hw.back = &sc->osdep; | ||||
/* | |||||
** Explicitly set the guest PCI BUSMASTER capability | |||||
** and we must rewrite the ENABLE in the MSIX control | |||||
** register again at this point to cause the host to | |||||
** successfully initialize us. | |||||
** | |||||
** This must be set before accessing any registers. | |||||
*/ | |||||
{ | |||||
u16 pci_cmd_word; | |||||
int msix_ctrl; | |||||
pci_cmd_word = pci_read_config(dev, PCIR_COMMAND, 2); | |||||
pci_cmd_word |= PCIM_CMD_BUSMASTEREN; | |||||
pci_write_config(dev, PCIR_COMMAND, pci_cmd_word, 2); | |||||
pci_find_cap(dev, PCIY_MSIX, &rid); | |||||
rid += PCIR_MSIX_CTRL; | |||||
msix_ctrl = pci_read_config(dev, rid, 2); | |||||
msix_ctrl |= PCIM_MSIXCTRL_MSIX_ENABLE; | |||||
pci_write_config(dev, rid, msix_ctrl, 2); | |||||
} | |||||
/* Disable adminq interrupts (just in case) */ | /* Disable adminq interrupts (just in case) */ | ||||
ixlv_disable_adminq_irq(&sc->hw); | /* TODO: Probably not necessary */ | ||||
// ixlv_disable_adminq_irq(&sc->hw); | |||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
ixlv_free_pci_resources(struct ixlv_sc *sc) | ixlv_free_pci_resources(struct ixlv_sc *sc) | ||||
{ | { | ||||
struct ixl_vsi *vsi = &sc->vsi; | struct ixl_vsi *vsi = &sc->vsi; | ||||
struct ixl_queue *que = vsi->queues; | struct ixl_rx_queue *rx_que = vsi->rx_queues; | ||||
device_t dev = sc->dev; | device_t dev = sc->dev; | ||||
/* We may get here before stations are setup */ | /* We may get here before stations are setup */ | ||||
if (que == NULL) | // TODO: Check if we can still check against sc->msix | ||||
if ((sc->msix > 0) || (rx_que == NULL)) | |||||
goto early; | goto early; | ||||
/* | /* | ||||
** Release all msix queue resources: | ** Release all msix VSI resources: | ||||
*/ | */ | ||||
for (int i = 0; i < vsi->num_queues; i++, que++) { | iflib_irq_free(vsi->ctx, &vsi->irq); | ||||
int rid = que->msix + 1; | |||||
if (que->tag != NULL) { | |||||
bus_teardown_intr(dev, que->res, que->tag); | |||||
que->tag = NULL; | |||||
} | |||||
if (que->res != NULL) { | |||||
bus_release_resource(dev, SYS_RES_IRQ, rid, que->res); | |||||
que->res = NULL; | |||||
} | |||||
} | |||||
early: | for (int i = 0; i < vsi->num_rx_queues; i++, rx_que++) | ||||
pci_release_msi(dev); | iflib_irq_free(vsi->ctx, &rx_que->que_irq); | ||||
if (sc->msix_mem != NULL) | early: | ||||
bus_release_resource(dev, SYS_RES_MEMORY, | |||||
PCIR_BAR(IXL_MSIX_BAR), sc->msix_mem); | |||||
if (sc->pci_mem != NULL) | if (sc->pci_mem != NULL) | ||||
bus_release_resource(dev, SYS_RES_MEMORY, | bus_release_resource(dev, SYS_RES_MEMORY, | ||||
PCIR_BAR(0), sc->pci_mem); | PCIR_BAR(0), sc->pci_mem); | ||||
} | } | ||||
/* | /* | ||||
* Create taskqueue and tasklet for Admin Queue interrupts. | |||||
*/ | |||||
static int | |||||
ixlv_init_taskqueue(struct ixlv_sc *sc) | |||||
{ | |||||
int error = 0; | |||||
TASK_INIT(&sc->aq_irq, 0, ixlv_do_adminq, sc); | |||||
sc->tq = taskqueue_create_fast("ixl_adm", M_NOWAIT, | |||||
taskqueue_thread_enqueue, &sc->tq); | |||||
taskqueue_start_threads(&sc->tq, 1, PI_NET, "%s sc->tq", | |||||
device_get_nameunit(sc->dev)); | |||||
return (error); | |||||
} | |||||
/********************************************************************* | |||||
* | |||||
* Setup MSIX Interrupt resources and handlers for the VSI queues | |||||
* | |||||
**********************************************************************/ | |||||
static int | |||||
ixlv_assign_msix(struct ixlv_sc *sc) | |||||
{ | |||||
device_t dev = sc->dev; | |||||
struct ixl_vsi *vsi = &sc->vsi; | |||||
struct ixl_queue *que = vsi->queues; | |||||
struct tx_ring *txr; | |||||
int error, rid, vector = 1; | |||||
#ifdef RSS | |||||
cpuset_t cpu_mask; | |||||
#endif | |||||
for (int i = 0; i < vsi->num_queues; i++, vector++, que++) { | |||||
int cpu_id = i; | |||||
rid = vector + 1; | |||||
txr = &que->txr; | |||||
que->res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, | |||||
RF_SHAREABLE | RF_ACTIVE); | |||||
if (que->res == NULL) { | |||||
device_printf(dev,"Unable to allocate" | |||||
" bus resource: que interrupt [%d]\n", vector); | |||||
return (ENXIO); | |||||
} | |||||
/* Set the handler function */ | |||||
error = bus_setup_intr(dev, que->res, | |||||
INTR_TYPE_NET | INTR_MPSAFE, NULL, | |||||
ixlv_msix_que, que, &que->tag); | |||||
if (error) { | |||||
que->res = NULL; | |||||
device_printf(dev, "Failed to register que handler"); | |||||
return (error); | |||||
} | |||||
bus_describe_intr(dev, que->res, que->tag, "que %d", i); | |||||
/* Bind the vector to a CPU */ | |||||
#ifdef RSS | |||||
cpu_id = rss_getcpu(i % rss_getnumbuckets()); | |||||
#endif | |||||
bus_bind_intr(dev, que->res, cpu_id); | |||||
que->msix = vector; | |||||
TASK_INIT(&que->tx_task, 0, ixl_deferred_mq_start, que); | |||||
TASK_INIT(&que->task, 0, ixlv_handle_que, que); | |||||
que->tq = taskqueue_create_fast("ixlv_que", M_NOWAIT, | |||||
taskqueue_thread_enqueue, &que->tq); | |||||
#ifdef RSS | |||||
CPU_SETOF(cpu_id, &cpu_mask); | |||||
taskqueue_start_threads_cpuset(&que->tq, 1, PI_NET, | |||||
&cpu_mask, "%s (bucket %d)", | |||||
device_get_nameunit(dev), cpu_id); | |||||
#else | |||||
taskqueue_start_threads(&que->tq, 1, PI_NET, | |||||
"%s que", device_get_nameunit(dev)); | |||||
#endif | |||||
} | |||||
return (0); | |||||
} | |||||
/* | |||||
** Requests a VF reset from the PF. | ** Requests a VF reset from the PF. | ||||
** | ** | ||||
** Requires the VF's Admin Queue to be initialized. | ** Requires the VF's Admin Queue to be initialized. | ||||
*/ | */ | ||||
static int | static int | ||||
ixlv_reset(struct ixlv_sc *sc) | ixlv_reset(struct ixlv_sc *sc) | ||||
{ | { | ||||
struct i40e_hw *hw = &sc->hw; | struct i40e_hw *hw = &sc->hw; | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/********************************************************************* | /********************************************************************* | ||||
* | * | ||||
* Setup networking device structure and register an interface. | * Setup networking device structure and register an interface. | ||||
* | * | ||||
**********************************************************************/ | **********************************************************************/ | ||||
static int | static void | ||||
ixlv_setup_interface(device_t dev, struct ixlv_sc *sc) | ixlv_setup_interface(device_t dev, struct ixl_vsi *vsi) | ||||
{ | { | ||||
struct ifnet *ifp; | if_ctx_t ctx = vsi->ctx; | ||||
struct ixl_vsi *vsi = &sc->vsi; | struct ixlv_sc *sc = vsi->back; | ||||
struct ixl_queue *que = vsi->queues; | struct ifnet *ifp = iflib_get_ifp(ctx); | ||||
uint64_t cap; | |||||
// struct ixl_queue *que = vsi->queues; | |||||
INIT_DBG_DEV(dev, "begin"); | INIT_DBG_DEV(dev, "begin"); | ||||
ifp = vsi->ifp = if_alloc(IFT_ETHER); | /* initialize fast path functions */ | ||||
if (ifp == NULL) { | cap = IXL_CAPS; | ||||
device_printf(dev, "%s: could not allocate ifnet" | if_setifheaderlen(ifp, sizeof(struct ether_vlan_header)); | ||||
" structure!\n", __func__); | if_setcapabilitiesbit(ifp, cap, 0); | ||||
return (-1); | if_setcapenable(ifp, if_getcapabilities(ifp)); | ||||
} | if_setbaudrate(ifp, IF_Gbps(40)); | ||||
/* TODO: Remove VLAN_ENCAP_LEN? */ | |||||
if_initname(ifp, device_get_name(dev), device_get_unit(dev)); | vsi->shared->isc_max_frame_size = | ||||
ifp->if_mtu = ETHERMTU; | |||||
ifp->if_baudrate = IF_Gbps(40); | |||||
ifp->if_init = ixlv_init; | |||||
ifp->if_softc = vsi; | |||||
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; | |||||
ifp->if_ioctl = ixlv_ioctl; | |||||
#if __FreeBSD_version >= 1100000 | |||||
if_setgetcounterfn(ifp, ixl_get_counter); | |||||
#endif | |||||
ifp->if_transmit = ixl_mq_start; | |||||
ifp->if_qflush = ixl_qflush; | |||||
ifp->if_snd.ifq_maxlen = que->num_desc - 2; | |||||
ether_ifattach(ifp, sc->hw.mac.addr); | |||||
vsi->max_frame_size = | |||||
ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN | ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN | ||||
+ ETHER_VLAN_ENCAP_LEN; | + ETHER_VLAN_ENCAP_LEN; | ||||
/* | /* | ||||
* Tell the upper layer(s) we support long frames. | |||||
*/ | |||||
ifp->if_hdrlen = sizeof(struct ether_vlan_header); | |||||
ifp->if_capabilities |= IFCAP_HWCSUM; | |||||
ifp->if_capabilities |= IFCAP_HWCSUM_IPV6; | |||||
ifp->if_capabilities |= IFCAP_TSO; | |||||
ifp->if_capabilities |= IFCAP_JUMBO_MTU; | |||||
ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | |||||
| IFCAP_VLAN_HWTSO | |||||
| IFCAP_VLAN_MTU | |||||
| IFCAP_VLAN_HWCSUM | |||||
| IFCAP_LRO; | |||||
ifp->if_capenable = ifp->if_capabilities; | |||||
/* | |||||
** Don't turn this on by default, if vlans are | ** Don't turn this on by default, if vlans are | ||||
** created on another pseudo device (eg. lagg) | ** created on another pseudo device (eg. lagg) | ||||
** then vlan events are not passed thru, breaking | ** then vlan events are not passed thru, breaking | ||||
** operation, but with HW FILTER off it works. If | ** operation, but with HW FILTER off it works. If | ||||
** using vlans directly on the ixl driver you can | ** using vlans directly on the ixl driver you can | ||||
** enable this and get full hardware tag filtering. | ** enable this and get full hardware tag filtering. | ||||
*/ | */ | ||||
ifp->if_capabilities |= IFCAP_VLAN_HWFILTER; | if_setcapabilitiesbit(ifp, IFCAP_VLAN_HWFILTER, 0); | ||||
/* | /* Use autoselect media by default */ | ||||
* Specify the media types supported by this adapter and register | |||||
* callbacks to update media and link information | |||||
*/ | |||||
ifmedia_init(&sc->media, IFM_IMASK, ixlv_media_change, | |||||
ixlv_media_status); | |||||
// JFV Add media types later? | |||||
ifmedia_add(&sc->media, IFM_ETHER | IFM_AUTO, 0, NULL); | ifmedia_add(&sc->media, IFM_ETHER | IFM_AUTO, 0, NULL); | ||||
ifmedia_set(&sc->media, IFM_ETHER | IFM_AUTO); | ifmedia_set(&sc->media, IFM_ETHER | IFM_AUTO); | ||||
INIT_DBG_DEV(dev, "end"); | INIT_DBG_DEV(dev, "end"); | ||||
return (0); | |||||
} | } | ||||
#if 0 | |||||
/* | /* | ||||
** Allocate and setup the interface queues | |||||
*/ | |||||
static int | |||||
ixlv_setup_queues(struct ixlv_sc *sc) | |||||
{ | |||||
device_t dev = sc->dev; | |||||
struct ixl_vsi *vsi; | |||||
struct ixl_queue *que; | |||||
struct tx_ring *txr; | |||||
struct rx_ring *rxr; | |||||
int rsize, tsize; | |||||
int error = I40E_SUCCESS; | |||||
vsi = &sc->vsi; | |||||
vsi->back = (void *)sc; | |||||
vsi->hw = &sc->hw; | |||||
vsi->num_vlans = 0; | |||||
/* Get memory for the station queues */ | |||||
if (!(vsi->queues = | |||||
(struct ixl_queue *) malloc(sizeof(struct ixl_queue) * | |||||
vsi->num_queues, M_DEVBUF, M_NOWAIT | M_ZERO))) { | |||||
device_printf(dev, "Unable to allocate queue memory\n"); | |||||
error = ENOMEM; | |||||
goto early; | |||||
} | |||||
for (int i = 0; i < vsi->num_queues; i++) { | |||||
que = &vsi->queues[i]; | |||||
que->num_desc = ixlv_ringsz; | |||||
que->me = i; | |||||
que->vsi = vsi; | |||||
txr = &que->txr; | |||||
txr->que = que; | |||||
txr->tail = I40E_QTX_TAIL1(que->me); | |||||
/* Initialize the TX lock */ | |||||
snprintf(txr->mtx_name, sizeof(txr->mtx_name), "%s:tx(%d)", | |||||
device_get_nameunit(dev), que->me); | |||||
mtx_init(&txr->mtx, txr->mtx_name, NULL, MTX_DEF); | |||||
/* | |||||
** Create the TX descriptor ring, the extra int is | |||||
** added as the location for HEAD WB. | |||||
*/ | |||||
tsize = roundup2((que->num_desc * | |||||
sizeof(struct i40e_tx_desc)) + | |||||
sizeof(u32), DBA_ALIGN); | |||||
if (i40e_allocate_dma_mem(&sc->hw, | |||||
&txr->dma, i40e_mem_reserved, tsize, DBA_ALIGN)) { | |||||
device_printf(dev, | |||||
"Unable to allocate TX Descriptor memory\n"); | |||||
error = ENOMEM; | |||||
goto fail; | |||||
} | |||||
txr->base = (struct i40e_tx_desc *)txr->dma.va; | |||||
bzero((void *)txr->base, tsize); | |||||
/* Now allocate transmit soft structs for the ring */ | |||||
if (ixl_allocate_tx_data(que)) { | |||||
device_printf(dev, | |||||
"Critical Failure setting up TX structures\n"); | |||||
error = ENOMEM; | |||||
goto fail; | |||||
} | |||||
/* Allocate a buf ring */ | |||||
txr->br = buf_ring_alloc(ixlv_txbrsz, M_DEVBUF, | |||||
M_WAITOK, &txr->mtx); | |||||
if (txr->br == NULL) { | |||||
device_printf(dev, | |||||
"Critical Failure setting up TX buf ring\n"); | |||||
error = ENOMEM; | |||||
goto fail; | |||||
} | |||||
/* | |||||
* Next the RX queues... | |||||
*/ | |||||
rsize = roundup2(que->num_desc * | |||||
sizeof(union i40e_rx_desc), DBA_ALIGN); | |||||
rxr = &que->rxr; | |||||
rxr->que = que; | |||||
rxr->tail = I40E_QRX_TAIL1(que->me); | |||||
/* Initialize the RX side lock */ | |||||
snprintf(rxr->mtx_name, sizeof(rxr->mtx_name), "%s:rx(%d)", | |||||
device_get_nameunit(dev), que->me); | |||||
mtx_init(&rxr->mtx, rxr->mtx_name, NULL, MTX_DEF); | |||||
if (i40e_allocate_dma_mem(&sc->hw, | |||||
&rxr->dma, i40e_mem_reserved, rsize, 4096)) { //JFV - should this be DBA? | |||||
device_printf(dev, | |||||
"Unable to allocate RX Descriptor memory\n"); | |||||
error = ENOMEM; | |||||
goto fail; | |||||
} | |||||
rxr->base = (union i40e_rx_desc *)rxr->dma.va; | |||||
bzero((void *)rxr->base, rsize); | |||||
/* Allocate receive soft structs for the ring */ | |||||
if (ixl_allocate_rx_data(que)) { | |||||
device_printf(dev, | |||||
"Critical Failure setting up receive structs\n"); | |||||
error = ENOMEM; | |||||
goto fail; | |||||
} | |||||
} | |||||
return (0); | |||||
fail: | |||||
for (int i = 0; i < vsi->num_queues; i++) { | |||||
que = &vsi->queues[i]; | |||||
rxr = &que->rxr; | |||||
txr = &que->txr; | |||||
if (rxr->base) | |||||
i40e_free_dma_mem(&sc->hw, &rxr->dma); | |||||
if (txr->base) | |||||
i40e_free_dma_mem(&sc->hw, &txr->dma); | |||||
} | |||||
free(vsi->queues, M_DEVBUF); | |||||
early: | |||||
return (error); | |||||
} | |||||
/* | |||||
** This routine is run via an vlan config EVENT, | ** This routine is run via an vlan config EVENT, | ||||
** it enables us to use the HW Filter table since | ** it enables us to use the HW Filter table since | ||||
** we can get the vlan id. This just creates the | ** we can get the vlan id. This just creates the | ||||
** entry in the soft version of the VFTA, init will | ** entry in the soft version of the VFTA, init will | ||||
** repopulate the real table. | ** repopulate the real table. | ||||
*/ | */ | ||||
static void | static void | ||||
ixlv_register_vlan(void *arg, struct ifnet *ifp, u16 vtag) | ixlv_register_vlan(void *arg, struct ifnet *ifp, u16 vtag) | ||||
Show All 21 Lines | ixlv_register_vlan(void *arg, struct ifnet *ifp, u16 vtag) | ||||
SLIST_INSERT_HEAD(sc->vlan_filters, v, next); | SLIST_INSERT_HEAD(sc->vlan_filters, v, next); | ||||
v->vlan = vtag; | v->vlan = vtag; | ||||
v->flags = IXL_FILTER_ADD; | v->flags = IXL_FILTER_ADD; | ||||
ixl_vc_enqueue(&sc->vc_mgr, &sc->add_vlan_cmd, | ixl_vc_enqueue(&sc->vc_mgr, &sc->add_vlan_cmd, | ||||
IXLV_FLAG_AQ_ADD_VLAN_FILTER, ixl_init_cmd_complete, sc); | IXLV_FLAG_AQ_ADD_VLAN_FILTER, ixl_init_cmd_complete, sc); | ||||
mtx_unlock(&sc->mtx); | mtx_unlock(&sc->mtx); | ||||
return; | return; | ||||
} | } | ||||
#endif | |||||
#if 0 | |||||
/* | /* | ||||
** This routine is run via an vlan | ** This routine is run via an vlan | ||||
** unconfig EVENT, remove our entry | ** unconfig EVENT, remove our entry | ||||
** in the soft vfta. | ** in the soft vfta. | ||||
*/ | */ | ||||
static void | static void | ||||
ixlv_unregister_vlan(void *arg, struct ifnet *ifp, u16 vtag) | ixlv_unregister_vlan(void *arg, struct ifnet *ifp, u16 vtag) | ||||
{ | { | ||||
Show All 17 Lines | SLIST_FOREACH(v, sc->vlan_filters, next) { | ||||
} | } | ||||
} | } | ||||
if (i) | if (i) | ||||
ixl_vc_enqueue(&sc->vc_mgr, &sc->del_vlan_cmd, | ixl_vc_enqueue(&sc->vc_mgr, &sc->del_vlan_cmd, | ||||
IXLV_FLAG_AQ_DEL_VLAN_FILTER, ixl_init_cmd_complete, sc); | IXLV_FLAG_AQ_DEL_VLAN_FILTER, ixl_init_cmd_complete, sc); | ||||
mtx_unlock(&sc->mtx); | mtx_unlock(&sc->mtx); | ||||
return; | return; | ||||
} | } | ||||
#endif | |||||
/* | /* | ||||
** Get a new filter and add it to the mac filter list. | ** Get a new filter and add it to the mac filter list. | ||||
*/ | */ | ||||
static struct ixlv_mac_filter * | static struct ixlv_mac_filter * | ||||
ixlv_get_mac_filter(struct ixlv_sc *sc) | ixlv_get_mac_filter(struct ixlv_sc *sc) | ||||
{ | { | ||||
struct ixlv_mac_filter *f; | struct ixlv_mac_filter *f; | ||||
Show All 22 Lines | SLIST_FOREACH(f, sc->mac_filters, next) { | ||||
} | } | ||||
} | } | ||||
if (!match) | if (!match) | ||||
f = NULL; | f = NULL; | ||||
return (f); | return (f); | ||||
} | } | ||||
static int | |||||
ixlv_teardown_adminq_msix(struct ixlv_sc *sc) | |||||
{ | |||||
device_t dev = sc->dev; | |||||
int error = 0; | |||||
if (sc->tag != NULL) { | |||||
bus_teardown_intr(dev, sc->res, sc->tag); | |||||
if (error) { | |||||
device_printf(dev, "bus_teardown_intr() for" | |||||
" interrupt 0 failed\n"); | |||||
// return (ENXIO); | |||||
} | |||||
sc->tag = NULL; | |||||
} | |||||
if (sc->res != NULL) { | |||||
bus_release_resource(dev, SYS_RES_IRQ, 1, sc->res); | |||||
if (error) { | |||||
device_printf(dev, "bus_release_resource() for" | |||||
" interrupt 0 failed\n"); | |||||
// return (ENXIO); | |||||
} | |||||
sc->res = NULL; | |||||
} | |||||
return (0); | |||||
} | |||||
/* | /* | ||||
** Admin Queue interrupt handler | ** Admin Queue interrupt handler | ||||
*/ | */ | ||||
static void | static int | ||||
ixlv_msix_adminq(void *arg) | ixlv_msix_adminq(void *arg) | ||||
{ | { | ||||
struct ixlv_sc *sc = arg; | struct ixlv_sc *sc = arg; | ||||
struct i40e_hw *hw = &sc->hw; | struct i40e_hw *hw = &sc->hw; | ||||
u32 reg, mask; | // device_t dev = sc->dev; | ||||
u32 reg; | |||||
bool do_task = FALSE; | |||||
++sc->admin_irq; | |||||
reg = rd32(hw, I40E_VFINT_ICR01); | reg = rd32(hw, I40E_VFINT_ICR01); | ||||
mask = rd32(hw, I40E_VFINT_ICR0_ENA1); | |||||
reg = rd32(hw, I40E_VFINT_DYN_CTL01); | /* Check on the cause */ | ||||
reg |= I40E_VFINT_DYN_CTL01_CLEARPBA_MASK; | if (reg & I40E_VFINT_ICR0_ADMINQ_MASK) | ||||
wr32(hw, I40E_VFINT_DYN_CTL01, reg); | do_task = TRUE; | ||||
/* schedule task */ | if (do_task) | ||||
taskqueue_enqueue(sc->tq, &sc->aq_irq); | iflib_admin_intr_deferred(sc->vsi.ctx); | ||||
return; | else | ||||
ixlv_enable_adminq_irq(hw); | |||||
return (FILTER_HANDLED); | |||||
} | } | ||||
void | void | ||||
ixlv_enable_intr(struct ixl_vsi *vsi) | ixlv_enable_intr(struct ixl_vsi *vsi) | ||||
{ | { | ||||
struct i40e_hw *hw = vsi->hw; | struct i40e_hw *hw = vsi->hw; | ||||
struct ixl_queue *que = vsi->queues; | struct ixl_rx_queue *que = vsi->rx_queues; | ||||
ixlv_enable_adminq_irq(hw); | ixlv_enable_adminq_irq(hw); | ||||
for (int i = 0; i < vsi->num_queues; i++, que++) | for (int i = 0; i < vsi->num_rx_queues; i++, que++) | ||||
ixlv_enable_queue_irq(hw, que->me); | ixlv_enable_queue_irq(hw, que->rxr.me); | ||||
} | } | ||||
void | void | ||||
ixlv_disable_intr(struct ixl_vsi *vsi) | ixlv_disable_intr(struct ixl_vsi *vsi) | ||||
{ | { | ||||
struct i40e_hw *hw = vsi->hw; | struct i40e_hw *hw = vsi->hw; | ||||
struct ixl_queue *que = vsi->queues; | struct ixl_rx_queue *que = vsi->rx_queues; | ||||
ixlv_disable_adminq_irq(hw); | ixlv_disable_adminq_irq(hw); | ||||
for (int i = 0; i < vsi->num_queues; i++, que++) | for (int i = 0; i < vsi->num_rx_queues; i++, que++) | ||||
ixlv_disable_queue_irq(hw, que->me); | ixlv_disable_queue_irq(hw, que->rxr.me); | ||||
} | } | ||||
static void | static void | ||||
ixlv_disable_adminq_irq(struct i40e_hw *hw) | ixlv_disable_adminq_irq(struct i40e_hw *hw) | ||||
{ | { | ||||
wr32(hw, I40E_VFINT_DYN_CTL01, 0); | wr32(hw, I40E_VFINT_DYN_CTL01, 0); | ||||
wr32(hw, I40E_VFINT_ICR0_ENA1, 0); | wr32(hw, I40E_VFINT_ICR0_ENA1, 0); | ||||
/* flush */ | /* flush */ | ||||
rd32(hw, I40E_VFGEN_RSTAT); | rd32(hw, I40E_VFGEN_RSTAT); | ||||
return; | |||||
} | } | ||||
static void | static void | ||||
ixlv_enable_adminq_irq(struct i40e_hw *hw) | ixlv_enable_adminq_irq(struct i40e_hw *hw) | ||||
{ | { | ||||
wr32(hw, I40E_VFINT_DYN_CTL01, | wr32(hw, I40E_VFINT_DYN_CTL01, | ||||
I40E_VFINT_DYN_CTL01_INTENA_MASK | | I40E_VFINT_DYN_CTL01_INTENA_MASK | | ||||
I40E_VFINT_DYN_CTL01_ITR_INDX_MASK); | I40E_VFINT_DYN_CTL01_ITR_INDX_MASK); | ||||
wr32(hw, I40E_VFINT_ICR0_ENA1, I40E_VFINT_ICR0_ENA1_ADMINQ_MASK); | wr32(hw, I40E_VFINT_ICR0_ENA1, I40E_VFINT_ICR0_ENA1_ADMINQ_MASK); | ||||
/* flush */ | /* flush */ | ||||
rd32(hw, I40E_VFGEN_RSTAT); | rd32(hw, I40E_VFGEN_RSTAT); | ||||
return; | |||||
} | } | ||||
static void | static void | ||||
ixlv_enable_queue_irq(struct i40e_hw *hw, int id) | ixlv_enable_queue_irq(struct i40e_hw *hw, int id) | ||||
{ | { | ||||
u32 reg; | u32 reg; | ||||
reg = I40E_VFINT_DYN_CTLN1_INTENA_MASK | | reg = I40E_VFINT_DYN_CTLN1_INTENA_MASK | | ||||
Show All 14 Lines | |||||
/* | /* | ||||
* Get initial ITR values from tunable values. | * Get initial ITR values from tunable values. | ||||
*/ | */ | ||||
static void | static void | ||||
ixlv_configure_itr(struct ixlv_sc *sc) | ixlv_configure_itr(struct ixlv_sc *sc) | ||||
{ | { | ||||
struct i40e_hw *hw = &sc->hw; | struct i40e_hw *hw = &sc->hw; | ||||
struct ixl_vsi *vsi = &sc->vsi; | struct ixl_vsi *vsi = &sc->vsi; | ||||
struct ixl_queue *que = vsi->queues; | struct ixl_rx_queue *rx_que = vsi->rx_queues; | ||||
vsi->rx_itr_setting = ixlv_rx_itr; | vsi->rx_itr_setting = ixlv_rx_itr; | ||||
vsi->tx_itr_setting = ixlv_tx_itr; | |||||
for (int i = 0; i < vsi->num_queues; i++, que++) { | for (int i = 0; i < vsi->num_rx_queues; i++, rx_que++) { | ||||
struct tx_ring *txr = &que->txr; | // struct tx_ring *txr = &que->txr; | ||||
struct rx_ring *rxr = &que->rxr; | struct rx_ring *rxr = &rx_que->rxr; | ||||
wr32(hw, I40E_VFINT_ITRN1(IXL_RX_ITR, i), | wr32(hw, I40E_VFINT_ITRN1(IXL_RX_ITR, i), | ||||
vsi->rx_itr_setting); | vsi->rx_itr_setting); | ||||
rxr->itr = vsi->rx_itr_setting; | rxr->itr = vsi->rx_itr_setting; | ||||
rxr->latency = IXL_AVE_LATENCY; | rxr->latency = IXL_AVE_LATENCY; | ||||
wr32(hw, I40E_VFINT_ITRN1(IXL_TX_ITR, i), | |||||
vsi->tx_itr_setting); | |||||
txr->itr = vsi->tx_itr_setting; | |||||
txr->latency = IXL_AVE_LATENCY; | |||||
} | } | ||||
} | } | ||||
/* | /* | ||||
** Provide a update to the queue RX | ** Provide a update to the queue RX | ||||
** interrupt moderation value. | ** interrupt moderation value. | ||||
*/ | */ | ||||
static void | static void | ||||
ixlv_set_queue_rx_itr(struct ixl_queue *que) | ixlv_set_queue_rx_itr(struct ixl_rx_queue *que) | ||||
{ | { | ||||
struct ixl_vsi *vsi = que->vsi; | struct ixl_vsi *vsi = que->vsi; | ||||
struct i40e_hw *hw = vsi->hw; | struct i40e_hw *hw = vsi->hw; | ||||
struct rx_ring *rxr = &que->rxr; | struct rx_ring *rxr = &que->rxr; | ||||
u16 rx_itr; | u16 rx_itr; | ||||
u16 rx_latency = 0; | u16 rx_latency = 0; | ||||
int rx_bytes; | int rx_bytes; | ||||
Show All 34 Lines | if (ixlv_dynamic_rx_itr) { | ||||
rxr->latency = rx_latency; | rxr->latency = rx_latency; | ||||
if (rx_itr != rxr->itr) { | if (rx_itr != rxr->itr) { | ||||
/* do an exponential smoothing */ | /* do an exponential smoothing */ | ||||
rx_itr = (10 * rx_itr * rxr->itr) / | rx_itr = (10 * rx_itr * rxr->itr) / | ||||
((9 * rx_itr) + rxr->itr); | ((9 * rx_itr) + rxr->itr); | ||||
rxr->itr = min(rx_itr, IXL_MAX_ITR); | rxr->itr = min(rx_itr, IXL_MAX_ITR); | ||||
wr32(hw, I40E_VFINT_ITRN1(IXL_RX_ITR, | wr32(hw, I40E_VFINT_ITRN1(IXL_RX_ITR, | ||||
que->me), rxr->itr); | que->rxr.me), rxr->itr); | ||||
} | } | ||||
} else { /* We may have have toggled to non-dynamic */ | } else { /* We may have have toggled to non-dynamic */ | ||||
if (vsi->rx_itr_setting & IXL_ITR_DYNAMIC) | if (vsi->rx_itr_setting & IXL_ITR_DYNAMIC) | ||||
vsi->rx_itr_setting = ixlv_rx_itr; | vsi->rx_itr_setting = ixlv_rx_itr; | ||||
/* Update the hardware if needed */ | /* Update the hardware if needed */ | ||||
if (rxr->itr != vsi->rx_itr_setting) { | if (rxr->itr != vsi->rx_itr_setting) { | ||||
rxr->itr = vsi->rx_itr_setting; | rxr->itr = vsi->rx_itr_setting; | ||||
wr32(hw, I40E_VFINT_ITRN1(IXL_RX_ITR, | wr32(hw, I40E_VFINT_ITRN1(IXL_RX_ITR, | ||||
que->me), rxr->itr); | que->rxr.me), rxr->itr); | ||||
} | } | ||||
} | } | ||||
rxr->bytes = 0; | rxr->bytes = 0; | ||||
rxr->packets = 0; | rxr->packets = 0; | ||||
return; | return; | ||||
} | } | ||||
#if 0 | |||||
/* | /* | ||||
** Provide a update to the queue TX | * TODO: Make sure this properly handles admin queue / single rx queue intr | ||||
** interrupt moderation value. | * | ||||
* Use this intr handler when there is only one msix interrupt (shared between | |||||
* admin queue and RX queue) | |||||
*/ | */ | ||||
static void | int | ||||
ixlv_set_queue_tx_itr(struct ixl_queue *que) | ixlv_intr(void *arg) | ||||
{ | { | ||||
struct ixl_vsi *vsi = que->vsi; | struct ixlv_sc *sc = arg; | ||||
struct i40e_hw *hw = vsi->hw; | struct i40e_hw *hw = &sc->hw; | ||||
struct tx_ring *txr = &que->txr; | struct ixl_vsi *vsi = &sc->vsi; | ||||
u16 tx_itr; | struct ixl_rx_queue *que = vsi->rx_queues; | ||||
u16 tx_latency = 0; | u32 icr0; | ||||
int tx_bytes; | |||||
sc->admin_irq++; | |||||
/* Idle, do nothing */ | icr0 = rd32(hw, I40E_PFINT_ICR0); | ||||
if (txr->bytes == 0) | |||||
return; | |||||
if (ixlv_dynamic_tx_itr) { | if (icr0 & I40E_PFINT_ICR0_ADMINQ_MASK) | ||||
tx_bytes = txr->bytes/txr->itr; | iflib_admin_intr_deferred(vsi->ctx); | ||||
tx_itr = txr->itr; | |||||
switch (txr->latency) { | ixlv_enable_adminq_irq(hw); | ||||
case IXL_LOW_LATENCY: | |||||
if (tx_bytes > 10) { | |||||
tx_latency = IXL_AVE_LATENCY; | |||||
tx_itr = IXL_ITR_20K; | |||||
} | |||||
break; | |||||
case IXL_AVE_LATENCY: | |||||
if (tx_bytes > 20) { | |||||
tx_latency = IXL_BULK_LATENCY; | |||||
tx_itr = IXL_ITR_8K; | |||||
} else if (tx_bytes <= 10) { | |||||
tx_latency = IXL_LOW_LATENCY; | |||||
tx_itr = IXL_ITR_100K; | |||||
} | |||||
break; | |||||
case IXL_BULK_LATENCY: | |||||
if (tx_bytes <= 20) { | |||||
tx_latency = IXL_AVE_LATENCY; | |||||
tx_itr = IXL_ITR_20K; | |||||
} | |||||
break; | |||||
} | |||||
txr->latency = tx_latency; | if (icr0 & I40E_PFINT_ICR0_QUEUE_0_MASK) | ||||
return (FILTER_SCHEDULE_THREAD); | |||||
if (tx_itr != txr->itr) { | else | ||||
/* do an exponential smoothing */ | return (FILTER_HANDLED); | ||||
tx_itr = (10 * tx_itr * txr->itr) / | |||||
((9 * tx_itr) + txr->itr); | |||||
txr->itr = min(tx_itr, IXL_MAX_ITR); | |||||
wr32(hw, I40E_VFINT_ITRN1(IXL_TX_ITR, | |||||
que->me), txr->itr); | |||||
} | } | ||||
#endif | |||||
} else { /* We may have have toggled to non-dynamic */ | |||||
if (vsi->tx_itr_setting & IXL_ITR_DYNAMIC) | |||||
vsi->tx_itr_setting = ixlv_tx_itr; | |||||
/* Update the hardware if needed */ | |||||
if (txr->itr != vsi->tx_itr_setting) { | |||||
txr->itr = vsi->tx_itr_setting; | |||||
wr32(hw, I40E_VFINT_ITRN1(IXL_TX_ITR, | |||||
que->me), txr->itr); | |||||
} | |||||
} | |||||
txr->bytes = 0; | |||||
txr->packets = 0; | |||||
return; | |||||
} | |||||
/* | |||||
** | |||||
** MSIX Interrupt Handlers and Tasklets | |||||
** | |||||
*/ | |||||
static void | |||||
ixlv_handle_que(void *context, int pending) | |||||
{ | |||||
struct ixl_queue *que = context; | |||||
struct ixl_vsi *vsi = que->vsi; | |||||
struct i40e_hw *hw = vsi->hw; | |||||
struct tx_ring *txr = &que->txr; | |||||
struct ifnet *ifp = vsi->ifp; | |||||
bool more; | |||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | |||||
more = ixl_rxeof(que, IXL_RX_LIMIT); | |||||
mtx_lock(&txr->mtx); | |||||
ixl_txeof(que); | |||||
if (!drbr_empty(ifp, txr->br)) | |||||
ixl_mq_start_locked(ifp, txr); | |||||
mtx_unlock(&txr->mtx); | |||||
if (more) { | |||||
taskqueue_enqueue(que->tq, &que->task); | |||||
return; | |||||
} | |||||
} | |||||
/* Reenable this interrupt - hmmm */ | |||||
ixlv_enable_queue_irq(hw, que->me); | |||||
return; | |||||
} | |||||
/********************************************************************* | /********************************************************************* | ||||
* | * | ||||
* MSIX Queue Interrupt Service routine | * MSIX Queue Interrupt Service routine | ||||
* | * | ||||
**********************************************************************/ | **********************************************************************/ | ||||
static void | static int | ||||
ixlv_msix_que(void *arg) | ixlv_msix_que(void *arg) | ||||
{ | { | ||||
struct ixl_queue *que = arg; | struct ixl_rx_queue *que = arg; | ||||
struct ixl_vsi *vsi = que->vsi; | |||||
struct i40e_hw *hw = vsi->hw; | |||||
struct tx_ring *txr = &que->txr; | |||||
bool more_tx, more_rx; | |||||
/* Spurious interrupts are ignored */ | |||||
if (!(vsi->ifp->if_drv_flags & IFF_DRV_RUNNING)) | |||||
return; | |||||
++que->irqs; | ++que->irqs; | ||||
more_rx = ixl_rxeof(que, IXL_RX_LIMIT); | |||||
mtx_lock(&txr->mtx); | |||||
more_tx = ixl_txeof(que); | |||||
/* | |||||
** Make certain that if the stack | |||||
** has anything queued the task gets | |||||
** scheduled to handle it. | |||||
*/ | |||||
if (!drbr_empty(vsi->ifp, txr->br)) | |||||
more_tx = 1; | |||||
mtx_unlock(&txr->mtx); | |||||
ixlv_set_queue_rx_itr(que); | ixlv_set_queue_rx_itr(que); | ||||
ixlv_set_queue_tx_itr(que); | |||||
if (more_tx || more_rx) | // TODO: Re-enable queue? Or is that done in handler? | ||||
taskqueue_enqueue(que->tq, &que->task); | |||||
else | |||||
ixlv_enable_queue_irq(hw, que->me); | |||||
return; | return (FILTER_SCHEDULE_THREAD); | ||||
} | } | ||||
#if 0 | |||||
/********************************************************************* | /********************************************************************* | ||||
* | |||||
* Media Ioctl callback | |||||
* | |||||
* This routine is called whenever the user queries the status of | |||||
* the interface using ifconfig. | |||||
* | |||||
**********************************************************************/ | |||||
static void | |||||
ixlv_media_status(struct ifnet * ifp, struct ifmediareq * ifmr) | |||||
{ | |||||
struct ixl_vsi *vsi = ifp->if_softc; | |||||
struct ixlv_sc *sc = vsi->back; | |||||
INIT_DBG_IF(ifp, "begin"); | |||||
mtx_lock(&sc->mtx); | |||||
ixlv_update_link_status(sc); | |||||
ifmr->ifm_status = IFM_AVALID; | |||||
ifmr->ifm_active = IFM_ETHER; | |||||
if (!sc->link_up) { | |||||
mtx_unlock(&sc->mtx); | |||||
INIT_DBG_IF(ifp, "end: link not up"); | |||||
return; | |||||
} | |||||
ifmr->ifm_status |= IFM_ACTIVE; | |||||
/* Hardware is always full-duplex */ | |||||
ifmr->ifm_active |= IFM_FDX; | |||||
mtx_unlock(&sc->mtx); | |||||
INIT_DBG_IF(ifp, "end"); | |||||
return; | |||||
} | |||||
/********************************************************************* | |||||
* | |||||
* Media Ioctl callback | |||||
* | |||||
* This routine is called when the user changes speed/duplex using | |||||
* media/mediopt option with ifconfig. | |||||
* | |||||
**********************************************************************/ | |||||
static int | |||||
ixlv_media_change(struct ifnet * ifp) | |||||
{ | |||||
struct ixl_vsi *vsi = ifp->if_softc; | |||||
struct ifmedia *ifm = &vsi->media; | |||||
INIT_DBG_IF(ifp, "begin"); | |||||
if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) | |||||
return (EINVAL); | |||||
INIT_DBG_IF(ifp, "end"); | |||||
return (0); | |||||
} | |||||
/********************************************************************* | |||||
* Multicast Initialization | * Multicast Initialization | ||||
* | * | ||||
* This routine is called by init to reset a fresh state. | * This routine is called by init to reset a fresh state. | ||||
* | * | ||||
**********************************************************************/ | **********************************************************************/ | ||||
static void | static void | ||||
ixlv_init_multi(struct ixl_vsi *vsi) | ixlv_init_multi(struct ixl_vsi *vsi) | ||||
▲ Show 20 Lines • Show All 122 Lines • ▼ Show 20 Lines | ixlv_del_multi(struct ixl_vsi *vsi) | ||||
if (mcnt > 0) | if (mcnt > 0) | ||||
ixl_vc_enqueue(&sc->vc_mgr, &sc->del_multi_cmd, | ixl_vc_enqueue(&sc->vc_mgr, &sc->del_multi_cmd, | ||||
IXLV_FLAG_AQ_DEL_MAC_FILTER, ixl_init_cmd_complete, | IXLV_FLAG_AQ_DEL_MAC_FILTER, ixl_init_cmd_complete, | ||||
sc); | sc); | ||||
IOCTL_DBG_IF(ifp, "end"); | IOCTL_DBG_IF(ifp, "end"); | ||||
} | } | ||||
#endif | |||||
/********************************************************************* | /********************************************************************* | ||||
* Timer routine | |||||
* | * | ||||
* This routine checks for link status,updates statistics, | |||||
* and runs the watchdog check. | |||||
* | |||||
**********************************************************************/ | |||||
static void | |||||
ixlv_local_timer(void *arg) | |||||
{ | |||||
struct ixlv_sc *sc = arg; | |||||
struct i40e_hw *hw = &sc->hw; | |||||
struct ixl_vsi *vsi = &sc->vsi; | |||||
struct ixl_queue *que = vsi->queues; | |||||
device_t dev = sc->dev; | |||||
struct tx_ring *txr; | |||||
int hung = 0; | |||||
u32 mask, val; | |||||
s32 timer, new_timer; | |||||
IXLV_CORE_LOCK_ASSERT(sc); | |||||
/* If Reset is in progress just bail */ | |||||
if (sc->init_state == IXLV_RESET_PENDING) | |||||
return; | |||||
/* Check for when PF triggers a VF reset */ | |||||
val = rd32(hw, I40E_VFGEN_RSTAT) & | |||||
I40E_VFGEN_RSTAT_VFR_STATE_MASK; | |||||
if (val != I40E_VFR_VFACTIVE | |||||
&& val != I40E_VFR_COMPLETED) { | |||||
DDPRINTF(dev, "reset in progress! (%d)", val); | |||||
return; | |||||
} | |||||
ixlv_request_stats(sc); | |||||
/* clean and process any events */ | |||||
taskqueue_enqueue(sc->tq, &sc->aq_irq); | |||||
/* | |||||
** Check status on the queues for a hang | |||||
*/ | |||||
mask = (I40E_VFINT_DYN_CTLN1_INTENA_MASK | | |||||
I40E_VFINT_DYN_CTLN1_SWINT_TRIG_MASK | | |||||
I40E_VFINT_DYN_CTLN1_ITR_INDX_MASK); | |||||
for (int i = 0; i < vsi->num_queues; i++, que++) { | |||||
txr = &que->txr; | |||||
timer = atomic_load_acq_32(&txr->watchdog_timer); | |||||
if (timer > 0) { | |||||
new_timer = timer - hz; | |||||
if (new_timer <= 0) { | |||||
atomic_store_rel_32(&txr->watchdog_timer, -1); | |||||
device_printf(dev, "WARNING: queue %d " | |||||
"appears to be hung!\n", que->me); | |||||
++hung; | |||||
} else { | |||||
/* | |||||
* If this fails, that means something in the TX path has updated | |||||
* the watchdog, so it means the TX path is still working and | |||||
* the watchdog doesn't need to countdown. | |||||
*/ | |||||
atomic_cmpset_rel_32(&txr->watchdog_timer, timer, new_timer); | |||||
/* Any queues with outstanding work get a sw irq */ | |||||
wr32(hw, I40E_VFINT_DYN_CTLN1(que->me), mask); | |||||
} | |||||
} | |||||
} | |||||
/* Reset when a queue shows hung */ | |||||
if (hung) | |||||
goto hung; | |||||
callout_reset(&sc->timer, hz, ixlv_local_timer, sc); | |||||
return; | |||||
hung: | |||||
device_printf(dev, "WARNING: Resetting!\n"); | |||||
sc->init_state = IXLV_RESET_REQUIRED; | |||||
sc->watchdog_events++; | |||||
ixlv_stop(sc); | |||||
ixlv_init_locked(sc); | |||||
} | |||||
/* | |||||
** Note: this routine updates the OS on the link state | |||||
** the real check of the hardware only happens with | |||||
** a link interrupt. | |||||
*/ | |||||
void | |||||
ixlv_update_link_status(struct ixlv_sc *sc) | |||||
{ | |||||
struct ixl_vsi *vsi = &sc->vsi; | |||||
struct ifnet *ifp = vsi->ifp; | |||||
if (sc->link_up){ | |||||
if (vsi->link_active == FALSE) { | |||||
if (bootverbose) | |||||
if_printf(ifp,"Link is Up, %d Gbps\n", | |||||
(sc->link_speed == I40E_LINK_SPEED_40GB) ? 40:10); | |||||
vsi->link_active = TRUE; | |||||
if_link_state_change(ifp, LINK_STATE_UP); | |||||
} | |||||
} else { /* Link down */ | |||||
if (vsi->link_active == TRUE) { | |||||
if (bootverbose) | |||||
if_printf(ifp,"Link is Down\n"); | |||||
if_link_state_change(ifp, LINK_STATE_DOWN); | |||||
vsi->link_active = FALSE; | |||||
} | |||||
} | |||||
return; | |||||
} | |||||
/********************************************************************* | |||||
* | |||||
* This routine disables all traffic on the adapter by issuing a | * This routine disables all traffic on the adapter by issuing a | ||||
* global reset on the MAC and deallocates TX/RX buffers. | * global reset on the MAC and deallocates TX/RX buffers. | ||||
* | * | ||||
**********************************************************************/ | **********************************************************************/ | ||||
static void | static void | ||||
ixlv_stop(struct ixlv_sc *sc) | ixlv_stop(struct ixlv_sc *sc) | ||||
{ | { | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
int start; | int start; | ||||
ifp = sc->vsi.ifp; | ifp = sc->vsi.ifp; | ||||
INIT_DBG_IF(ifp, "begin"); | INIT_DBG_IF(ifp, "begin"); | ||||
IXLV_CORE_LOCK_ASSERT(sc); | |||||
ixl_vc_flush(&sc->vc_mgr); | ixl_vc_flush(&sc->vc_mgr); | ||||
ixlv_disable_queues(sc); | ixlv_disable_queues(sc); | ||||
start = ticks; | start = ticks; | ||||
while ((ifp->if_drv_flags & IFF_DRV_RUNNING) && | while ((ifp->if_drv_flags & IFF_DRV_RUNNING) && | ||||
((ticks - start) < hz/10)) | ((ticks - start) < hz/10)) | ||||
ixlv_do_adminq_locked(sc); | ixlv_do_adminq_locked(sc); | ||||
/* Stop the local timer */ | /* Stop the local timer */ | ||||
callout_stop(&sc->timer); | callout_stop(&sc->timer); | ||||
INIT_DBG_IF(ifp, "end"); | INIT_DBG_IF(ifp, "end"); | ||||
} | } | ||||
/********************************************************************* | |||||
* | |||||
* Free all station queue structs. | |||||
* | |||||
**********************************************************************/ | |||||
static void | static void | ||||
ixlv_free_queues(struct ixl_vsi *vsi) | ixlv_if_stop(if_ctx_t ctx) | ||||
{ | { | ||||
struct ixl_vsi *vsi = iflib_get_softc(ctx); | |||||
struct ixlv_sc *sc = (struct ixlv_sc *)vsi->back; | struct ixlv_sc *sc = (struct ixlv_sc *)vsi->back; | ||||
struct ixl_queue *que = vsi->queues; | |||||
for (int i = 0; i < vsi->num_queues; i++, que++) { | ixlv_stop(sc); | ||||
struct tx_ring *txr = &que->txr; | |||||
struct rx_ring *rxr = &que->rxr; | |||||
if (!mtx_initialized(&txr->mtx)) /* uninitialized */ | |||||
continue; | |||||
IXL_TX_LOCK(txr); | |||||
ixl_free_que_tx(que); | |||||
if (txr->base) | |||||
i40e_free_dma_mem(&sc->hw, &txr->dma); | |||||
IXL_TX_UNLOCK(txr); | |||||
IXL_TX_LOCK_DESTROY(txr); | |||||
if (!mtx_initialized(&rxr->mtx)) /* uninitialized */ | |||||
continue; | |||||
IXL_RX_LOCK(rxr); | |||||
ixl_free_que_rx(que); | |||||
if (rxr->base) | |||||
i40e_free_dma_mem(&sc->hw, &rxr->dma); | |||||
IXL_RX_UNLOCK(rxr); | |||||
IXL_RX_LOCK_DESTROY(rxr); | |||||
} | } | ||||
free(vsi->queues, M_DEVBUF); | |||||
} | |||||
static void | static void | ||||
ixlv_config_rss_reg(struct ixlv_sc *sc) | ixlv_config_rss_reg(struct ixlv_sc *sc) | ||||
{ | { | ||||
struct i40e_hw *hw = &sc->hw; | struct i40e_hw *hw = &sc->hw; | ||||
struct ixl_vsi *vsi = &sc->vsi; | struct ixl_vsi *vsi = &sc->vsi; | ||||
u32 lut = 0; | u32 lut = 0; | ||||
u64 set_hena = 0, hena; | u64 set_hena = 0, hena; | ||||
int i, j, que_id; | int i, j, que_id; | ||||
u32 rss_seed[IXL_RSS_KEY_SIZE_REG]; | u32 rss_seed[IXL_RSS_KEY_SIZE_REG]; | ||||
#ifdef RSS | #ifdef RSS | ||||
u32 rss_hash_config; | u32 rss_hash_config; | ||||
#endif | #endif | ||||
/* Don't set up RSS if using a single queue */ | /* Don't set up RSS if using a single queue */ | ||||
if (vsi->num_queues == 1) { | if (vsi->num_rx_queues == 1) { | ||||
wr32(hw, I40E_VFQF_HENA(0), 0); | wr32(hw, I40E_VFQF_HENA(0), 0); | ||||
wr32(hw, I40E_VFQF_HENA(1), 0); | wr32(hw, I40E_VFQF_HENA(1), 0); | ||||
ixl_flush(hw); | ixl_flush(hw); | ||||
return; | return; | ||||
} | } | ||||
#ifdef RSS | #ifdef RSS | ||||
/* Fetch the configured RSS key */ | /* Fetch the configured RSS key */ | ||||
Show All 29 Lines | #endif | ||||
hena = (u64)rd32(hw, I40E_VFQF_HENA(0)) | | hena = (u64)rd32(hw, I40E_VFQF_HENA(0)) | | ||||
((u64)rd32(hw, I40E_VFQF_HENA(1)) << 32); | ((u64)rd32(hw, I40E_VFQF_HENA(1)) << 32); | ||||
hena |= set_hena; | hena |= set_hena; | ||||
wr32(hw, I40E_VFQF_HENA(0), (u32)hena); | wr32(hw, I40E_VFQF_HENA(0), (u32)hena); | ||||
wr32(hw, I40E_VFQF_HENA(1), (u32)(hena >> 32)); | wr32(hw, I40E_VFQF_HENA(1), (u32)(hena >> 32)); | ||||
/* Populate the LUT with max no. of queues in round robin fashion */ | /* Populate the LUT with max no. of queues in round robin fashion */ | ||||
for (i = 0, j = 0; i < IXL_RSS_VSI_LUT_SIZE; i++, j++) { | for (i = 0, j = 0; i < IXL_RSS_VSI_LUT_SIZE; i++, j++) { | ||||
if (j == vsi->num_queues) | if (j == vsi->num_rx_queues) | ||||
j = 0; | j = 0; | ||||
#ifdef RSS | #ifdef RSS | ||||
/* | /* | ||||
* Fetch the RSS bucket id for the given indirection entry. | * Fetch the RSS bucket id for the given indirection entry. | ||||
* Cap it at the number of configured buckets (which is | * Cap it at the number of configured buckets (which is | ||||
* num_queues.) | * num_queues.) | ||||
*/ | */ | ||||
que_id = rss_get_indirection_to_bucket(i); | que_id = rss_get_indirection_to_bucket(i); | ||||
▲ Show 20 Lines • Show All 115 Lines • ▼ Show 20 Lines | ixlv_del_mac_filter(struct ixlv_sc *sc, u8 *macaddr) | ||||
f = ixlv_find_mac_filter(sc, macaddr); | f = ixlv_find_mac_filter(sc, macaddr); | ||||
if (f == NULL) | if (f == NULL) | ||||
return (ENOENT); | return (ENOENT); | ||||
f->flags |= IXL_FILTER_DEL; | f->flags |= IXL_FILTER_DEL; | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | |||||
** Tasklet handler for MSIX Adminq interrupts | |||||
** - done outside interrupt context since it might sleep | |||||
*/ | |||||
static void | static void | ||||
ixlv_do_adminq(void *context, int pending) | |||||
{ | |||||
struct ixlv_sc *sc = context; | |||||
mtx_lock(&sc->mtx); | |||||
ixlv_do_adminq_locked(sc); | |||||
mtx_unlock(&sc->mtx); | |||||
return; | |||||
} | |||||
static void | |||||
ixlv_do_adminq_locked(struct ixlv_sc *sc) | ixlv_do_adminq_locked(struct ixlv_sc *sc) | ||||
{ | { | ||||
struct i40e_hw *hw = &sc->hw; | struct i40e_hw *hw = &sc->hw; | ||||
struct i40e_arq_event_info event; | struct i40e_arq_event_info event; | ||||
struct i40e_virtchnl_msg *v_msg; | struct i40e_virtchnl_msg *v_msg; | ||||
device_t dev = sc->dev; | device_t dev = sc->dev; | ||||
u16 result = 0; | u16 result = 0; | ||||
u32 reg, oldreg; | u32 reg, oldreg; | ||||
i40e_status ret; | i40e_status ret; | ||||
bool aq_error = false; | bool aq_error = false; | ||||
IXLV_CORE_LOCK_ASSERT(sc); | |||||
event.buf_len = IXL_AQ_BUF_SZ; | event.buf_len = IXL_AQ_BUF_SZ; | ||||
event.msg_buf = sc->aq_buffer; | event.msg_buf = sc->aq_buffer; | ||||
v_msg = (struct i40e_virtchnl_msg *)&event.desc; | v_msg = (struct i40e_virtchnl_msg *)&event.desc; | ||||
do { | do { | ||||
ret = i40e_clean_arq_element(hw, &event, &result); | ret = i40e_clean_arq_element(hw, &event, &result); | ||||
if (ret) | if (ret) | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | ixlv_do_adminq_locked(struct ixlv_sc *sc) | ||||
if (oldreg != reg) | if (oldreg != reg) | ||||
wr32(hw, hw->aq.asq.len, reg); | wr32(hw, hw->aq.asq.len, reg); | ||||
if (aq_error) { | if (aq_error) { | ||||
/* Need to reset adapter */ | /* Need to reset adapter */ | ||||
device_printf(dev, "WARNING: Resetting!\n"); | device_printf(dev, "WARNING: Resetting!\n"); | ||||
sc->init_state = IXLV_RESET_REQUIRED; | sc->init_state = IXLV_RESET_REQUIRED; | ||||
ixlv_stop(sc); | ixlv_stop(sc); | ||||
ixlv_init_locked(sc); | // TODO: Make stop/init calls match | ||||
ixlv_if_init(sc->vsi.ctx); | |||||
} | } | ||||
ixlv_enable_adminq_irq(hw); | ixlv_enable_adminq_irq(hw); | ||||
} | } | ||||
static void | static void | ||||
ixlv_add_sysctls(struct ixlv_sc *sc) | ixlv_add_sysctls(struct ixlv_sc *sc) | ||||
{ | { | ||||
device_t dev = sc->dev; | device_t dev = sc->dev; | ||||
struct ixl_vsi *vsi = &sc->vsi; | struct ixl_vsi *vsi = &sc->vsi; | ||||
struct i40e_eth_stats *es = &vsi->eth_stats; | struct i40e_eth_stats *es = &vsi->eth_stats; | ||||
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); | struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); | ||||
struct sysctl_oid *tree = device_get_sysctl_tree(dev); | struct sysctl_oid *tree = device_get_sysctl_tree(dev); | ||||
struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree); | struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree); | ||||
struct sysctl_oid *vsi_node, *queue_node; | struct sysctl_oid *vsi_node; //, *queue_node; | ||||
struct sysctl_oid_list *vsi_list, *queue_list; | struct sysctl_oid_list *vsi_list; //, *queue_list; | ||||
#define QUEUE_NAME_LEN 32 | #define QUEUE_NAME_LEN 32 | ||||
char queue_namebuf[QUEUE_NAME_LEN]; | //char queue_namebuf[QUEUE_NAME_LEN]; | ||||
struct ixl_queue *queues = vsi->queues; | #if 0 | ||||
struct ixl_tx_queue *tx_queues = vsi->tx_queues; | |||||
struct ixl_rx_queue *rx_queues = vsi->rx_queues; | |||||
struct tx_ring *txr; | struct tx_ring *txr; | ||||
struct rx_ring *rxr; | struct rx_ring *rxr; | ||||
#endif | |||||
/* Driver statistics sysctls */ | /* Driver statistics sysctls */ | ||||
SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "watchdog_events", | SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "watchdog_events", | ||||
CTLFLAG_RD, &sc->watchdog_events, | CTLFLAG_RD, &sc->watchdog_events, | ||||
"Watchdog timeouts"); | "Watchdog timeouts"); | ||||
SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "admin_irq", | SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "admin_irq", | ||||
CTLFLAG_RD, &sc->admin_irq, | CTLFLAG_RD, &sc->admin_irq, | ||||
"Admin Queue IRQ Handled"); | "Admin Queue IRQ Handled"); | ||||
Show All 28 Lines | #endif | ||||
while (entry->stat != NULL) | while (entry->stat != NULL) | ||||
{ | { | ||||
SYSCTL_ADD_QUAD(ctx, child, OID_AUTO, entry->name, | SYSCTL_ADD_QUAD(ctx, child, OID_AUTO, entry->name, | ||||
CTLFLAG_RD, entry->stat, | CTLFLAG_RD, entry->stat, | ||||
entry->description); | entry->description); | ||||
entry++; | entry++; | ||||
} | } | ||||
#if 0 | |||||
/* Queue sysctls */ | /* Queue sysctls */ | ||||
for (int q = 0; q < vsi->num_queues; q++) { | for (int q = 0; q < vsi->num_queues; q++) { | ||||
snprintf(queue_namebuf, QUEUE_NAME_LEN, "que%d", q); | snprintf(queue_namebuf, QUEUE_NAME_LEN, "que%d", q); | ||||
queue_node = SYSCTL_ADD_NODE(ctx, vsi_list, OID_AUTO, queue_namebuf, | queue_node = SYSCTL_ADD_NODE(ctx, vsi_list, OID_AUTO, queue_namebuf, | ||||
CTLFLAG_RD, NULL, "Queue Name"); | CTLFLAG_RD, NULL, "Queue Name"); | ||||
queue_list = SYSCTL_CHILDREN(queue_node); | queue_list = SYSCTL_CHILDREN(queue_node); | ||||
txr = &(queues[q].txr); | txr = &(queues[q].txr); | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "qrx_head", | ||||
sizeof(struct ixl_queue), | sizeof(struct ixl_queue), | ||||
ixlv_sysctl_qrx_tail_handler, "IU", | ixlv_sysctl_qrx_tail_handler, "IU", | ||||
"Queue Receive Descriptor Tail"); | "Queue Receive Descriptor Tail"); | ||||
SYSCTL_ADD_INT(ctx, queue_list, OID_AUTO, "watchdog_timer", | SYSCTL_ADD_INT(ctx, queue_list, OID_AUTO, "watchdog_timer", | ||||
CTLFLAG_RD, &(txr.watchdog_timer), 0, | CTLFLAG_RD, &(txr.watchdog_timer), 0, | ||||
"Ticks before watchdog event is triggered"); | "Ticks before watchdog event is triggered"); | ||||
#endif | #endif | ||||
} | } | ||||
#endif | |||||
} | } | ||||
static void | static void | ||||
ixlv_init_filters(struct ixlv_sc *sc) | ixlv_init_filters(struct ixlv_sc *sc) | ||||
{ | { | ||||
sc->mac_filters = malloc(sizeof(struct ixlv_mac_filter), | sc->mac_filters = malloc(sizeof(struct ixlv_mac_filter), | ||||
M_DEVBUF, M_NOWAIT | M_ZERO); | M_DEVBUF, M_NOWAIT | M_ZERO); | ||||
SLIST_INIT(sc->mac_filters); | SLIST_INIT(sc->mac_filters); | ||||
sc->vlan_filters = malloc(sizeof(struct ixlv_vlan_filter), | sc->vlan_filters = malloc(sizeof(struct ixlv_vlan_filter), | ||||
M_DEVBUF, M_NOWAIT | M_ZERO); | M_DEVBUF, M_NOWAIT | M_ZERO); | ||||
SLIST_INIT(sc->vlan_filters); | SLIST_INIT(sc->vlan_filters); | ||||
return; | |||||
} | } | ||||
static void | static void | ||||
ixlv_free_filters(struct ixlv_sc *sc) | ixlv_free_filters(struct ixlv_sc *sc) | ||||
{ | { | ||||
struct ixlv_mac_filter *f; | struct ixlv_mac_filter *f; | ||||
struct ixlv_vlan_filter *v; | struct ixlv_vlan_filter *v; | ||||
while (!SLIST_EMPTY(sc->mac_filters)) { | while (!SLIST_EMPTY(sc->mac_filters)) { | ||||
f = SLIST_FIRST(sc->mac_filters); | f = SLIST_FIRST(sc->mac_filters); | ||||
SLIST_REMOVE_HEAD(sc->mac_filters, next); | SLIST_REMOVE_HEAD(sc->mac_filters, next); | ||||
free(f, M_DEVBUF); | free(f, M_DEVBUF); | ||||
} | } | ||||
while (!SLIST_EMPTY(sc->vlan_filters)) { | while (!SLIST_EMPTY(sc->vlan_filters)) { | ||||
v = SLIST_FIRST(sc->vlan_filters); | v = SLIST_FIRST(sc->vlan_filters); | ||||
SLIST_REMOVE_HEAD(sc->vlan_filters, next); | SLIST_REMOVE_HEAD(sc->vlan_filters, next); | ||||
free(v, M_DEVBUF); | free(v, M_DEVBUF); | ||||
} | } | ||||
return; | |||||
} | } | ||||
#ifdef IXL_DEBUG | #ifdef IXL_DEBUG | ||||
/** | /** | ||||
* ixlv_sysctl_qtx_tail_handler | * ixlv_sysctl_qtx_tail_handler | ||||
* Retrieves I40E_QTX_TAIL1 value from hardware | * Retrieves I40E_QTX_TAIL1 value from hardware | ||||
* for a sysctl. | * for a sysctl. | ||||
*/ | */ | ||||
Show All 40 Lines |