Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ixl/if_ixl.c
Show First 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
#ifdef RSS | #ifdef RSS | ||||
#include <net/rss_config.h> | #include <net/rss_config.h> | ||||
#endif | #endif | ||||
/********************************************************************* | /********************************************************************* | ||||
* Driver version | * Driver version | ||||
*********************************************************************/ | *********************************************************************/ | ||||
char ixl_driver_version[] = "1.4.3"; | char ixl_driver_version[] = "1.4.27-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 ixl_strings | * Last field stores an index into ixl_strings | ||||
* Last entry must be all 0s | * Last entry must be all 0s | ||||
* | * | ||||
* { Vendor ID, Device ID, SubVendor ID, SubDevice ID, String Index } | * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, String Index } | ||||
*********************************************************************/ | *********************************************************************/ | ||||
static ixl_vendor_info_t ixl_vendor_info_array[] = | static ixl_vendor_info_t ixl_vendor_info_array[] = | ||||
{ | { | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_SFP_XL710, 0, 0, 0}, | {I40E_INTEL_VENDOR_ID, I40E_DEV_ID_SFP_XL710, 0, 0, 0}, | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_KX_B, 0, 0, 0}, | {I40E_INTEL_VENDOR_ID, I40E_DEV_ID_KX_B, 0, 0, 0}, | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_KX_C, 0, 0, 0}, | {I40E_INTEL_VENDOR_ID, I40E_DEV_ID_KX_C, 0, 0, 0}, | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_QSFP_A, 0, 0, 0}, | {I40E_INTEL_VENDOR_ID, I40E_DEV_ID_QSFP_A, 0, 0, 0}, | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_QSFP_B, 0, 0, 0}, | {I40E_INTEL_VENDOR_ID, I40E_DEV_ID_QSFP_B, 0, 0, 0}, | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_QSFP_C, 0, 0, 0}, | {I40E_INTEL_VENDOR_ID, I40E_DEV_ID_QSFP_C, 0, 0, 0}, | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_10G_BASE_T, 0, 0, 0}, | {I40E_INTEL_VENDOR_ID, I40E_DEV_ID_10G_BASE_T, 0, 0, 0}, | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_10G_BASE_T4, 0, 0, 0}, | {I40E_INTEL_VENDOR_ID, I40E_DEV_ID_10G_BASE_T4, 0, 0, 0}, | ||||
#ifdef X722_SUPPORT | |||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_SFP_X722, 0, 0, 0}, | |||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_1G_BASE_T_X722, 0, 0, 0}, | |||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_10G_BASE_T_X722, 0, 0, 0}, | |||||
#endif | |||||
/* required last entry */ | /* required last entry */ | ||||
{0, 0, 0, 0, 0} | {0, 0, 0, 0, 0} | ||||
}; | }; | ||||
/********************************************************************* | /********************************************************************* | ||||
* Table of branding strings | * Table of branding strings | ||||
*********************************************************************/ | *********************************************************************/ | ||||
Show All 10 Lines | |||||
static int ixl_detach(device_t); | static int ixl_detach(device_t); | ||||
static int ixl_shutdown(device_t); | static int ixl_shutdown(device_t); | ||||
static int ixl_get_hw_capabilities(struct ixl_pf *); | static int ixl_get_hw_capabilities(struct ixl_pf *); | ||||
static void ixl_cap_txcsum_tso(struct ixl_vsi *, struct ifnet *, int); | static void ixl_cap_txcsum_tso(struct ixl_vsi *, struct ifnet *, int); | ||||
static int ixl_ioctl(struct ifnet *, u_long, caddr_t); | static int ixl_ioctl(struct ifnet *, u_long, caddr_t); | ||||
static void ixl_init(void *); | static void ixl_init(void *); | ||||
static void ixl_init_locked(struct ixl_pf *); | static void ixl_init_locked(struct ixl_pf *); | ||||
static void ixl_stop(struct ixl_pf *); | static void ixl_stop(struct ixl_pf *); | ||||
static void ixl_stop_locked(struct ixl_pf *); | |||||
static void ixl_media_status(struct ifnet *, struct ifmediareq *); | static void ixl_media_status(struct ifnet *, struct ifmediareq *); | ||||
static int ixl_media_change(struct ifnet *); | static int ixl_media_change(struct ifnet *); | ||||
static void ixl_update_link_status(struct ixl_pf *); | static void ixl_update_link_status(struct ixl_pf *); | ||||
static int ixl_allocate_pci_resources(struct ixl_pf *); | static int ixl_allocate_pci_resources(struct ixl_pf *); | ||||
static u16 ixl_get_bus_info(struct i40e_hw *, device_t); | static u16 ixl_get_bus_info(struct i40e_hw *, device_t); | ||||
static int ixl_setup_stations(struct ixl_pf *); | static int ixl_setup_stations(struct ixl_pf *); | ||||
static int ixl_switch_config(struct ixl_pf *); | static int ixl_switch_config(struct ixl_pf *); | ||||
static int ixl_initialize_vsi(struct ixl_vsi *); | static int ixl_initialize_vsi(struct ixl_vsi *); | ||||
static int ixl_assign_vsi_msix(struct ixl_pf *); | |||||
static int ixl_setup_adminq_msix(struct ixl_pf *); | |||||
static int ixl_setup_adminq_tq(struct ixl_pf *); | |||||
static int ixl_setup_queue_msix(struct ixl_vsi *); | |||||
static int ixl_setup_queue_tqs(struct ixl_vsi *); | |||||
static int ixl_teardown_adminq_msix(struct ixl_pf *); | |||||
static int ixl_teardown_queue_msix(struct ixl_vsi *); | |||||
static void ixl_configure_intr0_msix(struct ixl_pf *); | |||||
static void ixl_configure_queue_intr_msix(struct ixl_pf *); | |||||
static void ixl_free_queue_tqs(struct ixl_vsi *); | |||||
static void ixl_free_adminq_tq(struct ixl_pf *); | |||||
static int ixl_assign_vsi_legacy(struct ixl_pf *); | static int ixl_assign_vsi_legacy(struct ixl_pf *); | ||||
static int ixl_init_msix(struct ixl_pf *); | static int ixl_init_msix(struct ixl_pf *); | ||||
static void ixl_configure_msix(struct ixl_pf *); | |||||
static void ixl_configure_itr(struct ixl_pf *); | static void ixl_configure_itr(struct ixl_pf *); | ||||
static void ixl_configure_legacy(struct ixl_pf *); | static void ixl_configure_legacy(struct ixl_pf *); | ||||
static void ixl_init_taskqueues(struct ixl_pf *); | |||||
static void ixl_free_taskqueues(struct ixl_pf *); | |||||
static void ixl_free_pci_resources(struct ixl_pf *); | static void ixl_free_pci_resources(struct ixl_pf *); | ||||
static void ixl_local_timer(void *); | static void ixl_local_timer(void *); | ||||
static int ixl_setup_interface(device_t, struct ixl_vsi *); | static int ixl_setup_interface(device_t, struct ixl_vsi *); | ||||
static void ixl_link_event(struct ixl_pf *, struct i40e_arq_event_info *); | static void ixl_link_event(struct ixl_pf *, struct i40e_arq_event_info *); | ||||
static void ixl_config_rss(struct ixl_vsi *); | static void ixl_config_rss(struct ixl_vsi *); | ||||
static void ixl_set_queue_rx_itr(struct ixl_queue *); | static void ixl_set_queue_rx_itr(struct ixl_queue *); | ||||
static void ixl_set_queue_tx_itr(struct ixl_queue *); | static void ixl_set_queue_tx_itr(struct ixl_queue *); | ||||
static int ixl_set_advertised_speeds(struct ixl_pf *, int); | static int ixl_set_advertised_speeds(struct ixl_pf *, int); | ||||
static void ixl_get_initial_advertised_speeds(struct ixl_pf *); | |||||
static int ixl_enable_rings(struct ixl_vsi *); | static int ixl_enable_rings(struct ixl_vsi *); | ||||
static int ixl_disable_rings(struct ixl_vsi *); | static int ixl_disable_rings(struct ixl_vsi *); | ||||
static void ixl_enable_intr(struct ixl_vsi *); | static void ixl_enable_intr(struct ixl_vsi *); | ||||
static void ixl_disable_intr(struct ixl_vsi *); | static void ixl_disable_intr(struct ixl_vsi *); | ||||
static void ixl_disable_rings_intr(struct ixl_vsi *); | static void ixl_disable_rings_intr(struct ixl_vsi *); | ||||
static void ixl_enable_adminq(struct i40e_hw *); | static void ixl_enable_adminq(struct i40e_hw *); | ||||
Show All 16 Lines | |||||
static void ixl_del_filter(struct ixl_vsi *, u8 *, s16 vlan); | static void ixl_del_filter(struct ixl_vsi *, u8 *, s16 vlan); | ||||
static void ixl_add_hw_filters(struct ixl_vsi *, int, int); | static void ixl_add_hw_filters(struct ixl_vsi *, int, int); | ||||
static void ixl_del_hw_filters(struct ixl_vsi *, int); | static void ixl_del_hw_filters(struct ixl_vsi *, int); | ||||
static struct ixl_mac_filter * | static struct ixl_mac_filter * | ||||
ixl_find_filter(struct ixl_vsi *, u8 *, s16); | ixl_find_filter(struct ixl_vsi *, u8 *, s16); | ||||
static void ixl_add_mc_filter(struct ixl_vsi *, u8 *); | static void ixl_add_mc_filter(struct ixl_vsi *, u8 *); | ||||
static void ixl_free_mac_filters(struct ixl_vsi *vsi); | static void ixl_free_mac_filters(struct ixl_vsi *vsi); | ||||
/* Sysctls*/ | |||||
static void ixl_add_device_sysctls(struct ixl_pf *); | |||||
/* Sysctl debug interface */ | static int ixl_set_flowcntl(SYSCTL_HANDLER_ARGS); | ||||
static int ixl_set_advertise(SYSCTL_HANDLER_ARGS); | |||||
static int ixl_current_speed(SYSCTL_HANDLER_ARGS); | |||||
static int ixl_sysctl_show_fw(SYSCTL_HANDLER_ARGS); | |||||
#ifdef IXL_DEBUG_SYSCTL | #ifdef IXL_DEBUG_SYSCTL | ||||
static int ixl_debug_info(SYSCTL_HANDLER_ARGS); | static int ixl_debug_info(SYSCTL_HANDLER_ARGS); | ||||
static void ixl_print_debug_info(struct ixl_pf *); | static void ixl_print_debug_info(struct ixl_pf *); | ||||
static int ixl_sysctl_link_status(SYSCTL_HANDLER_ARGS); | |||||
static int ixl_sysctl_phy_abilities(SYSCTL_HANDLER_ARGS); | |||||
static int ixl_sysctl_sw_filter_list(SYSCTL_HANDLER_ARGS); | |||||
static int ixl_sysctl_hw_res_alloc(SYSCTL_HANDLER_ARGS); | |||||
static int ixl_sysctl_switch_config(SYSCTL_HANDLER_ARGS); | |||||
#endif | #endif | ||||
/* The MSI/X Interrupt handlers */ | /* The MSI/X Interrupt handlers */ | ||||
static void ixl_intr(void *); | static void ixl_intr(void *); | ||||
static void ixl_msix_que(void *); | static void ixl_msix_que(void *); | ||||
static void ixl_msix_adminq(void *); | static void ixl_msix_adminq(void *); | ||||
static void ixl_handle_mdd_event(struct ixl_pf *); | static void ixl_handle_mdd_event(struct ixl_pf *); | ||||
/* Deferred interrupt tasklets */ | /* Deferred interrupt tasklets */ | ||||
static void ixl_do_adminq(void *, int); | static void ixl_do_adminq(void *, int); | ||||
/* Sysctl handlers */ | |||||
static int ixl_set_flowcntl(SYSCTL_HANDLER_ARGS); | |||||
static int ixl_set_advertise(SYSCTL_HANDLER_ARGS); | |||||
static int ixl_current_speed(SYSCTL_HANDLER_ARGS); | |||||
static int ixl_sysctl_show_fw(SYSCTL_HANDLER_ARGS); | |||||
/* Statistics */ | /* Statistics */ | ||||
static void ixl_add_hw_stats(struct ixl_pf *); | static void ixl_add_hw_stats(struct ixl_pf *); | ||||
static void ixl_add_sysctls_mac_stats(struct sysctl_ctx_list *, | static void ixl_add_sysctls_mac_stats(struct sysctl_ctx_list *, | ||||
struct sysctl_oid_list *, struct i40e_hw_port_stats *); | struct sysctl_oid_list *, struct i40e_hw_port_stats *); | ||||
static void ixl_add_sysctls_eth_stats(struct sysctl_ctx_list *, | static void ixl_add_sysctls_eth_stats(struct sysctl_ctx_list *, | ||||
struct sysctl_oid_list *, | struct sysctl_oid_list *, | ||||
struct i40e_eth_stats *); | struct i40e_eth_stats *); | ||||
static void ixl_update_stats_counters(struct ixl_pf *); | static void ixl_update_stats_counters(struct ixl_pf *); | ||||
static void ixl_update_eth_stats(struct ixl_vsi *); | static void ixl_update_eth_stats(struct ixl_vsi *); | ||||
static void ixl_update_vsi_stats(struct ixl_vsi *); | static void ixl_update_vsi_stats(struct ixl_vsi *); | ||||
static void ixl_pf_reset_stats(struct ixl_pf *); | static void ixl_pf_reset_stats(struct ixl_pf *); | ||||
static void ixl_vsi_reset_stats(struct ixl_vsi *); | static void ixl_vsi_reset_stats(struct ixl_vsi *); | ||||
static void ixl_stat_update48(struct i40e_hw *, u32, u32, bool, | static void ixl_stat_update48(struct i40e_hw *, u32, u32, bool, | ||||
u64 *, u64 *); | u64 *, u64 *); | ||||
static void ixl_stat_update32(struct i40e_hw *, u32, bool, | static void ixl_stat_update32(struct i40e_hw *, u32, bool, | ||||
u64 *, u64 *); | u64 *, u64 *); | ||||
/* NVM update */ | |||||
static int ixl_handle_nvmupd_cmd(struct ixl_pf *, struct ifdrv *); | |||||
static void ixl_handle_empr_reset(struct ixl_pf *); | |||||
static int ixl_rebuild_hw_structs_after_reset(struct ixl_pf *); | |||||
#ifdef IXL_DEBUG_SYSCTL | /* Debug helper functions */ | ||||
static int ixl_sysctl_link_status(SYSCTL_HANDLER_ARGS); | #ifdef IXL_DEBUG | ||||
static int ixl_sysctl_phy_abilities(SYSCTL_HANDLER_ARGS); | static void ixl_print_nvm_cmd(device_t, struct i40e_nvm_access *); | ||||
static int ixl_sysctl_sw_filter_list(SYSCTL_HANDLER_ARGS); | |||||
static int ixl_sysctl_hw_res_alloc(SYSCTL_HANDLER_ARGS); | |||||
static int ixl_sysctl_switch_config(SYSCTL_HANDLER_ARGS); | |||||
#endif | #endif | ||||
#ifdef PCI_IOV | #ifdef PCI_IOV | ||||
static int ixl_adminq_err_to_errno(enum i40e_admin_queue_err err); | static int ixl_adminq_err_to_errno(enum i40e_admin_queue_err err); | ||||
static int ixl_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t*); | static int ixl_iov_init(device_t dev, uint16_t num_vfs, const nvlist_t*); | ||||
static void ixl_iov_uninit(device_t dev); | static void ixl_iov_uninit(device_t dev); | ||||
static int ixl_add_vf(device_t dev, uint16_t vfnum, const nvlist_t*); | static int ixl_add_vf(device_t dev, uint16_t vfnum, const nvlist_t*); | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | |||||
SYSCTL_INT(_hw_ixl, OID_AUTO, max_queues, CTLFLAG_RDTUN, | SYSCTL_INT(_hw_ixl, OID_AUTO, max_queues, CTLFLAG_RDTUN, | ||||
&ixl_max_queues, 0, "Number of Queues"); | &ixl_max_queues, 0, "Number of Queues"); | ||||
/* | /* | ||||
** 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 ixl_dynamic_rx_itr = 0; | int ixl_dynamic_rx_itr = 1; | ||||
TUNABLE_INT("hw.ixl.dynamic_rx_itr", &ixl_dynamic_rx_itr); | TUNABLE_INT("hw.ixl.dynamic_rx_itr", &ixl_dynamic_rx_itr); | ||||
SYSCTL_INT(_hw_ixl, OID_AUTO, dynamic_rx_itr, CTLFLAG_RDTUN, | SYSCTL_INT(_hw_ixl, OID_AUTO, dynamic_rx_itr, CTLFLAG_RDTUN, | ||||
&ixl_dynamic_rx_itr, 0, "Dynamic RX Interrupt Rate"); | &ixl_dynamic_rx_itr, 0, "Dynamic RX Interrupt Rate"); | ||||
int ixl_dynamic_tx_itr = 0; | int ixl_dynamic_tx_itr = 1; | ||||
TUNABLE_INT("hw.ixl.dynamic_tx_itr", &ixl_dynamic_tx_itr); | TUNABLE_INT("hw.ixl.dynamic_tx_itr", &ixl_dynamic_tx_itr); | ||||
SYSCTL_INT(_hw_ixl, OID_AUTO, dynamic_tx_itr, CTLFLAG_RDTUN, | SYSCTL_INT(_hw_ixl, OID_AUTO, dynamic_tx_itr, CTLFLAG_RDTUN, | ||||
&ixl_dynamic_tx_itr, 0, "Dynamic TX Interrupt Rate"); | &ixl_dynamic_tx_itr, 0, "Dynamic TX Interrupt Rate"); | ||||
int ixl_rx_itr = IXL_ITR_8K; | int ixl_rx_itr = IXL_ITR_8K; | ||||
TUNABLE_INT("hw.ixl.rx_itr", &ixl_rx_itr); | TUNABLE_INT("hw.ixl.rx_itr", &ixl_rx_itr); | ||||
SYSCTL_INT(_hw_ixl, OID_AUTO, rx_itr, CTLFLAG_RDTUN, | SYSCTL_INT(_hw_ixl, OID_AUTO, rx_itr, CTLFLAG_RDTUN, | ||||
&ixl_rx_itr, 0, "RX Interrupt Rate"); | &ixl_rx_itr, 0, "RX Interrupt Rate"); | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
ixl_vendor_info_t *ent; | ixl_vendor_info_t *ent; | ||||
u16 pci_vendor_id, pci_device_id; | u16 pci_vendor_id, pci_device_id; | ||||
u16 pci_subvendor_id, pci_subdevice_id; | u16 pci_subvendor_id, pci_subdevice_id; | ||||
char device_name[256]; | char device_name[256]; | ||||
static bool lock_init = FALSE; | static bool lock_init = FALSE; | ||||
#if 0 | |||||
INIT_DEBUGOUT("ixl_probe: begin"); | INIT_DEBUGOUT("ixl_probe: begin"); | ||||
#endif | |||||
pci_vendor_id = pci_get_vendor(dev); | pci_vendor_id = pci_get_vendor(dev); | ||||
if (pci_vendor_id != I40E_INTEL_VENDOR_ID) | if (pci_vendor_id != I40E_INTEL_VENDOR_ID) | ||||
return (ENXIO); | return (ENXIO); | ||||
pci_device_id = pci_get_device(dev); | pci_device_id = pci_get_device(dev); | ||||
pci_subvendor_id = pci_get_subvendor(dev); | pci_subvendor_id = pci_get_subvendor(dev); | ||||
pci_subdevice_id = pci_get_subdevice(dev); | pci_subdevice_id = pci_get_subdevice(dev); | ||||
Show All 35 Lines | |||||
* return 0 on success, positive on failure | * return 0 on success, positive on failure | ||||
*********************************************************************/ | *********************************************************************/ | ||||
static int | static int | ||||
ixl_attach(device_t dev) | ixl_attach(device_t dev) | ||||
{ | { | ||||
struct ixl_pf *pf; | struct ixl_pf *pf; | ||||
struct i40e_hw *hw; | struct i40e_hw *hw; | ||||
struct ixl_vsi *vsi; | struct ixl_vsi *vsi; | ||||
u16 bus; | u16 bus; | ||||
int error = 0; | int error = 0; | ||||
#ifdef PCI_IOV | #ifdef PCI_IOV | ||||
nvlist_t *pf_schema, *vf_schema; | nvlist_t *pf_schema, *vf_schema; | ||||
int iov_error; | int iov_error; | ||||
#endif | #endif | ||||
INIT_DEBUGOUT("ixl_attach: begin"); | INIT_DEBUGOUT("ixl_attach: begin"); | ||||
Show All 11 Lines | #endif | ||||
vsi->dev = pf->dev; | vsi->dev = pf->dev; | ||||
/* Core Lock Init*/ | /* Core Lock Init*/ | ||||
IXL_PF_LOCK_INIT(pf, device_get_nameunit(dev)); | IXL_PF_LOCK_INIT(pf, device_get_nameunit(dev)); | ||||
/* Set up the timer callout */ | /* Set up the timer callout */ | ||||
callout_init_mtx(&pf->timer, &pf->pf_mtx, 0); | callout_init_mtx(&pf->timer, &pf->pf_mtx, 0); | ||||
/* Set up sysctls */ | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "fc", CTLTYPE_INT | CTLFLAG_RW, | |||||
pf, 0, ixl_set_flowcntl, "I", "Flow Control"); | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "advertise_speed", CTLTYPE_INT | CTLFLAG_RW, | |||||
pf, 0, ixl_set_advertise, "I", "Advertised Speed"); | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "current_speed", CTLTYPE_STRING | CTLFLAG_RD, | |||||
pf, 0, ixl_current_speed, "A", "Current Port Speed"); | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "fw_version", CTLTYPE_STRING | CTLFLAG_RD, | |||||
pf, 0, ixl_sysctl_show_fw, "A", "Firmware version"); | |||||
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "rx_itr", CTLFLAG_RW, | |||||
&ixl_rx_itr, IXL_ITR_8K, "RX ITR"); | |||||
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "dynamic_rx_itr", CTLFLAG_RW, | |||||
&ixl_dynamic_rx_itr, 0, "Dynamic RX ITR"); | |||||
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "tx_itr", CTLFLAG_RW, | |||||
&ixl_tx_itr, IXL_ITR_4K, "TX ITR"); | |||||
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "dynamic_tx_itr", CTLFLAG_RW, | |||||
&ixl_dynamic_tx_itr, 0, "Dynamic TX ITR"); | |||||
#ifdef IXL_DEBUG_SYSCTL | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "debug", CTLTYPE_INT|CTLFLAG_RW, pf, 0, | |||||
ixl_debug_info, "I", "Debug Information"); | |||||
/* Debug shared-code message level */ | |||||
SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "debug_mask", CTLFLAG_RW, | |||||
&pf->hw.debug_mask, 0, "Debug Message Level"); | |||||
SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "vc_debug_level", CTLFLAG_RW, &pf->vc_debug_lvl, | |||||
0, "PF/VF Virtual Channel debug level"); | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "link_status", CTLTYPE_STRING | CTLFLAG_RD, | |||||
pf, 0, ixl_sysctl_link_status, "A", "Current Link Status"); | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "phy_abilities", CTLTYPE_STRING | CTLFLAG_RD, | |||||
pf, 0, ixl_sysctl_phy_abilities, "A", "PHY Abilities"); | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "filter_list", CTLTYPE_STRING | CTLFLAG_RD, | |||||
pf, 0, ixl_sysctl_sw_filter_list, "A", "SW Filter List"); | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "hw_res_alloc", CTLTYPE_STRING | CTLFLAG_RD, | |||||
pf, 0, ixl_sysctl_hw_res_alloc, "A", "HW Resource Allocation"); | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "switch_config", CTLTYPE_STRING | CTLFLAG_RD, | |||||
pf, 0, ixl_sysctl_switch_config, "A", "HW Switch Configuration"); | |||||
#endif | |||||
/* Save off the PCI information */ | /* Save off the PCI information */ | ||||
hw->vendor_id = pci_get_vendor(dev); | hw->vendor_id = pci_get_vendor(dev); | ||||
hw->device_id = pci_get_device(dev); | hw->device_id = pci_get_device(dev); | ||||
hw->revision_id = pci_read_config(dev, PCIR_REVID, 1); | hw->revision_id = pci_read_config(dev, PCIR_REVID, 1); | ||||
hw->subsystem_vendor_id = | hw->subsystem_vendor_id = | ||||
pci_read_config(dev, PCIR_SUBVEND_0, 2); | pci_read_config(dev, PCIR_SUBVEND_0, 2); | ||||
hw->subsystem_device_id = | hw->subsystem_device_id = | ||||
pci_read_config(dev, PCIR_SUBDEV_0, 2); | pci_read_config(dev, PCIR_SUBDEV_0, 2); | ||||
Show All 9 Lines | if (ixl_allocate_pci_resources(pf)) { | ||||
error = ENXIO; | error = ENXIO; | ||||
goto err_out; | goto err_out; | ||||
} | } | ||||
/* Establish a clean starting point */ | /* Establish a clean starting point */ | ||||
i40e_clear_hw(hw); | i40e_clear_hw(hw); | ||||
error = i40e_pf_reset(hw); | error = i40e_pf_reset(hw); | ||||
if (error) { | if (error) { | ||||
device_printf(dev,"PF reset failure %x\n", error); | device_printf(dev, "PF reset failure %d\n", error); | ||||
error = EIO; | error = EIO; | ||||
goto err_out; | goto err_out; | ||||
} | } | ||||
/* Set admin queue parameters */ | /* Set admin queue parameters */ | ||||
hw->aq.num_arq_entries = IXL_AQ_LEN; | hw->aq.num_arq_entries = IXL_AQ_LEN; | ||||
hw->aq.num_asq_entries = IXL_AQ_LEN; | hw->aq.num_asq_entries = IXL_AQ_LEN; | ||||
hw->aq.arq_buf_size = IXL_AQ_BUFSZ; | hw->aq.arq_buf_size = IXL_AQ_BUFSZ; | ||||
hw->aq.asq_buf_size = IXL_AQ_BUFSZ; | hw->aq.asq_buf_size = IXL_AQ_BUFSZ; | ||||
/* Initialize mac filter list for VSI */ | |||||
SLIST_INIT(&vsi->ftl); | |||||
/* Initialize the shared code */ | /* Initialize the shared code */ | ||||
error = i40e_init_shared_code(hw); | error = i40e_init_shared_code(hw); | ||||
if (error) { | if (error) { | ||||
device_printf(dev,"Unable to initialize the shared code\n"); | device_printf(dev, "Unable to initialize shared code, error %d\n", | ||||
error); | |||||
error = EIO; | error = EIO; | ||||
goto err_out; | goto err_out; | ||||
} | } | ||||
/* Set up the admin queue */ | /* Set up the admin queue */ | ||||
error = i40e_init_adminq(hw); | error = i40e_init_adminq(hw); | ||||
if (error) { | if (error != 0 && error != I40E_ERR_FIRMWARE_API_VERSION) { | ||||
device_printf(dev, "Unable to initialize Admin Queue, error %d\n", | |||||
error); | |||||
error = EIO; | |||||
goto err_out; | |||||
} | |||||
ixl_print_nvm_version(pf); | |||||
if (error == I40E_ERR_FIRMWARE_API_VERSION) { | |||||
device_printf(dev, "The driver for the device stopped " | device_printf(dev, "The driver for the device stopped " | ||||
"because the NVM image is newer than expected.\n" | "because the NVM image is newer than expected.\n" | ||||
"You must install the most recent version of " | "You must install the most recent version of " | ||||
" the network driver.\n"); | "the network driver.\n"); | ||||
error = EIO; | |||||
goto err_out; | goto err_out; | ||||
} | } | ||||
device_printf(dev, "%s\n", ixl_fw_version_str(hw)); | |||||
if (hw->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR && | if (hw->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR && | ||||
hw->aq.api_min_ver > I40E_FW_API_VERSION_MINOR) | hw->aq.api_min_ver > I40E_FW_API_VERSION_MINOR) | ||||
device_printf(dev, "The driver for the device detected " | device_printf(dev, "The driver for the device detected " | ||||
"a newer version of the NVM image than expected.\n" | "a newer version of the NVM image than expected.\n" | ||||
"Please install the most recent version of the network driver.\n"); | "Please install the most recent version of the network driver.\n"); | ||||
else if (hw->aq.api_maj_ver < I40E_FW_API_VERSION_MAJOR || | else if (hw->aq.api_maj_ver < I40E_FW_API_VERSION_MAJOR || | ||||
hw->aq.api_min_ver < (I40E_FW_API_VERSION_MINOR - 1)) | hw->aq.api_min_ver < (I40E_FW_API_VERSION_MINOR - 1)) | ||||
Show All 20 Lines | #endif | ||||
} | } | ||||
error = i40e_configure_lan_hmc(hw, I40E_HMC_MODEL_DIRECT_ONLY); | error = i40e_configure_lan_hmc(hw, I40E_HMC_MODEL_DIRECT_ONLY); | ||||
if (error) { | if (error) { | ||||
device_printf(dev, "configure_lan_hmc failed: %d\n", error); | device_printf(dev, "configure_lan_hmc failed: %d\n", error); | ||||
goto err_mac_hmc; | goto err_mac_hmc; | ||||
} | } | ||||
/* Disable LLDP from the firmware */ | /* Disable LLDP from the firmware for certain NVM versions */ | ||||
if (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver < 3)) || | |||||
(pf->hw.aq.fw_maj_ver < 4)) | |||||
i40e_aq_stop_lldp(hw, TRUE, NULL); | i40e_aq_stop_lldp(hw, TRUE, NULL); | ||||
i40e_get_mac_addr(hw, hw->mac.addr); | i40e_get_mac_addr(hw, hw->mac.addr); | ||||
error = i40e_validate_mac_addr(hw->mac.addr); | error = i40e_validate_mac_addr(hw->mac.addr); | ||||
if (error) { | if (error) { | ||||
device_printf(dev, "validate_mac_addr failed: %d\n", error); | device_printf(dev, "validate_mac_addr failed: %d\n", error); | ||||
goto err_mac_hmc; | goto err_mac_hmc; | ||||
} | } | ||||
bcopy(hw->mac.addr, hw->mac.perm_addr, ETHER_ADDR_LEN); | bcopy(hw->mac.addr, hw->mac.perm_addr, ETHER_ADDR_LEN); | ||||
i40e_get_port_mac_addr(hw, hw->mac.port_addr); | i40e_get_port_mac_addr(hw, hw->mac.port_addr); | ||||
/* Set up VSI and queues */ | /* Set up VSI and queues */ | ||||
if (ixl_setup_stations(pf) != 0) { | if (ixl_setup_stations(pf) != 0) { | ||||
device_printf(dev, "setup stations failed!\n"); | device_printf(dev, "setup stations failed!\n"); | ||||
error = ENOMEM; | error = ENOMEM; | ||||
goto err_mac_hmc; | goto err_mac_hmc; | ||||
} | } | ||||
/* Initialize mac filter list for VSI */ | |||||
SLIST_INIT(&vsi->ftl); | |||||
/* Set up interrupt routing here */ | |||||
if (pf->msix > 1) | |||||
error = ixl_assign_vsi_msix(pf); | |||||
else | |||||
error = ixl_assign_vsi_legacy(pf); | |||||
if (error) | |||||
goto err_mac_hmc; | |||||
if (((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver < 33)) || | if (((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver < 33)) || | ||||
(hw->aq.fw_maj_ver < 4)) { | (hw->aq.fw_maj_ver < 4)) { | ||||
i40e_msec_delay(75); | i40e_msec_delay(75); | ||||
error = i40e_aq_set_link_restart_an(hw, TRUE, NULL); | error = i40e_aq_set_link_restart_an(hw, TRUE, NULL); | ||||
if (error) | if (error) { | ||||
device_printf(dev, "link restart failed, aq_err=%d\n", | device_printf(dev, "link restart failed, aq_err=%d\n", | ||||
pf->hw.aq.asq_last_status); | pf->hw.aq.asq_last_status); | ||||
goto err_late; | |||||
} | } | ||||
} | |||||
/* Determine link state */ | /* Determine link state */ | ||||
i40e_aq_get_link_info(hw, TRUE, NULL, NULL); | hw->phy.get_link_info = TRUE; | ||||
i40e_get_link_status(hw, &pf->link_up); | i40e_get_link_status(hw, &pf->link_up); | ||||
/* Setup OS specific network interface */ | /* Setup OS network interface / ifnet */ | ||||
if (ixl_setup_interface(dev, vsi) != 0) { | if (ixl_setup_interface(dev, vsi) != 0) { | ||||
device_printf(dev, "interface setup failed!\n"); | device_printf(dev, "interface setup failed!\n"); | ||||
error = EIO; | error = EIO; | ||||
goto err_late; | goto err_late; | ||||
} | } | ||||
error = ixl_switch_config(pf); | error = ixl_switch_config(pf); | ||||
if (error) { | if (error) { | ||||
device_printf(dev, "Initial switch config failed: %d\n", error); | device_printf(dev, "Initial ixl_switch_config() failed: %d\n", | ||||
error); | |||||
goto err_late; | goto err_late; | ||||
} | } | ||||
/* Limit phy interrupts to link and modules failure */ | /* Limit PHY interrupts to link, autoneg, and modules failure */ | ||||
error = i40e_aq_set_phy_int_mask(hw, ~(I40E_AQ_EVENT_LINK_UPDOWN | | error = i40e_aq_set_phy_int_mask(hw, IXL_DEFAULT_PHY_INT_MASK, | ||||
I40E_AQ_EVENT_MODULE_QUAL_FAIL), NULL); | NULL); | ||||
if (error) | if (error) { | ||||
device_printf(dev, "set phy mask failed: %d\n", error); | device_printf(dev, "i40e_aq_set_phy_mask() failed: err %d," | ||||
" aq_err %d\n", error, hw->aq.asq_last_status); | |||||
goto err_late; | |||||
} | |||||
/* Get the bus configuration and set the shared code */ | /* Get the bus configuration and set the shared code's config */ | ||||
bus = ixl_get_bus_info(hw, dev); | bus = ixl_get_bus_info(hw, dev); | ||||
i40e_set_pci_config_data(hw, bus); | i40e_set_pci_config_data(hw, bus); | ||||
/* Initialize taskqueues */ | /* | ||||
ixl_init_taskqueues(pf); | * In MSI-X mode, initialize the Admin Queue interrupt, | ||||
* so userland tools can communicate with the adapter regardless of | |||||
* the ifnet interface's status. | |||||
*/ | |||||
if (pf->msix > 1) { | |||||
error = ixl_setup_adminq_msix(pf); | |||||
if (error) { | |||||
device_printf(dev, "ixl_setup_adminq_msix error: %d\n", | |||||
error); | |||||
goto err_late; | |||||
} | |||||
error = ixl_setup_adminq_tq(pf); | |||||
if (error) { | |||||
device_printf(dev, "ixl_setup_adminq_tq error: %d\n", | |||||
error); | |||||
goto err_late; | |||||
} | |||||
ixl_configure_intr0_msix(pf); | |||||
ixl_enable_adminq(hw); | |||||
} | |||||
/* Initialize statistics */ | /* Initialize statistics & add sysctls */ | ||||
ixl_add_device_sysctls(pf); | |||||
ixl_pf_reset_stats(pf); | ixl_pf_reset_stats(pf); | ||||
ixl_update_stats_counters(pf); | ixl_update_stats_counters(pf); | ||||
ixl_add_hw_stats(pf); | ixl_add_hw_stats(pf); | ||||
/* Register for VLAN events */ | /* Register for VLAN events */ | ||||
vsi->vlan_attach = EVENTHANDLER_REGISTER(vlan_config, | vsi->vlan_attach = EVENTHANDLER_REGISTER(vlan_config, | ||||
ixl_register_vlan, vsi, EVENTHANDLER_PRI_FIRST); | ixl_register_vlan, vsi, EVENTHANDLER_PRI_FIRST); | ||||
vsi->vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig, | vsi->vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig, | ||||
ixl_unregister_vlan, vsi, EVENTHANDLER_PRI_FIRST); | ixl_unregister_vlan, vsi, EVENTHANDLER_PRI_FIRST); | ||||
#ifdef PCI_IOV | #ifdef PCI_IOV | ||||
/* SR-IOV is only supported when MSI-X is in use. */ | /* SR-IOV is only supported when MSI-X is in use. */ | ||||
if (pf->msix > 1) { | if (pf->msix > 1) { | ||||
pf_schema = pci_iov_schema_alloc_node(); | pf_schema = pci_iov_schema_alloc_node(); | ||||
vf_schema = pci_iov_schema_alloc_node(); | vf_schema = pci_iov_schema_alloc_node(); | ||||
pci_iov_schema_add_unicast_mac(vf_schema, "mac-addr", 0, NULL); | pci_iov_schema_add_unicast_mac(vf_schema, "mac-addr", 0, NULL); | ||||
pci_iov_schema_add_bool(vf_schema, "mac-anti-spoof", | pci_iov_schema_add_bool(vf_schema, "mac-anti-spoof", | ||||
IOV_SCHEMA_HASDEFAULT, TRUE); | IOV_SCHEMA_HASDEFAULT, TRUE); | ||||
pci_iov_schema_add_bool(vf_schema, "allow-set-mac", | pci_iov_schema_add_bool(vf_schema, "allow-set-mac", | ||||
IOV_SCHEMA_HASDEFAULT, FALSE); | IOV_SCHEMA_HASDEFAULT, FALSE); | ||||
pci_iov_schema_add_bool(vf_schema, "allow-promisc", | pci_iov_schema_add_bool(vf_schema, "allow-promisc", | ||||
IOV_SCHEMA_HASDEFAULT, FALSE); | IOV_SCHEMA_HASDEFAULT, FALSE); | ||||
iov_error = pci_iov_attach(dev, pf_schema, vf_schema); | iov_error = pci_iov_attach(dev, pf_schema, vf_schema); | ||||
if (iov_error != 0) | if (iov_error != 0) { | ||||
device_printf(dev, | device_printf(dev, | ||||
"Failed to initialize SR-IOV (error=%d)\n", | "Failed to initialize SR-IOV (error=%d)\n", | ||||
iov_error); | iov_error); | ||||
} else | |||||
device_printf(dev, "SR-IOV ready\n"); | |||||
} | } | ||||
#endif | #endif | ||||
#ifdef DEV_NETMAP | #ifdef DEV_NETMAP | ||||
ixl_netmap_attach(vsi); | ixl_netmap_attach(vsi); | ||||
#endif /* DEV_NETMAP */ | #endif /* DEV_NETMAP */ | ||||
INIT_DEBUGOUT("ixl_attach: end"); | INIT_DEBUGOUT("ixl_attach: end"); | ||||
return (0); | return (0); | ||||
Show All 23 Lines | |||||
*********************************************************************/ | *********************************************************************/ | ||||
static int | static int | ||||
ixl_detach(device_t dev) | ixl_detach(device_t dev) | ||||
{ | { | ||||
struct ixl_pf *pf = device_get_softc(dev); | struct ixl_pf *pf = device_get_softc(dev); | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
struct ixl_vsi *vsi = &pf->vsi; | struct ixl_vsi *vsi = &pf->vsi; | ||||
i40e_status status; | enum i40e_status_code status; | ||||
#ifdef PCI_IOV | #ifdef PCI_IOV | ||||
int error; | int error; | ||||
#endif | #endif | ||||
INIT_DEBUGOUT("ixl_detach: begin"); | INIT_DEBUGOUT("ixl_detach: begin"); | ||||
/* Make sure VLANS are not using driver */ | /* Make sure VLANS are not using driver */ | ||||
if (vsi->ifp->if_vlantrunk != NULL) { | if (vsi->ifp->if_vlantrunk != NULL) { | ||||
device_printf(dev,"Vlan in use, detach first\n"); | device_printf(dev, "Vlan in use, detach first\n"); | ||||
return (EBUSY); | return (EBUSY); | ||||
} | } | ||||
#ifdef PCI_IOV | #ifdef PCI_IOV | ||||
error = pci_iov_detach(dev); | error = pci_iov_detach(dev); | ||||
if (error != 0) { | if (error != 0) { | ||||
device_printf(dev, "SR-IOV in use; detach first.\n"); | device_printf(dev, "SR-IOV in use; detach first.\n"); | ||||
return (error); | return (error); | ||||
} | } | ||||
#endif | #endif | ||||
ether_ifdetach(vsi->ifp); | ether_ifdetach(vsi->ifp); | ||||
if (vsi->ifp->if_drv_flags & IFF_DRV_RUNNING) { | if (vsi->ifp->if_drv_flags & IFF_DRV_RUNNING) | ||||
IXL_PF_LOCK(pf); | |||||
ixl_stop(pf); | ixl_stop(pf); | ||||
IXL_PF_UNLOCK(pf); | |||||
} | |||||
ixl_free_taskqueues(pf); | ixl_free_queue_tqs(vsi); | ||||
/* Shutdown LAN HMC */ | /* Shutdown LAN HMC */ | ||||
status = i40e_shutdown_lan_hmc(hw); | status = i40e_shutdown_lan_hmc(hw); | ||||
if (status) | if (status) | ||||
device_printf(dev, | device_printf(dev, | ||||
"Shutdown LAN HMC failed with code %d\n", status); | "Shutdown LAN HMC failed with code %d\n", status); | ||||
/* Shutdown admin queue */ | /* Shutdown admin queue */ | ||||
ixl_disable_adminq(hw); | |||||
ixl_free_adminq_tq(pf); | |||||
ixl_teardown_adminq_msix(pf); | |||||
status = i40e_shutdown_adminq(hw); | status = i40e_shutdown_adminq(hw); | ||||
if (status) | if (status) | ||||
device_printf(dev, | device_printf(dev, | ||||
"Shutdown Admin queue failed with code %d\n", status); | "Shutdown Admin queue failed with code %d\n", status); | ||||
/* Unregister VLAN events */ | /* Unregister VLAN events */ | ||||
if (vsi->vlan_attach != NULL) | if (vsi->vlan_attach != NULL) | ||||
EVENTHANDLER_DEREGISTER(vlan_config, vsi->vlan_attach); | EVENTHANDLER_DEREGISTER(vlan_config, vsi->vlan_attach); | ||||
Show All 17 Lines | |||||
* Shutdown entry point | * Shutdown entry point | ||||
* | * | ||||
**********************************************************************/ | **********************************************************************/ | ||||
static int | static int | ||||
ixl_shutdown(device_t dev) | ixl_shutdown(device_t dev) | ||||
{ | { | ||||
struct ixl_pf *pf = device_get_softc(dev); | struct ixl_pf *pf = device_get_softc(dev); | ||||
IXL_PF_LOCK(pf); | |||||
ixl_stop(pf); | ixl_stop(pf); | ||||
IXL_PF_UNLOCK(pf); | |||||
return (0); | return (0); | ||||
} | } | ||||
/********************************************************************* | /********************************************************************* | ||||
* | * | ||||
* Get the hardware capabilities | * Get the hardware capabilities | ||||
* | * | ||||
Show All 32 Lines | device_printf(dev, "capability discovery failed: %d\n", | ||||
pf->hw.aq.asq_last_status); | pf->hw.aq.asq_last_status); | ||||
return (ENODEV); | return (ENODEV); | ||||
} | } | ||||
/* Capture this PF's starting queue pair */ | /* Capture this PF's starting queue pair */ | ||||
pf->qbase = hw->func_caps.base_queue; | pf->qbase = hw->func_caps.base_queue; | ||||
#ifdef IXL_DEBUG | #ifdef IXL_DEBUG | ||||
device_printf(dev,"pf_id=%d, num_vfs=%d, msix_pf=%d, " | device_printf(dev, "pf_id=%d, num_vfs=%d, msix_pf=%d, " | ||||
"msix_vf=%d, fd_g=%d, fd_b=%d, tx_qp=%d rx_qp=%d qbase=%d\n", | "msix_vf=%d, fd_g=%d, fd_b=%d, tx_qp=%d rx_qp=%d qbase=%d\n", | ||||
hw->pf_id, hw->func_caps.num_vfs, | hw->pf_id, hw->func_caps.num_vfs, | ||||
hw->func_caps.num_msix_vectors, | hw->func_caps.num_msix_vectors, | ||||
hw->func_caps.num_msix_vectors_vf, | hw->func_caps.num_msix_vectors_vf, | ||||
hw->func_caps.fd_filters_guaranteed, | hw->func_caps.fd_filters_guaranteed, | ||||
hw->func_caps.fd_filters_best_effort, | hw->func_caps.fd_filters_best_effort, | ||||
hw->func_caps.num_tx_qp, | hw->func_caps.num_tx_qp, | ||||
hw->func_caps.num_rx_qp, | hw->func_caps.num_rx_qp, | ||||
▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Lines | |||||
* return 0 on success, positive on failure | * return 0 on success, positive on failure | ||||
**********************************************************************/ | **********************************************************************/ | ||||
static int | static int | ||||
ixl_ioctl(struct ifnet * ifp, u_long command, caddr_t data) | ixl_ioctl(struct ifnet * ifp, u_long command, caddr_t data) | ||||
{ | { | ||||
struct ixl_vsi *vsi = ifp->if_softc; | struct ixl_vsi *vsi = ifp->if_softc; | ||||
struct ixl_pf *pf = vsi->back; | struct ixl_pf *pf = vsi->back; | ||||
struct ifreq *ifr = (struct ifreq *) data; | struct ifreq *ifr = (struct ifreq *)data; | ||||
struct ifdrv *ifd = (struct ifdrv *)data; | |||||
#if defined(INET) || defined(INET6) | #if defined(INET) || defined(INET6) | ||||
struct ifaddr *ifa = (struct ifaddr *)data; | struct ifaddr *ifa = (struct ifaddr *)data; | ||||
bool avoid_reset = FALSE; | bool avoid_reset = FALSE; | ||||
#endif | #endif | ||||
int error = 0; | int error = 0; | ||||
switch (command) { | switch (command) { | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | case SIOCSIFFLAGS: | ||||
IOCTL_DEBUGOUT("ioctl: SIOCSIFFLAGS (Set Interface Flags)"); | IOCTL_DEBUGOUT("ioctl: SIOCSIFFLAGS (Set Interface Flags)"); | ||||
IXL_PF_LOCK(pf); | IXL_PF_LOCK(pf); | ||||
if (ifp->if_flags & IFF_UP) { | if (ifp->if_flags & IFF_UP) { | ||||
if ((ifp->if_drv_flags & IFF_DRV_RUNNING)) { | if ((ifp->if_drv_flags & IFF_DRV_RUNNING)) { | ||||
if ((ifp->if_flags ^ pf->if_flags) & | if ((ifp->if_flags ^ pf->if_flags) & | ||||
(IFF_PROMISC | IFF_ALLMULTI)) { | (IFF_PROMISC | IFF_ALLMULTI)) { | ||||
ixl_set_promisc(vsi); | ixl_set_promisc(vsi); | ||||
} | } | ||||
} else | } else { | ||||
ixl_init_locked(pf); | IXL_PF_UNLOCK(pf); | ||||
} else | ixl_init(pf); | ||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) | IXL_PF_LOCK(pf); | ||||
} | |||||
} else { | |||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | |||||
IXL_PF_UNLOCK(pf); | |||||
ixl_stop(pf); | ixl_stop(pf); | ||||
IXL_PF_LOCK(pf); | |||||
} | |||||
} | |||||
pf->if_flags = ifp->if_flags; | pf->if_flags = ifp->if_flags; | ||||
IXL_PF_UNLOCK(pf); | IXL_PF_UNLOCK(pf); | ||||
break; | break; | ||||
case SIOCSDRVSPEC: | |||||
case SIOCGDRVSPEC: | |||||
IOCTL_DEBUGOUT("ioctl: SIOCxDRVSPEC (Get/Set Driver-specific " | |||||
"Info)\n"); | |||||
/* NVM update command */ | |||||
if (ifd->ifd_cmd == I40E_NVM_ACCESS) | |||||
error = ixl_handle_nvmupd_cmd(pf, ifd); | |||||
else | |||||
error = EINVAL; | |||||
break; | |||||
case SIOCADDMULTI: | case SIOCADDMULTI: | ||||
IOCTL_DEBUGOUT("ioctl: SIOCADDMULTI"); | IOCTL_DEBUGOUT("ioctl: SIOCADDMULTI"); | ||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | ||||
IXL_PF_LOCK(pf); | IXL_PF_LOCK(pf); | ||||
ixl_disable_intr(vsi); | ixl_disable_intr(vsi); | ||||
ixl_add_multi(vsi); | ixl_add_multi(vsi); | ||||
ixl_enable_intr(vsi); | ixl_enable_intr(vsi); | ||||
IXL_PF_UNLOCK(pf); | IXL_PF_UNLOCK(pf); | ||||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | ixl_init_locked(struct ixl_pf *pf) | ||||
struct ixl_vsi *vsi = &pf->vsi; | struct ixl_vsi *vsi = &pf->vsi; | ||||
struct ifnet *ifp = vsi->ifp; | struct ifnet *ifp = vsi->ifp; | ||||
device_t dev = pf->dev; | device_t dev = pf->dev; | ||||
struct i40e_filter_control_settings filter; | struct i40e_filter_control_settings filter; | ||||
u8 tmpaddr[ETHER_ADDR_LEN]; | u8 tmpaddr[ETHER_ADDR_LEN]; | ||||
int ret; | int ret; | ||||
mtx_assert(&pf->pf_mtx, MA_OWNED); | mtx_assert(&pf->pf_mtx, MA_OWNED); | ||||
INIT_DEBUGOUT("ixl_init: begin"); | INIT_DEBUGOUT("ixl_init_locked: begin"); | ||||
ixl_stop(pf); | |||||
ixl_stop_locked(pf); | |||||
/* Get the latest mac address... User might use a LAA */ | /* Get the latest mac address... User might use a LAA */ | ||||
bcopy(IF_LLADDR(vsi->ifp), tmpaddr, | bcopy(IF_LLADDR(vsi->ifp), tmpaddr, | ||||
I40E_ETH_LENGTH_OF_ADDRESS); | I40E_ETH_LENGTH_OF_ADDRESS); | ||||
if (!cmp_etheraddr(hw->mac.addr, tmpaddr) && | if (!cmp_etheraddr(hw->mac.addr, tmpaddr) && | ||||
(i40e_validate_mac_addr(tmpaddr) == I40E_SUCCESS)) { | (i40e_validate_mac_addr(tmpaddr) == I40E_SUCCESS)) { | ||||
ixl_del_filter(vsi, hw->mac.addr, IXL_VLAN_ANY); | ixl_del_filter(vsi, hw->mac.addr, IXL_VLAN_ANY); | ||||
bcopy(tmpaddr, hw->mac.addr, | bcopy(tmpaddr, hw->mac.addr, | ||||
I40E_ETH_LENGTH_OF_ADDRESS); | I40E_ETH_LENGTH_OF_ADDRESS); | ||||
ret = i40e_aq_mac_address_write(hw, | ret = i40e_aq_mac_address_write(hw, | ||||
I40E_AQC_WRITE_TYPE_LAA_ONLY, | I40E_AQC_WRITE_TYPE_LAA_ONLY, | ||||
hw->mac.addr, NULL); | hw->mac.addr, NULL); | ||||
if (ret) { | if (ret) { | ||||
device_printf(dev, "LLA address" | device_printf(dev, "LLA address" | ||||
"change failed!!\n"); | "change failed!!\n"); | ||||
return; | return; | ||||
} else { | |||||
ixl_add_filter(vsi, hw->mac.addr, IXL_VLAN_ANY); | |||||
} | } | ||||
} | } | ||||
ixl_add_filter(vsi, hw->mac.addr, IXL_VLAN_ANY); | |||||
/* Set the various hardware offload abilities */ | /* Set the various hardware offload abilities */ | ||||
ifp->if_hwassist = 0; | ifp->if_hwassist = 0; | ||||
if (ifp->if_capenable & IFCAP_TSO) | if (ifp->if_capenable & IFCAP_TSO) | ||||
ifp->if_hwassist |= CSUM_TSO; | ifp->if_hwassist |= CSUM_TSO; | ||||
if (ifp->if_capenable & IFCAP_TXCSUM) | if (ifp->if_capenable & IFCAP_TXCSUM) | ||||
ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP); | ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP); | ||||
if (ifp->if_capenable & IFCAP_TXCSUM_IPV6) | if (ifp->if_capenable & IFCAP_TXCSUM_IPV6) | ||||
ifp->if_hwassist |= (CSUM_TCP_IPV6 | CSUM_UDP_IPV6); | ifp->if_hwassist |= (CSUM_TCP_IPV6 | CSUM_UDP_IPV6); | ||||
/* Set up the device filtering */ | /* Set up the device filtering */ | ||||
bzero(&filter, sizeof(filter)); | bzero(&filter, sizeof(filter)); | ||||
filter.enable_ethtype = TRUE; | filter.enable_ethtype = TRUE; | ||||
filter.enable_macvlan = TRUE; | filter.enable_macvlan = TRUE; | ||||
#ifdef IXL_FDIR | #ifdef IXL_FDIR | ||||
filter.enable_fdir = TRUE; | filter.enable_fdir = TRUE; | ||||
#endif | #endif | ||||
filter.hash_lut_size = I40E_HASH_LUT_SIZE_512; | filter.hash_lut_size = I40E_HASH_LUT_SIZE_512; | ||||
if (i40e_set_filter_control(hw, &filter)) | if (i40e_set_filter_control(hw, &filter)) | ||||
device_printf(dev, "set_filter_control() failed\n"); | device_printf(dev, "i40e_set_filter_control() failed\n"); | ||||
/* Set up RSS */ | /* Set up RSS */ | ||||
ixl_config_rss(vsi); | ixl_config_rss(vsi); | ||||
/* | /* Prepare the VSI: rings, hmc contexts, etc... */ | ||||
** Prepare the VSI: rings, hmc contexts, etc... | |||||
*/ | |||||
if (ixl_initialize_vsi(vsi)) { | if (ixl_initialize_vsi(vsi)) { | ||||
device_printf(dev, "initialize vsi failed!!\n"); | device_printf(dev, "initialize vsi failed!!\n"); | ||||
return; | return; | ||||
} | } | ||||
/* Add protocol filters to list */ | /* Add protocol filters to list */ | ||||
ixl_init_filters(vsi); | ixl_init_filters(vsi); | ||||
/* Setup vlan's if needed */ | /* Setup vlan's if needed */ | ||||
ixl_setup_vlan_filters(vsi); | ixl_setup_vlan_filters(vsi); | ||||
/* Start the local timer */ | |||||
callout_reset(&pf->timer, hz, ixl_local_timer, pf); | |||||
/* Set up MSI/X routing and the ITR settings */ | /* Set up MSI/X routing and the ITR settings */ | ||||
if (ixl_enable_msix) { | if (ixl_enable_msix) { | ||||
ixl_configure_msix(pf); | ixl_configure_queue_intr_msix(pf); | ||||
ixl_configure_itr(pf); | ixl_configure_itr(pf); | ||||
} else | } else | ||||
ixl_configure_legacy(pf); | ixl_configure_legacy(pf); | ||||
ixl_enable_rings(vsi); | ixl_enable_rings(vsi); | ||||
i40e_aq_set_default_vsi(hw, vsi->seid, NULL); | i40e_aq_set_default_vsi(hw, vsi->seid, NULL); | ||||
ixl_reconfigure_filters(vsi); | ixl_reconfigure_filters(vsi); | ||||
/* Set MTU in hardware*/ | |||||
int aq_error = i40e_aq_set_mac_config(hw, vsi->max_frame_size, | |||||
TRUE, 0, NULL); | |||||
if (aq_error) | |||||
device_printf(vsi->dev, | |||||
"aq_set_mac_config in init error, code %d\n", | |||||
aq_error); | |||||
/* And now turn on interrupts */ | /* And now turn on interrupts */ | ||||
ixl_enable_intr(vsi); | ixl_enable_intr(vsi); | ||||
/* Get link info */ | |||||
hw->phy.get_link_info = TRUE; | |||||
i40e_get_link_status(hw, &pf->link_up); | |||||
ixl_update_link_status(pf); | |||||
/* Set initial advertised speed sysctl value */ | |||||
ixl_get_initial_advertised_speeds(pf); | |||||
/* Start the local timer */ | |||||
callout_reset(&pf->timer, hz, ixl_local_timer, pf); | |||||
/* Now inform the stack we're ready */ | /* Now inform the stack we're ready */ | ||||
ifp->if_drv_flags |= IFF_DRV_RUNNING; | ifp->if_drv_flags |= IFF_DRV_RUNNING; | ||||
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; | |||||
return; | return; | ||||
} | } | ||||
/* For the set_advertise sysctl */ | |||||
static void | static void | ||||
ixl_get_initial_advertised_speeds(struct ixl_pf *pf) | |||||
{ | |||||
struct i40e_hw *hw = &pf->hw; | |||||
device_t dev = pf->dev; | |||||
enum i40e_status_code status; | |||||
struct i40e_aq_get_phy_abilities_resp abilities; | |||||
/* Set initial sysctl values */ | |||||
status = i40e_aq_get_phy_capabilities(hw, FALSE, false, &abilities, | |||||
NULL); | |||||
if (status) { | |||||
/* Non-fatal error */ | |||||
device_printf(dev, "%s: i40e_aq_get_phy_capabilities() error %d\n", | |||||
__func__, status); | |||||
return; | |||||
} | |||||
if (abilities.link_speed & I40E_LINK_SPEED_40GB) | |||||
pf->advertised_speed |= 0x10; | |||||
if (abilities.link_speed & I40E_LINK_SPEED_20GB) | |||||
pf->advertised_speed |= 0x8; | |||||
if (abilities.link_speed & I40E_LINK_SPEED_10GB) | |||||
pf->advertised_speed |= 0x4; | |||||
if (abilities.link_speed & I40E_LINK_SPEED_1GB) | |||||
pf->advertised_speed |= 0x2; | |||||
if (abilities.link_speed & I40E_LINK_SPEED_100MB) | |||||
pf->advertised_speed |= 0x1; | |||||
} | |||||
static int | |||||
ixl_teardown_hw_structs(struct ixl_pf *pf) | |||||
{ | |||||
enum i40e_status_code status = 0; | |||||
struct i40e_hw *hw = &pf->hw; | |||||
device_t dev = pf->dev; | |||||
/* Shutdown LAN HMC */ | |||||
if (hw->hmc.hmc_obj) { | |||||
status = i40e_shutdown_lan_hmc(hw); | |||||
if (status) { | |||||
device_printf(dev, | |||||
"init: LAN HMC shutdown failure; status %d\n", status); | |||||
goto err_out; | |||||
} | |||||
} | |||||
// XXX: This gets called when we know the adminq is inactive; | |||||
// so we already know it's setup when we get here. | |||||
/* Shutdown admin queue */ | |||||
status = i40e_shutdown_adminq(hw); | |||||
if (status) | |||||
device_printf(dev, | |||||
"init: Admin Queue shutdown failure; status %d\n", status); | |||||
err_out: | |||||
return (status); | |||||
} | |||||
static int | |||||
ixl_reset(struct ixl_pf *pf) | |||||
{ | |||||
struct i40e_hw *hw = &pf->hw; | |||||
device_t dev = pf->dev; | |||||
u8 set_fc_err_mask; | |||||
int error = 0; | |||||
// XXX: clear_hw() actually writes to hw registers -- maybe this isn't necessary | |||||
i40e_clear_hw(hw); | |||||
error = i40e_pf_reset(hw); | |||||
if (error) { | |||||
device_printf(dev, "init: PF reset failure"); | |||||
error = EIO; | |||||
goto err_out; | |||||
} | |||||
error = i40e_init_adminq(hw); | |||||
if (error) { | |||||
device_printf(dev, "init: Admin queue init failure;" | |||||
" status code %d", error); | |||||
error = EIO; | |||||
goto err_out; | |||||
} | |||||
i40e_clear_pxe_mode(hw); | |||||
error = ixl_get_hw_capabilities(pf); | |||||
if (error) { | |||||
device_printf(dev, "init: Error retrieving HW capabilities;" | |||||
" status code %d\n", error); | |||||
goto err_out; | |||||
} | |||||
error = i40e_init_lan_hmc(hw, hw->func_caps.num_tx_qp, | |||||
hw->func_caps.num_rx_qp, 0, 0); | |||||
if (error) { | |||||
device_printf(dev, "init: LAN HMC init failed; status code %d\n", | |||||
error); | |||||
error = EIO; | |||||
goto err_out; | |||||
} | |||||
error = i40e_configure_lan_hmc(hw, I40E_HMC_MODEL_DIRECT_ONLY); | |||||
if (error) { | |||||
device_printf(dev, "init: LAN HMC config failed; status code %d\n", | |||||
error); | |||||
error = EIO; | |||||
goto err_out; | |||||
} | |||||
// XXX: possible fix for panic, but our failure recovery is still broken | |||||
error = ixl_switch_config(pf); | |||||
if (error) { | |||||
device_printf(dev, "init: ixl_switch_config() failed: %d\n", | |||||
error); | |||||
goto err_out; | |||||
} | |||||
error = i40e_aq_set_phy_int_mask(hw, IXL_DEFAULT_PHY_INT_MASK, | |||||
NULL); | |||||
if (error) { | |||||
device_printf(dev, "init: i40e_aq_set_phy_mask() failed: err %d," | |||||
" aq_err %d\n", error, hw->aq.asq_last_status); | |||||
error = EIO; | |||||
goto err_out; | |||||
} | |||||
error = i40e_set_fc(hw, &set_fc_err_mask, true); | |||||
if (error) { | |||||
device_printf(dev, "init: setting link flow control failed; retcode %d," | |||||
" fc_err_mask 0x%02x\n", error, set_fc_err_mask); | |||||
goto err_out; | |||||
} | |||||
// XXX: (Rebuild VSIs?) | |||||
/* Firmware delay workaround */ | |||||
if (((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver < 33)) || | |||||
(hw->aq.fw_maj_ver < 4)) { | |||||
i40e_msec_delay(75); | |||||
error = i40e_aq_set_link_restart_an(hw, TRUE, NULL); | |||||
if (error) { | |||||
device_printf(dev, "init: link restart failed, aq_err %d\n", | |||||
hw->aq.asq_last_status); | |||||
goto err_out; | |||||
} | |||||
} | |||||
err_out: | |||||
return (error); | |||||
} | |||||
static void | |||||
ixl_init(void *arg) | ixl_init(void *arg) | ||||
{ | { | ||||
struct ixl_pf *pf = arg; | struct ixl_pf *pf = arg; | ||||
struct ixl_vsi *vsi = &pf->vsi; | |||||
device_t dev = pf->dev; | |||||
int error = 0; | |||||
/* | |||||
* If the aq is dead here, it probably means something outside of the driver | |||||
* did something to the adapter, like a PF reset. | |||||
* So rebuild the driver's state here if that occurs. | |||||
*/ | |||||
if (!i40e_check_asq_alive(&pf->hw)) { | |||||
device_printf(dev, "Admin Queue is down; resetting...\n"); | |||||
IXL_PF_LOCK(pf); | IXL_PF_LOCK(pf); | ||||
ixl_init_locked(pf); | ixl_teardown_hw_structs(pf); | ||||
ixl_reset(pf); | |||||
IXL_PF_UNLOCK(pf); | IXL_PF_UNLOCK(pf); | ||||
} | |||||
/* | |||||
* Set up LAN queue interrupts here. | |||||
* Kernel interrupt setup functions cannot be called while holding a lock, | |||||
* so this is done outside of init_locked(). | |||||
*/ | |||||
if (pf->msix > 1) { | |||||
/* Teardown existing interrupts, if they exist */ | |||||
ixl_teardown_queue_msix(vsi); | |||||
ixl_free_queue_tqs(vsi); | |||||
/* Then set them up again */ | |||||
error = ixl_setup_queue_msix(vsi); | |||||
if (error) | |||||
device_printf(dev, "ixl_setup_queue_msix() error: %d\n", | |||||
error); | |||||
error = ixl_setup_queue_tqs(vsi); | |||||
if (error) | |||||
device_printf(dev, "ixl_setup_queue_tqs() error: %d\n", | |||||
error); | |||||
} else | |||||
// possibly broken | |||||
error = ixl_assign_vsi_legacy(pf); | |||||
if (error) { | |||||
device_printf(pf->dev, "assign_vsi_msix/legacy error: %d\n", error); | |||||
return; | return; | ||||
} | } | ||||
IXL_PF_LOCK(pf); | |||||
ixl_init_locked(pf); | |||||
IXL_PF_UNLOCK(pf); | |||||
} | |||||
/* | /* | ||||
** | |||||
** MSIX Interrupt Handlers and Tasklets | ** MSIX Interrupt Handlers and Tasklets | ||||
** | |||||
*/ | */ | ||||
static void | static void | ||||
ixl_handle_que(void *context, int pending) | ixl_handle_que(void *context, int pending) | ||||
{ | { | ||||
struct ixl_queue *que = context; | struct ixl_queue *que = context; | ||||
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 tx_ring *txr = &que->txr; | struct tx_ring *txr = &que->txr; | ||||
struct ifnet *ifp = vsi->ifp; | struct ifnet *ifp = vsi->ifp; | ||||
bool more; | bool more; | ||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | ||||
more = ixl_rxeof(que, IXL_RX_LIMIT); | more = ixl_rxeof(que, IXL_RX_LIMIT); | ||||
IXL_TX_LOCK(txr); | IXL_TX_LOCK(txr); | ||||
ixl_txeof(que); | ixl_txeof(que); | ||||
if (!drbr_empty(ifp, txr->br)) | if (!drbr_empty(ifp, txr->br)) | ||||
ixl_mq_start_locked(ifp, txr); | ixl_mq_start_locked(ifp, txr); | ||||
IXL_TX_UNLOCK(txr); | IXL_TX_UNLOCK(txr); | ||||
if (more) { | if (more) { | ||||
taskqueue_enqueue(que->tq, &que->task); | taskqueue_enqueue(que->tq, &que->task); | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
/* Reenable this interrupt - hmmm */ | /* Reenable this interrupt - hmmm */ | ||||
Lint: Possible Spelling Mistake: Possible spelling error. You wrote 'reenable', but did you mean 're-enable'? | |||||
ixl_enable_queue(hw, que->me); | ixl_enable_queue(hw, que->me); | ||||
return; | return; | ||||
} | } | ||||
/********************************************************************* | /********************************************************************* | ||||
* | * | ||||
* Legacy Interrupt Service routine | * Legacy Interrupt Service routine | ||||
▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | |||||
* MSIX Admin Queue Interrupt Service routine | * MSIX Admin Queue Interrupt Service routine | ||||
* | * | ||||
**********************************************************************/ | **********************************************************************/ | ||||
static void | static void | ||||
ixl_msix_adminq(void *arg) | ixl_msix_adminq(void *arg) | ||||
{ | { | ||||
struct ixl_pf *pf = arg; | struct ixl_pf *pf = arg; | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
u32 reg, mask; | u32 reg, mask, rstat_reg; | ||||
bool do_task = FALSE; | |||||
++pf->admin_irq; | ++pf->admin_irq; | ||||
reg = rd32(hw, I40E_PFINT_ICR0); | reg = rd32(hw, I40E_PFINT_ICR0); | ||||
mask = rd32(hw, I40E_PFINT_ICR0_ENA); | mask = rd32(hw, I40E_PFINT_ICR0_ENA); | ||||
/* Check on the cause */ | /* Check on the cause */ | ||||
if (reg & I40E_PFINT_ICR0_ADMINQ_MASK) | if (reg & I40E_PFINT_ICR0_ADMINQ_MASK) { | ||||
mask &= ~I40E_PFINT_ICR0_ENA_ADMINQ_MASK; | mask &= ~I40E_PFINT_ICR0_ADMINQ_MASK; | ||||
do_task = TRUE; | |||||
} | |||||
if (reg & I40E_PFINT_ICR0_MAL_DETECT_MASK) { | if (reg & I40E_PFINT_ICR0_MAL_DETECT_MASK) { | ||||
ixl_handle_mdd_event(pf); | ixl_handle_mdd_event(pf); | ||||
mask &= ~I40E_PFINT_ICR0_ENA_MAL_DETECT_MASK; | mask &= ~I40E_PFINT_ICR0_MAL_DETECT_MASK; | ||||
} | } | ||||
if (reg & I40E_PFINT_ICR0_GRST_MASK) { | |||||
device_printf(pf->dev, "Reset Requested!\n"); | |||||
rstat_reg = rd32(hw, I40E_GLGEN_RSTAT); | |||||
rstat_reg = (rstat_reg & I40E_GLGEN_RSTAT_RESET_TYPE_MASK) | |||||
>> I40E_GLGEN_RSTAT_RESET_TYPE_SHIFT; | |||||
device_printf(pf->dev, "Reset type: "); | |||||
switch (rstat_reg) { | |||||
/* These others might be handled similarly to an EMPR reset */ | |||||
case I40E_RESET_CORER: | |||||
printf("CORER\n"); | |||||
break; | |||||
case I40E_RESET_GLOBR: | |||||
printf("GLOBR\n"); | |||||
break; | |||||
case I40E_RESET_EMPR: | |||||
printf("EMPR\n"); | |||||
atomic_set_int(&pf->state, IXL_PF_STATE_EMPR_RESETTING); | |||||
break; | |||||
default: | |||||
printf("?\n"); | |||||
break; | |||||
} | |||||
// overload admin queue task to check reset progress? | |||||
do_task = TRUE; | |||||
} | |||||
if (reg & I40E_PFINT_ICR0_ECC_ERR_MASK) { | |||||
device_printf(pf->dev, "ECC Error detected!\n"); | |||||
} | |||||
if (reg & I40E_PFINT_ICR0_HMC_ERR_MASK) { | |||||
device_printf(pf->dev, "HMC Error detected!\n"); | |||||
} | |||||
if (reg & I40E_PFINT_ICR0_PCI_EXCEPTION_MASK) { | |||||
device_printf(pf->dev, "PCI Exception detected!\n"); | |||||
} | |||||
#ifdef PCI_IOV | #ifdef PCI_IOV | ||||
if (reg & I40E_PFINT_ICR0_VFLR_MASK) { | if (reg & I40E_PFINT_ICR0_VFLR_MASK) { | ||||
mask &= ~I40E_PFINT_ICR0_ENA_VFLR_MASK; | mask &= ~I40E_PFINT_ICR0_ENA_VFLR_MASK; | ||||
taskqueue_enqueue(pf->tq, &pf->vflr_task); | taskqueue_enqueue(pf->tq, &pf->vflr_task); | ||||
} | } | ||||
#endif | #endif | ||||
reg = rd32(hw, I40E_PFINT_DYN_CTL0); | reg = rd32(hw, I40E_PFINT_DYN_CTL0); | ||||
reg = reg | I40E_PFINT_DYN_CTL0_CLEARPBA_MASK; | reg = reg | I40E_PFINT_DYN_CTL0_CLEARPBA_MASK; | ||||
wr32(hw, I40E_PFINT_DYN_CTL0, reg); | wr32(hw, I40E_PFINT_DYN_CTL0, reg); | ||||
if (do_task) | |||||
taskqueue_enqueue(pf->tq, &pf->adminq); | taskqueue_enqueue(pf->tq, &pf->adminq); | ||||
return; | |||||
} | } | ||||
/********************************************************************* | /********************************************************************* | ||||
* | * | ||||
* Media Ioctl callback | * Media Ioctl callback | ||||
* | * | ||||
* This routine is called whenever the user queries the status of | * This routine is called whenever the user queries the status of | ||||
* the interface using ifconfig. | * the interface using ifconfig. | ||||
Show All 17 Lines | ixl_media_status(struct ifnet * ifp, struct ifmediareq * ifmr) | ||||
ifmr->ifm_active = IFM_ETHER; | ifmr->ifm_active = IFM_ETHER; | ||||
if (!pf->link_up) { | if (!pf->link_up) { | ||||
IXL_PF_UNLOCK(pf); | IXL_PF_UNLOCK(pf); | ||||
return; | return; | ||||
} | } | ||||
ifmr->ifm_status |= IFM_ACTIVE; | ifmr->ifm_status |= IFM_ACTIVE; | ||||
/* Hardware is always full-duplex */ | |||||
/* Hardware always does full-duplex */ | |||||
ifmr->ifm_active |= IFM_FDX; | ifmr->ifm_active |= IFM_FDX; | ||||
switch (hw->phy.link_info.phy_type) { | switch (hw->phy.link_info.phy_type) { | ||||
/* 100 M */ | /* 100 M */ | ||||
case I40E_PHY_TYPE_100BASE_TX: | case I40E_PHY_TYPE_100BASE_TX: | ||||
ifmr->ifm_active |= IFM_100_TX; | ifmr->ifm_active |= IFM_100_TX; | ||||
break; | break; | ||||
/* 1 G */ | /* 1 G */ | ||||
case I40E_PHY_TYPE_1000BASE_T: | case I40E_PHY_TYPE_1000BASE_T: | ||||
ifmr->ifm_active |= IFM_1000_T; | ifmr->ifm_active |= IFM_1000_T; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_1000BASE_SX: | case I40E_PHY_TYPE_1000BASE_SX: | ||||
ifmr->ifm_active |= IFM_1000_SX; | ifmr->ifm_active |= IFM_1000_SX; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_1000BASE_LX: | case I40E_PHY_TYPE_1000BASE_LX: | ||||
ifmr->ifm_active |= IFM_1000_LX; | ifmr->ifm_active |= IFM_1000_LX; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_1000BASE_T_OPTICAL: | |||||
ifmr->ifm_active |= IFM_OTHER; | |||||
break; | |||||
/* 10 G */ | /* 10 G */ | ||||
case I40E_PHY_TYPE_10GBASE_SFPP_CU: | case I40E_PHY_TYPE_10GBASE_SFPP_CU: | ||||
ifmr->ifm_active |= IFM_10G_TWINAX; | ifmr->ifm_active |= IFM_10G_TWINAX; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_10GBASE_SR: | case I40E_PHY_TYPE_10GBASE_SR: | ||||
ifmr->ifm_active |= IFM_10G_SR; | ifmr->ifm_active |= IFM_10G_SR; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_10GBASE_LR: | case I40E_PHY_TYPE_10GBASE_LR: | ||||
ifmr->ifm_active |= IFM_10G_LR; | ifmr->ifm_active |= IFM_10G_LR; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_10GBASE_T: | case I40E_PHY_TYPE_10GBASE_T: | ||||
ifmr->ifm_active |= IFM_10G_T; | ifmr->ifm_active |= IFM_10G_T; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_XAUI: | |||||
case I40E_PHY_TYPE_XFI: | |||||
case I40E_PHY_TYPE_10GBASE_AOC: | |||||
ifmr->ifm_active |= IFM_OTHER; | |||||
break; | |||||
/* 40 G */ | /* 40 G */ | ||||
case I40E_PHY_TYPE_40GBASE_CR4: | case I40E_PHY_TYPE_40GBASE_CR4: | ||||
case I40E_PHY_TYPE_40GBASE_CR4_CU: | case I40E_PHY_TYPE_40GBASE_CR4_CU: | ||||
ifmr->ifm_active |= IFM_40G_CR4; | ifmr->ifm_active |= IFM_40G_CR4; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_40GBASE_SR4: | case I40E_PHY_TYPE_40GBASE_SR4: | ||||
ifmr->ifm_active |= IFM_40G_SR4; | ifmr->ifm_active |= IFM_40G_SR4; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_40GBASE_LR4: | case I40E_PHY_TYPE_40GBASE_LR4: | ||||
ifmr->ifm_active |= IFM_40G_LR4; | ifmr->ifm_active |= IFM_40G_LR4; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_XLAUI: | |||||
ifmr->ifm_active |= IFM_OTHER; | |||||
break; | |||||
#ifndef IFM_ETH_XTYPE | #ifndef IFM_ETH_XTYPE | ||||
case I40E_PHY_TYPE_1000BASE_KX: | case I40E_PHY_TYPE_1000BASE_KX: | ||||
ifmr->ifm_active |= IFM_1000_CX; | ifmr->ifm_active |= IFM_1000_CX; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_SGMII: | |||||
ifmr->ifm_active |= IFM_OTHER; | |||||
break; | |||||
case I40E_PHY_TYPE_10GBASE_CR1_CU: | case I40E_PHY_TYPE_10GBASE_CR1_CU: | ||||
case I40E_PHY_TYPE_10GBASE_CR1: | case I40E_PHY_TYPE_10GBASE_CR1: | ||||
ifmr->ifm_active |= IFM_10G_TWINAX; | ifmr->ifm_active |= IFM_10G_TWINAX; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_10GBASE_KX4: | case I40E_PHY_TYPE_10GBASE_KX4: | ||||
ifmr->ifm_active |= IFM_10G_CX4; | ifmr->ifm_active |= IFM_10G_CX4; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_10GBASE_KR: | case I40E_PHY_TYPE_10GBASE_KR: | ||||
ifmr->ifm_active |= IFM_10G_SR; | ifmr->ifm_active |= IFM_10G_SR; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_SFI: | |||||
ifmr->ifm_active |= IFM_OTHER; | |||||
break; | |||||
case I40E_PHY_TYPE_40GBASE_KR4: | case I40E_PHY_TYPE_40GBASE_KR4: | ||||
case I40E_PHY_TYPE_XLPPI: | case I40E_PHY_TYPE_XLPPI: | ||||
case I40E_PHY_TYPE_40GBASE_AOC: | |||||
ifmr->ifm_active |= IFM_40G_SR4; | ifmr->ifm_active |= IFM_40G_SR4; | ||||
break; | break; | ||||
#else | #else | ||||
case I40E_PHY_TYPE_1000BASE_KX: | case I40E_PHY_TYPE_1000BASE_KX: | ||||
ifmr->ifm_active |= IFM_1000_KX; | ifmr->ifm_active |= IFM_1000_KX; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_SGMII: | |||||
ifmr->ifm_active |= IFM_1000_SGMII; | |||||
break; | |||||
/* ERJ: What's the difference between these? */ | /* ERJ: What's the difference between these? */ | ||||
case I40E_PHY_TYPE_10GBASE_CR1_CU: | case I40E_PHY_TYPE_10GBASE_CR1_CU: | ||||
case I40E_PHY_TYPE_10GBASE_CR1: | case I40E_PHY_TYPE_10GBASE_CR1: | ||||
ifmr->ifm_active |= IFM_10G_CR1; | ifmr->ifm_active |= IFM_10G_CR1; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_10GBASE_KX4: | case I40E_PHY_TYPE_10GBASE_KX4: | ||||
ifmr->ifm_active |= IFM_10G_KX4; | ifmr->ifm_active |= IFM_10G_KX4; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_10GBASE_KR: | case I40E_PHY_TYPE_10GBASE_KR: | ||||
ifmr->ifm_active |= IFM_10G_KR; | ifmr->ifm_active |= IFM_10G_KR; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_SFI: | |||||
ifmr->ifm_active |= IFM_10G_SFI; | |||||
break; | |||||
/* Our single 20G media type */ | |||||
case I40E_PHY_TYPE_20GBASE_KR2: | case I40E_PHY_TYPE_20GBASE_KR2: | ||||
ifmr->ifm_active |= IFM_20G_KR2; | ifmr->ifm_active |= IFM_20G_KR2; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_40GBASE_KR4: | case I40E_PHY_TYPE_40GBASE_KR4: | ||||
ifmr->ifm_active |= IFM_40G_KR4; | ifmr->ifm_active |= IFM_40G_KR4; | ||||
break; | break; | ||||
case I40E_PHY_TYPE_XLPPI: | case I40E_PHY_TYPE_XLPPI: | ||||
case I40E_PHY_TYPE_40GBASE_AOC: | |||||
ifmr->ifm_active |= IFM_40G_XLPPI; | ifmr->ifm_active |= IFM_40G_XLPPI; | ||||
break; | break; | ||||
#endif | #endif | ||||
/* Unknown to driver */ | |||||
default: | default: | ||||
ifmr->ifm_active |= IFM_UNKNOWN; | ifmr->ifm_active |= IFM_UNKNOWN; | ||||
break; | break; | ||||
} | } | ||||
/* Report flow control status as well */ | /* Report flow control status as well */ | ||||
if (hw->phy.link_info.an_info & I40E_AQ_LINK_PAUSE_TX) | if (hw->phy.link_info.an_info & I40E_AQ_LINK_PAUSE_TX) | ||||
ifmr->ifm_active |= IFM_ETH_TXPAUSE; | ifmr->ifm_active |= IFM_ETH_TXPAUSE; | ||||
if (hw->phy.link_info.an_info & I40E_AQ_LINK_PAUSE_RX) | if (hw->phy.link_info.an_info & I40E_AQ_LINK_PAUSE_RX) | ||||
ifmr->ifm_active |= IFM_ETH_RXPAUSE; | ifmr->ifm_active |= IFM_ETH_RXPAUSE; | ||||
IXL_PF_UNLOCK(pf); | IXL_PF_UNLOCK(pf); | ||||
return; | return; | ||||
} | } | ||||
/********************************************************************* | /* | ||||
* | * NOTE: Fortville does not support forcing media speeds. Instead, | ||||
* Media Ioctl callback | * use the set_advertise sysctl to set the speeds Fortville | ||||
* | * will advertise or be allowed to operate at. | ||||
* This routine is called when the user changes speed/duplex using | */ | ||||
* media/mediopt option with ifconfig. | |||||
* | |||||
**********************************************************************/ | |||||
static int | static int | ||||
ixl_media_change(struct ifnet * ifp) | ixl_media_change(struct ifnet * ifp) | ||||
{ | { | ||||
struct ixl_vsi *vsi = ifp->if_softc; | struct ixl_vsi *vsi = ifp->if_softc; | ||||
struct ifmedia *ifm = &vsi->media; | struct ifmedia *ifm = &vsi->media; | ||||
INIT_DEBUGOUT("ixl_media_change: begin"); | INIT_DEBUGOUT("ixl_media_change: begin"); | ||||
if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) | if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) | ||||
return (EINVAL); | return (EINVAL); | ||||
if_printf(ifp, "Media change is currently not supported.\n"); | if_printf(ifp, "Media change is not supported.\n"); | ||||
return (ENODEV); | return (ENODEV); | ||||
} | } | ||||
#ifdef IXL_FDIR | #ifdef IXL_FDIR | ||||
/* | /* | ||||
** ATR: Application Targetted Receive - creates a filter | ** ATR: Application Targetted Receive - creates a filter | ||||
▲ Show 20 Lines • Show All 196 Lines • ▼ Show 20 Lines | |||||
/********************************************************************* | /********************************************************************* | ||||
* Timer routine | * Timer routine | ||||
* | * | ||||
* This routine checks for link status,updates statistics, | * This routine checks for link status,updates statistics, | ||||
* and runs the watchdog check. | * and runs the watchdog check. | ||||
* | * | ||||
* Only runs when the driver is configured UP and RUNNING. | |||||
* | |||||
**********************************************************************/ | **********************************************************************/ | ||||
static void | static void | ||||
ixl_local_timer(void *arg) | ixl_local_timer(void *arg) | ||||
{ | { | ||||
struct ixl_pf *pf = arg; | struct ixl_pf *pf = arg; | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
struct ixl_vsi *vsi = &pf->vsi; | struct ixl_vsi *vsi = &pf->vsi; | ||||
Show All 11 Lines | ixl_local_timer(void *arg) | ||||
ixl_update_stats_counters(pf); | ixl_update_stats_counters(pf); | ||||
/* | /* | ||||
** Check status of the queues | ** Check status of the queues | ||||
*/ | */ | ||||
mask = (I40E_PFINT_DYN_CTLN_INTENA_MASK | | mask = (I40E_PFINT_DYN_CTLN_INTENA_MASK | | ||||
I40E_PFINT_DYN_CTLN_SWINT_TRIG_MASK); | I40E_PFINT_DYN_CTLN_SWINT_TRIG_MASK); | ||||
for (int i = 0; i < vsi->num_queues; i++,que++) { | for (int i = 0; i < vsi->num_queues; i++, que++) { | ||||
/* Any queues with outstanding work get a sw irq */ | /* Any queues with outstanding work get a sw irq */ | ||||
if (que->busy) | if (que->busy) | ||||
wr32(hw, I40E_PFINT_DYN_CTLN(que->me), mask); | wr32(hw, I40E_PFINT_DYN_CTLN(que->me), mask); | ||||
/* | /* | ||||
** Each time txeof runs without cleaning, but there | ** Each time txeof runs without cleaning, but there | ||||
** are uncleaned descriptors it increments busy. If | ** are uncleaned descriptors it increments busy. If | ||||
** we get to 5 we declare it hung. | ** we get to 5 we declare it hung. | ||||
*/ | */ | ||||
Show All 36 Lines | |||||
static void | static void | ||||
ixl_update_link_status(struct ixl_pf *pf) | ixl_update_link_status(struct ixl_pf *pf) | ||||
{ | { | ||||
struct ixl_vsi *vsi = &pf->vsi; | struct ixl_vsi *vsi = &pf->vsi; | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
struct ifnet *ifp = vsi->ifp; | struct ifnet *ifp = vsi->ifp; | ||||
device_t dev = pf->dev; | device_t dev = pf->dev; | ||||
if (pf->link_up){ | if (pf->link_up) { | ||||
if (vsi->link_active == FALSE) { | if (vsi->link_active == FALSE) { | ||||
pf->fc = hw->fc.current_mode; | pf->fc = hw->fc.current_mode; | ||||
if (bootverbose) { | if (bootverbose) { | ||||
device_printf(dev,"Link is up %d Gbps %s," | device_printf(dev,"Link is up %d Gbps %s," | ||||
" Flow Control: %s\n", | " Flow Control: %s\n", | ||||
((pf->link_speed == | ((pf->link_speed == | ||||
I40E_LINK_SPEED_40GB)? 40:10), | I40E_LINK_SPEED_40GB)? 40:10), | ||||
"Full Duplex", ixl_fc_string[pf->fc]); | "Full Duplex", ixl_fc_string[pf->fc]); | ||||
Show All 10 Lines | if (vsi->link_active == FALSE) { | ||||
I40E_LINK_SPEED_100MB)) | I40E_LINK_SPEED_100MB)) | ||||
device_printf(dev, "The partition detected" | device_printf(dev, "The partition detected" | ||||
"link speed that is less than 10Gbps\n"); | "link speed that is less than 10Gbps\n"); | ||||
if_link_state_change(ifp, LINK_STATE_UP); | if_link_state_change(ifp, LINK_STATE_UP); | ||||
} | } | ||||
} else { /* Link down */ | } else { /* Link down */ | ||||
if (vsi->link_active == TRUE) { | if (vsi->link_active == TRUE) { | ||||
if (bootverbose) | if (bootverbose) | ||||
device_printf(dev,"Link is Down\n"); | device_printf(dev, "Link is Down\n"); | ||||
if_link_state_change(ifp, LINK_STATE_DOWN); | if_link_state_change(ifp, LINK_STATE_DOWN); | ||||
vsi->link_active = FALSE; | vsi->link_active = FALSE; | ||||
} | } | ||||
} | } | ||||
return; | return; | ||||
} | } | ||||
static void | |||||
ixl_stop(struct ixl_pf *pf) | |||||
{ | |||||
IXL_PF_LOCK(pf); | |||||
ixl_stop_locked(pf); | |||||
IXL_PF_UNLOCK(pf); | |||||
ixl_teardown_queue_msix(&pf->vsi); | |||||
ixl_free_queue_tqs(&pf->vsi); | |||||
} | |||||
/********************************************************************* | /********************************************************************* | ||||
* | * | ||||
* 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 | ||||
ixl_stop(struct ixl_pf *pf) | ixl_stop_locked(struct ixl_pf *pf) | ||||
{ | { | ||||
struct ixl_vsi *vsi = &pf->vsi; | struct ixl_vsi *vsi = &pf->vsi; | ||||
struct ifnet *ifp = vsi->ifp; | struct ifnet *ifp = vsi->ifp; | ||||
mtx_assert(&pf->pf_mtx, MA_OWNED); | |||||
INIT_DEBUGOUT("ixl_stop: begin\n"); | INIT_DEBUGOUT("ixl_stop: begin\n"); | ||||
if (pf->num_vfs == 0) | |||||
ixl_disable_intr(vsi); | |||||
else | |||||
ixl_disable_rings_intr(vsi); | |||||
ixl_disable_rings(vsi); | |||||
/* Tell the stack that the interface is no longer active */ | IXL_PF_LOCK_ASSERT(pf); | ||||
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); | |||||
/* Stop the local timer */ | /* Stop the local timer */ | ||||
callout_stop(&pf->timer); | callout_stop(&pf->timer); | ||||
return; | ixl_disable_rings_intr(vsi); | ||||
ixl_disable_rings(vsi); | |||||
/* Tell the stack that the interface is no longer active */ | |||||
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING); | |||||
} | } | ||||
/********************************************************************* | /********************************************************************* | ||||
* | * | ||||
* Setup MSIX Interrupt resources and handlers for the VSI | * Setup MSIX Interrupt resources and handlers for the VSI | ||||
* | * | ||||
**********************************************************************/ | **********************************************************************/ | ||||
static int | static int | ||||
ixl_assign_vsi_legacy(struct ixl_pf *pf) | ixl_assign_vsi_legacy(struct ixl_pf *pf) | ||||
{ | { | ||||
device_t dev = pf->dev; | device_t dev = pf->dev; | ||||
struct ixl_vsi *vsi = &pf->vsi; | struct ixl_vsi *vsi = &pf->vsi; | ||||
struct ixl_queue *que = vsi->queues; | struct ixl_queue *que = vsi->queues; | ||||
int error, rid = 0; | int error, rid = 0; | ||||
if (pf->msix == 1) | if (pf->msix == 1) | ||||
rid = 1; | rid = 1; | ||||
pf->res = bus_alloc_resource_any(dev, SYS_RES_IRQ, | pf->res = bus_alloc_resource_any(dev, SYS_RES_IRQ, | ||||
&rid, RF_SHAREABLE | RF_ACTIVE); | &rid, RF_SHAREABLE | RF_ACTIVE); | ||||
if (pf->res == NULL) { | if (pf->res == NULL) { | ||||
device_printf(dev,"Unable to allocate" | device_printf(dev, "Unable to allocate" | ||||
" bus resource: vsi legacy/msi interrupt\n"); | " bus resource: vsi legacy/msi interrupt\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
/* Set the handler function */ | /* Set the handler function */ | ||||
error = bus_setup_intr(dev, pf->res, | error = bus_setup_intr(dev, pf->res, | ||||
INTR_TYPE_NET | INTR_MPSAFE, NULL, | INTR_TYPE_NET | INTR_MPSAFE, NULL, | ||||
ixl_intr, pf, &pf->tag); | ixl_intr, pf, &pf->tag); | ||||
if (error) { | if (error) { | ||||
pf->res = NULL; | pf->res = NULL; | ||||
device_printf(dev, "Failed to register legacy/msi handler"); | device_printf(dev, "Failed to register legacy/msi handler\n"); | ||||
return (error); | return (error); | ||||
} | } | ||||
bus_describe_intr(dev, pf->res, pf->tag, "irq0"); | bus_describe_intr(dev, pf->res, pf->tag, "irq0"); | ||||
TASK_INIT(&que->tx_task, 0, ixl_deferred_mq_start, que); | TASK_INIT(&que->tx_task, 0, ixl_deferred_mq_start, que); | ||||
TASK_INIT(&que->task, 0, ixl_handle_que, que); | TASK_INIT(&que->task, 0, ixl_handle_que, que); | ||||
que->tq = taskqueue_create_fast("ixl_que", M_NOWAIT, | que->tq = taskqueue_create_fast("ixl_que", M_NOWAIT, | ||||
taskqueue_thread_enqueue, &que->tq); | taskqueue_thread_enqueue, &que->tq); | ||||
taskqueue_start_threads(&que->tq, 1, PI_NET, "%s que", | taskqueue_start_threads(&que->tq, 1, PI_NET, "%s que", | ||||
device_get_nameunit(dev)); | device_get_nameunit(dev)); | ||||
TASK_INIT(&pf->adminq, 0, ixl_do_adminq, pf); | TASK_INIT(&pf->adminq, 0, ixl_do_adminq, pf); | ||||
#ifdef PCI_IOV | |||||
TASK_INIT(&pf->vflr_task, 0, ixl_handle_vflr, pf); | |||||
#endif | |||||
pf->tq = taskqueue_create_fast("ixl_adm", M_NOWAIT, | pf->tq = taskqueue_create_fast("ixl_adm", M_NOWAIT, | ||||
taskqueue_thread_enqueue, &pf->tq); | taskqueue_thread_enqueue, &pf->tq); | ||||
taskqueue_start_threads(&pf->tq, 1, PI_NET, "%s adminq", | taskqueue_start_threads(&pf->tq, 1, PI_NET, "%s adminq", | ||||
device_get_nameunit(dev)); | device_get_nameunit(dev)); | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static int | ||||
ixl_init_taskqueues(struct ixl_pf *pf) | ixl_setup_adminq_tq(struct ixl_pf *pf) | ||||
{ | { | ||||
struct ixl_vsi *vsi = &pf->vsi; | |||||
struct ixl_queue *que = vsi->queues; | |||||
device_t dev = pf->dev; | device_t dev = pf->dev; | ||||
#ifdef RSS | int error = 0; | ||||
int cpu_id; | |||||
cpuset_t cpu_mask; | |||||
#endif | |||||
/* Tasklet for Admin Queue */ | /* Tasklet for Admin Queue interrupts */ | ||||
TASK_INIT(&pf->adminq, 0, ixl_do_adminq, pf); | TASK_INIT(&pf->adminq, 0, ixl_do_adminq, pf); | ||||
#ifdef PCI_IOV | #ifdef PCI_IOV | ||||
/* VFLR Tasklet */ | /* VFLR Tasklet */ | ||||
TASK_INIT(&pf->vflr_task, 0, ixl_handle_vflr, pf); | TASK_INIT(&pf->vflr_task, 0, ixl_handle_vflr, pf); | ||||
#endif | #endif | ||||
/* Create and start Admin Queue taskqueue */ | |||||
/* Create and start PF taskqueue */ | pf->tq = taskqueue_create_fast("ixl_aq", M_NOWAIT, | ||||
pf->tq = taskqueue_create_fast("ixl_adm", M_NOWAIT, | |||||
taskqueue_thread_enqueue, &pf->tq); | taskqueue_thread_enqueue, &pf->tq); | ||||
taskqueue_start_threads(&pf->tq, 1, PI_NET, "%s adminq", | if (!pf->tq) { | ||||
device_printf(dev, "taskqueue_create_fast (for AQ) returned NULL!\n"); | |||||
return (ENOMEM); | |||||
} | |||||
error = taskqueue_start_threads(&pf->tq, 1, PI_NET, "%s aq", | |||||
device_get_nameunit(dev)); | device_get_nameunit(dev)); | ||||
if (error) { | |||||
device_printf(dev, "taskqueue_start_threads (for AQ) error: %d\n", | |||||
error); | |||||
taskqueue_free(pf->tq); | |||||
return (error); | |||||
} | |||||
return (0); | |||||
} | |||||
static int | |||||
ixl_setup_queue_tqs(struct ixl_vsi *vsi) | |||||
{ | |||||
struct ixl_queue *que = vsi->queues; | |||||
device_t dev = vsi->dev; | |||||
/* Create queue tasks and start queue taskqueues */ | /* Create queue tasks and start queue taskqueues */ | ||||
for (int i = 0; i < vsi->num_queues; i++, que++) { | for (int i = 0; i < vsi->num_queues; i++, que++) { | ||||
TASK_INIT(&que->tx_task, 0, ixl_deferred_mq_start, que); | TASK_INIT(&que->tx_task, 0, ixl_deferred_mq_start, que); | ||||
TASK_INIT(&que->task, 0, ixl_handle_que, que); | TASK_INIT(&que->task, 0, ixl_handle_que, que); | ||||
que->tq = taskqueue_create_fast("ixl_que", M_NOWAIT, | que->tq = taskqueue_create_fast("ixl_que", M_NOWAIT, | ||||
taskqueue_thread_enqueue, &que->tq); | taskqueue_thread_enqueue, &que->tq); | ||||
#ifdef RSS | #ifdef RSS | ||||
cpu_id = rss_getcpu(i % rss_getnumbuckets()); | |||||
CPU_SETOF(cpu_id, &cpu_mask); | CPU_SETOF(cpu_id, &cpu_mask); | ||||
taskqueue_start_threads_cpuset(&que->tq, 1, PI_NET, | taskqueue_start_threads_cpuset(&que->tq, 1, PI_NET, | ||||
&cpu_mask, "%s (bucket %d)", | &cpu_mask, "%s (bucket %d)", | ||||
device_get_nameunit(dev), cpu_id); | device_get_nameunit(dev), cpu_id); | ||||
#else | #else | ||||
taskqueue_start_threads(&que->tq, 1, PI_NET, | taskqueue_start_threads(&que->tq, 1, PI_NET, | ||||
"%s (que %d)", device_get_nameunit(dev), que->me); | "%s (que %d)", device_get_nameunit(dev), que->me); | ||||
#endif | #endif | ||||
} | } | ||||
return (0); | |||||
} | } | ||||
static void | static void | ||||
ixl_free_taskqueues(struct ixl_pf *pf) | ixl_free_adminq_tq(struct ixl_pf *pf) | ||||
{ | { | ||||
struct ixl_vsi *vsi = &pf->vsi; | if (pf->tq) { | ||||
taskqueue_free(pf->tq); | |||||
pf->tq = NULL; | |||||
} | |||||
} | |||||
static void | |||||
ixl_free_queue_tqs(struct ixl_vsi *vsi) | |||||
{ | |||||
struct ixl_queue *que = vsi->queues; | struct ixl_queue *que = vsi->queues; | ||||
if (pf->tq) | |||||
taskqueue_free(pf->tq); | |||||
for (int i = 0; i < vsi->num_queues; i++, que++) { | for (int i = 0; i < vsi->num_queues; i++, que++) { | ||||
if (que->tq) | if (que->tq) { | ||||
taskqueue_free(que->tq); | taskqueue_free(que->tq); | ||||
que->tq = NULL; | |||||
} | } | ||||
} | } | ||||
} | |||||
/********************************************************************* | |||||
* | |||||
* Setup MSIX Interrupt resources and handlers for the VSI | |||||
* | |||||
**********************************************************************/ | |||||
static int | static int | ||||
ixl_assign_vsi_msix(struct ixl_pf *pf) | ixl_setup_adminq_msix(struct ixl_pf *pf) | ||||
{ | { | ||||
device_t dev = pf->dev; | device_t dev = pf->dev; | ||||
struct ixl_vsi *vsi = &pf->vsi; | int rid, error = 0; | ||||
struct ixl_queue *que = vsi->queues; | |||||
struct tx_ring *txr; | |||||
int error, rid, vector = 0; | |||||
/* Admin Que is vector 0*/ | /* Admin IRQ rid is 1, vector is 0 */ | ||||
rid = vector + 1; | rid = 1; | ||||
/* Get interrupt resource from bus */ | |||||
pf->res = bus_alloc_resource_any(dev, | pf->res = bus_alloc_resource_any(dev, | ||||
SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); | SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); | ||||
if (!pf->res) { | if (!pf->res) { | ||||
device_printf(dev,"Unable to allocate" | device_printf(dev, "bus_alloc_resource_any() for Admin Queue" | ||||
" bus resource: Adminq interrupt [%d]\n", rid); | " interrupt failed [rid=%d]\n", rid); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
/* Set the adminq vector and handler */ | /* Then associate interrupt with handler */ | ||||
error = bus_setup_intr(dev, pf->res, | error = bus_setup_intr(dev, pf->res, | ||||
INTR_TYPE_NET | INTR_MPSAFE, NULL, | INTR_TYPE_NET | INTR_MPSAFE, NULL, | ||||
ixl_msix_adminq, pf, &pf->tag); | ixl_msix_adminq, pf, &pf->tag); | ||||
if (error) { | if (error) { | ||||
pf->res = NULL; | pf->res = NULL; | ||||
device_printf(dev, "Failed to register Admin que handler"); | device_printf(dev, "bus_setup_intr() for Admin Queue" | ||||
return (error); | " interrupt handler failed, error %d\n", error); | ||||
return (ENXIO); | |||||
} | } | ||||
bus_describe_intr(dev, pf->res, pf->tag, "aq"); | error = bus_describe_intr(dev, pf->res, pf->tag, "aq"); | ||||
pf->admvec = vector; | if (error) { | ||||
++vector; | /* Probably non-fatal? */ | ||||
device_printf(dev, "bus_describe_intr() for Admin Queue" | |||||
" interrupt name failed, error %d\n", error); | |||||
} | |||||
pf->admvec = 0; | |||||
/* Now set up the stations */ | return (0); | ||||
} | |||||
/* | |||||
* Allocate interrupt resources from bus and associate an interrupt handler | |||||
* to those for the VSI's queues. | |||||
*/ | |||||
static int | |||||
ixl_setup_queue_msix(struct ixl_vsi *vsi) | |||||
{ | |||||
device_t dev = vsi->dev; | |||||
struct ixl_queue *que = vsi->queues; | |||||
struct tx_ring *txr; | |||||
int error, rid, vector = 1; | |||||
#ifdef RSS | |||||
cpuset_t cpu_mask; | |||||
#endif | |||||
/* Queue interrupt vector numbers start at 1 (adminq intr is 0) */ | |||||
for (int i = 0; i < vsi->num_queues; i++, vector++, que++) { | for (int i = 0; i < vsi->num_queues; i++, vector++, que++) { | ||||
int cpu_id = i; | int cpu_id = i; | ||||
rid = vector + 1; | rid = vector + 1; | ||||
txr = &que->txr; | txr = &que->txr; | ||||
que->res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, | que->res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, | ||||
RF_SHAREABLE | RF_ACTIVE); | RF_SHAREABLE | RF_ACTIVE); | ||||
if (que->res == NULL) { | if (!que->res) { | ||||
device_printf(dev,"Unable to allocate" | device_printf(dev, "bus_alloc_resource_any() for" | ||||
" bus resource: que interrupt [%d]\n", vector); | " Queue %d interrupt failed [rid=%d]\n", | ||||
que->me, rid); | |||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
/* Set the handler function */ | /* Set the handler function */ | ||||
error = bus_setup_intr(dev, que->res, | error = bus_setup_intr(dev, que->res, | ||||
INTR_TYPE_NET | INTR_MPSAFE, NULL, | INTR_TYPE_NET | INTR_MPSAFE, NULL, | ||||
ixl_msix_que, que, &que->tag); | ixl_msix_que, que, &que->tag); | ||||
if (error) { | if (error) { | ||||
que->res = NULL; | device_printf(dev, "bus_setup_intr() for Queue %d" | ||||
device_printf(dev, "Failed to register que handler"); | " interrupt handler failed, error %d\n", | ||||
que->me, error); | |||||
// TODO: Check for error from this? | |||||
bus_release_resource(dev, SYS_RES_IRQ, rid, que->res); | |||||
return (error); | return (error); | ||||
} | } | ||||
bus_describe_intr(dev, que->res, que->tag, "q%d", i); | error = bus_describe_intr(dev, que->res, que->tag, "q%d", i); | ||||
if (error) { | |||||
device_printf(dev, "bus_describe_intr() for Queue %d" | |||||
" interrupt name failed, error %d\n", | |||||
que->me, error); | |||||
} | |||||
/* Bind the vector to a CPU */ | /* Bind the vector to a CPU */ | ||||
#ifdef RSS | #ifdef RSS | ||||
cpu_id = rss_getcpu(i % rss_getnumbuckets()); | cpu_id = rss_getcpu(i % rss_getnumbuckets()); | ||||
#endif | #endif | ||||
bus_bind_intr(dev, que->res, cpu_id); | error = bus_bind_intr(dev, que->res, cpu_id); | ||||
if (error) { | |||||
device_printf(dev, "bus_bind_intr() for Queue %d" | |||||
" to CPU %d failed, error %d\n", | |||||
que->me, cpu_id, error); | |||||
} | |||||
que->msix = vector; | que->msix = vector; | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Allocate MSI/X vectors | * Allocate MSI/X vectors | ||||
*/ | */ | ||||
static int | static int | ||||
ixl_init_msix(struct ixl_pf *pf) | ixl_init_msix(struct ixl_pf *pf) | ||||
{ | { | ||||
device_t dev = pf->dev; | device_t dev = pf->dev; | ||||
int rid, want, vectors, queues, available; | int rid, want, vectors, queues, available; | ||||
/* Override by tuneable */ | /* Override by tuneable */ | ||||
if (ixl_enable_msix == 0) | if (ixl_enable_msix == 0) | ||||
goto msi; | goto no_msix; | ||||
/* | /* | ||||
** When used in a virtualized environment | ** When used in a virtualized environment | ||||
** PCI BUSMASTER capability may not be set | ** PCI BUSMASTER capability may not be set | ||||
** so explicity set it here and rewrite | ** so explicity set it here and rewrite | ||||
** the ENABLE in the MSIX control register | ** the ENABLE in the MSIX control register | ||||
** at this point to cause the host to | ** at this point to cause the host to | ||||
** successfully initialize us. | ** successfully initialize us. | ||||
Show All 13 Lines | ixl_init_msix(struct ixl_pf *pf) | ||||
/* First try MSI/X */ | /* First try MSI/X */ | ||||
rid = PCIR_BAR(IXL_BAR); | rid = PCIR_BAR(IXL_BAR); | ||||
pf->msix_mem = bus_alloc_resource_any(dev, | pf->msix_mem = bus_alloc_resource_any(dev, | ||||
SYS_RES_MEMORY, &rid, RF_ACTIVE); | SYS_RES_MEMORY, &rid, RF_ACTIVE); | ||||
if (!pf->msix_mem) { | if (!pf->msix_mem) { | ||||
/* May not be enabled */ | /* May not be enabled */ | ||||
device_printf(pf->dev, | device_printf(pf->dev, | ||||
"Unable to map MSIX table \n"); | "Unable to map MSIX table\n"); | ||||
goto msi; | goto no_msix; | ||||
} | } | ||||
available = pci_msix_count(dev); | available = pci_msix_count(dev); | ||||
if (available == 0) { /* system has msix disabled */ | if (available == 0) { /* system has msix disabled */ | ||||
bus_release_resource(dev, SYS_RES_MEMORY, | bus_release_resource(dev, SYS_RES_MEMORY, | ||||
rid, pf->msix_mem); | rid, pf->msix_mem); | ||||
pf->msix_mem = NULL; | pf->msix_mem = NULL; | ||||
goto msi; | goto no_msix; | ||||
} | } | ||||
/* Figure out a reasonable auto config value */ | /* Figure out a reasonable auto config value */ | ||||
queues = (mp_ncpus > (available - 1)) ? (available - 1) : mp_ncpus; | queues = (mp_ncpus > (available - 1)) ? (available - 1) : mp_ncpus; | ||||
/* Override with hardcoded value if it's less than autoconfig count */ | /* Override with tunable value if tunable is less than autoconfig count */ | ||||
if ((ixl_max_queues != 0) && (ixl_max_queues <= queues)) | if ((ixl_max_queues != 0) && (ixl_max_queues <= queues)) | ||||
queues = ixl_max_queues; | queues = ixl_max_queues; | ||||
else if ((ixl_max_queues != 0) && (ixl_max_queues > queues)) | else if ((ixl_max_queues != 0) && (ixl_max_queues > queues)) | ||||
device_printf(dev, "ixl_max_queues > # of cpus, using " | device_printf(dev, "ixl_max_queues > # of cpus, using " | ||||
"autoconfig amount...\n"); | "autoconfig amount...\n"); | ||||
/* Or limit maximum auto-configured queues to 8 */ | /* Or limit maximum auto-configured queues to 8 */ | ||||
else if ((ixl_max_queues == 0) && (queues > 8)) | else if ((ixl_max_queues == 0) && (queues > 8)) | ||||
queues = 8; | queues = 8; | ||||
Show All 40 Lines | if (queues != rss_getnumbuckets()) { | ||||
device_printf(dev, | device_printf(dev, | ||||
"%s: queues (%d) != RSS buckets (%d)" | "%s: queues (%d) != RSS buckets (%d)" | ||||
"; performance will be impacted.\n", | "; performance will be impacted.\n", | ||||
__func__, queues, rss_getnumbuckets()); | __func__, queues, rss_getnumbuckets()); | ||||
} | } | ||||
#endif | #endif | ||||
return (vectors); | return (vectors); | ||||
} | } | ||||
msi: | no_msix: | ||||
vectors = pci_msi_count(dev); | vectors = pci_msi_count(dev); | ||||
pf->vsi.num_queues = 1; | pf->vsi.num_queues = 1; | ||||
pf->msix = 1; | |||||
ixl_max_queues = 1; | ixl_max_queues = 1; | ||||
ixl_enable_msix = 0; | ixl_enable_msix = 0; | ||||
if (vectors == 1 && pci_alloc_msi(dev, &vectors) == 0) | if (vectors == 1 && pci_alloc_msi(dev, &vectors) == 0) | ||||
device_printf(pf->dev,"Using an MSI interrupt\n"); | device_printf(pf->dev, "Using an MSI interrupt\n"); | ||||
else { | else { | ||||
pf->msix = 0; | vectors = 0; | ||||
device_printf(pf->dev,"Using a Legacy interrupt\n"); | device_printf(pf->dev, "Using a Legacy interrupt\n"); | ||||
} | } | ||||
return (vectors); | return (vectors); | ||||
} | } | ||||
/* | /* | ||||
* Plumb MSI/X vectors | * Configure admin queue/misc interrupt cause registers in hardware. | ||||
*/ | */ | ||||
static void | static void | ||||
ixl_configure_msix(struct ixl_pf *pf) | ixl_configure_intr0_msix(struct ixl_pf *pf) | ||||
{ | { | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
struct ixl_vsi *vsi = &pf->vsi; | |||||
u32 reg; | u32 reg; | ||||
u16 vector = 1; | |||||
/* First set up the adminq - vector 0 */ | /* First set up the adminq - vector 0 */ | ||||
wr32(hw, I40E_PFINT_ICR0_ENA, 0); /* disable all */ | wr32(hw, I40E_PFINT_ICR0_ENA, 0); /* disable all */ | ||||
rd32(hw, I40E_PFINT_ICR0); /* read to clear */ | rd32(hw, I40E_PFINT_ICR0); /* read to clear */ | ||||
reg = I40E_PFINT_ICR0_ENA_ECC_ERR_MASK | | reg = I40E_PFINT_ICR0_ENA_ECC_ERR_MASK | | ||||
I40E_PFINT_ICR0_ENA_GRST_MASK | | I40E_PFINT_ICR0_ENA_GRST_MASK | | ||||
I40E_PFINT_ICR0_HMC_ERR_MASK | | I40E_PFINT_ICR0_ENA_HMC_ERR_MASK | | ||||
I40E_PFINT_ICR0_ENA_ADMINQ_MASK | | I40E_PFINT_ICR0_ENA_ADMINQ_MASK | | ||||
I40E_PFINT_ICR0_ENA_MAL_DETECT_MASK | | I40E_PFINT_ICR0_ENA_MAL_DETECT_MASK | | ||||
I40E_PFINT_ICR0_ENA_VFLR_MASK | | I40E_PFINT_ICR0_ENA_VFLR_MASK | | ||||
I40E_PFINT_ICR0_ENA_PCI_EXCEPTION_MASK; | I40E_PFINT_ICR0_ENA_PCI_EXCEPTION_MASK; | ||||
wr32(hw, I40E_PFINT_ICR0_ENA, reg); | wr32(hw, I40E_PFINT_ICR0_ENA, reg); | ||||
/* | |||||
* 0x7FF is the end of the queue list. | |||||
* This means we won't use MSI-X vector 0 for a queue interrupt | |||||
* in MSIX mode. | |||||
*/ | |||||
wr32(hw, I40E_PFINT_LNKLST0, 0x7FF); | wr32(hw, I40E_PFINT_LNKLST0, 0x7FF); | ||||
wr32(hw, I40E_PFINT_ITR0(IXL_RX_ITR), 0x003E); | /* Value is in 2 usec units, so 0x3E is 62*2 = 124 usecs. */ | ||||
wr32(hw, I40E_PFINT_ITR0(IXL_RX_ITR), 0x3E); | |||||
wr32(hw, I40E_PFINT_DYN_CTL0, | wr32(hw, I40E_PFINT_DYN_CTL0, | ||||
I40E_PFINT_DYN_CTL0_SW_ITR_INDX_MASK | | I40E_PFINT_DYN_CTL0_SW_ITR_INDX_MASK | | ||||
I40E_PFINT_DYN_CTL0_INTENA_MSK_MASK); | I40E_PFINT_DYN_CTL0_INTENA_MSK_MASK); | ||||
wr32(hw, I40E_PFINT_STAT_CTL0, 0); | wr32(hw, I40E_PFINT_STAT_CTL0, 0); | ||||
} | |||||
/* Next configure the queues */ | /* | ||||
* Configure queue interrupt cause registers in hardware. | |||||
*/ | |||||
static void | |||||
ixl_configure_queue_intr_msix(struct ixl_pf *pf) | |||||
{ | |||||
struct i40e_hw *hw = &pf->hw; | |||||
struct ixl_vsi *vsi = &pf->vsi; | |||||
u32 reg; | |||||
u16 vector = 1; | |||||
for (int i = 0; i < vsi->num_queues; i++, vector++) { | for (int i = 0; i < vsi->num_queues; i++, vector++) { | ||||
wr32(hw, I40E_PFINT_DYN_CTLN(i), 0); | wr32(hw, I40E_PFINT_DYN_CTLN(i), 0); | ||||
/* First queue type is RX / type 0 */ | /* First queue type is RX / 0 */ | ||||
wr32(hw, I40E_PFINT_LNKLSTN(i), i); | wr32(hw, I40E_PFINT_LNKLSTN(i), i); | ||||
reg = I40E_QINT_RQCTL_CAUSE_ENA_MASK | | reg = I40E_QINT_RQCTL_CAUSE_ENA_MASK | | ||||
(IXL_RX_ITR << I40E_QINT_RQCTL_ITR_INDX_SHIFT) | | (IXL_RX_ITR << I40E_QINT_RQCTL_ITR_INDX_SHIFT) | | ||||
(vector << I40E_QINT_RQCTL_MSIX_INDX_SHIFT) | | (vector << I40E_QINT_RQCTL_MSIX_INDX_SHIFT) | | ||||
(i << I40E_QINT_RQCTL_NEXTQ_INDX_SHIFT) | | (i << I40E_QINT_RQCTL_NEXTQ_INDX_SHIFT) | | ||||
(I40E_QUEUE_TYPE_TX << I40E_QINT_RQCTL_NEXTQ_TYPE_SHIFT); | (I40E_QUEUE_TYPE_TX << I40E_QINT_RQCTL_NEXTQ_TYPE_SHIFT); | ||||
wr32(hw, I40E_QINT_RQCTL(i), reg); | wr32(hw, I40E_QINT_RQCTL(i), reg); | ||||
Show All 11 Lines | |||||
* Configure for MSI single vector operation | * Configure for MSI single vector operation | ||||
*/ | */ | ||||
static void | static void | ||||
ixl_configure_legacy(struct ixl_pf *pf) | ixl_configure_legacy(struct ixl_pf *pf) | ||||
{ | { | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
u32 reg; | u32 reg; | ||||
wr32(hw, I40E_PFINT_ITR0(0), 0); | wr32(hw, I40E_PFINT_ITR0(0), 0); | ||||
wr32(hw, I40E_PFINT_ITR0(1), 0); | wr32(hw, I40E_PFINT_ITR0(1), 0); | ||||
/* Setup "other" causes */ | /* Setup "other" causes */ | ||||
reg = I40E_PFINT_ICR0_ENA_ECC_ERR_MASK | reg = I40E_PFINT_ICR0_ENA_ECC_ERR_MASK | ||||
| I40E_PFINT_ICR0_ENA_MAL_DETECT_MASK | | I40E_PFINT_ICR0_ENA_MAL_DETECT_MASK | ||||
| I40E_PFINT_ICR0_ENA_GRST_MASK | | I40E_PFINT_ICR0_ENA_GRST_MASK | ||||
| I40E_PFINT_ICR0_ENA_PCI_EXCEPTION_MASK | | I40E_PFINT_ICR0_ENA_PCI_EXCEPTION_MASK | ||||
| I40E_PFINT_ICR0_ENA_GPIO_MASK | | I40E_PFINT_ICR0_ENA_GPIO_MASK | ||||
| I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_MASK | | I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_MASK | ||||
| I40E_PFINT_ICR0_ENA_HMC_ERR_MASK | | I40E_PFINT_ICR0_ENA_HMC_ERR_MASK | ||||
Show All 18 Lines | reg = I40E_QINT_RQCTL_CAUSE_ENA_MASK | ||||
| (IXL_RX_ITR << I40E_QINT_RQCTL_ITR_INDX_SHIFT) | | (IXL_RX_ITR << I40E_QINT_RQCTL_ITR_INDX_SHIFT) | ||||
| (I40E_QUEUE_TYPE_TX << I40E_QINT_TQCTL_NEXTQ_TYPE_SHIFT); | | (I40E_QUEUE_TYPE_TX << I40E_QINT_TQCTL_NEXTQ_TYPE_SHIFT); | ||||
wr32(hw, I40E_QINT_RQCTL(0), reg); | wr32(hw, I40E_QINT_RQCTL(0), reg); | ||||
reg = I40E_QINT_TQCTL_CAUSE_ENA_MASK | reg = I40E_QINT_TQCTL_CAUSE_ENA_MASK | ||||
| (IXL_TX_ITR << I40E_QINT_TQCTL_ITR_INDX_SHIFT) | | (IXL_TX_ITR << I40E_QINT_TQCTL_ITR_INDX_SHIFT) | ||||
| (IXL_QUEUE_EOL << I40E_QINT_TQCTL_NEXTQ_INDX_SHIFT); | | (IXL_QUEUE_EOL << I40E_QINT_TQCTL_NEXTQ_INDX_SHIFT); | ||||
wr32(hw, I40E_QINT_TQCTL(0), reg); | wr32(hw, I40E_QINT_TQCTL(0), reg); | ||||
/* Next enable the queue pair */ | |||||
reg = rd32(hw, I40E_QTX_ENA(0)); | |||||
reg |= I40E_QTX_ENA_QENA_REQ_MASK; | |||||
wr32(hw, I40E_QTX_ENA(0), reg); | |||||
reg = rd32(hw, I40E_QRX_ENA(0)); | |||||
reg |= I40E_QRX_ENA_QENA_REQ_MASK; | |||||
wr32(hw, I40E_QRX_ENA(0), reg); | |||||
} | } | ||||
/* | /* | ||||
* Set the Initial ITR state | * Get initial ITR values from tunable values. | ||||
*/ | */ | ||||
static void | static void | ||||
ixl_configure_itr(struct ixl_pf *pf) | ixl_configure_itr(struct ixl_pf *pf) | ||||
{ | { | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
struct ixl_vsi *vsi = &pf->vsi; | struct ixl_vsi *vsi = &pf->vsi; | ||||
struct ixl_queue *que = vsi->queues; | struct ixl_queue *que = vsi->queues; | ||||
vsi->rx_itr_setting = ixl_rx_itr; | vsi->rx_itr_setting = ixl_rx_itr; | ||||
if (ixl_dynamic_rx_itr) | |||||
vsi->rx_itr_setting |= IXL_ITR_DYNAMIC; | |||||
vsi->tx_itr_setting = ixl_tx_itr; | vsi->tx_itr_setting = ixl_tx_itr; | ||||
if (ixl_dynamic_tx_itr) | |||||
vsi->tx_itr_setting |= IXL_ITR_DYNAMIC; | |||||
for (int i = 0; i < vsi->num_queues; i++, que++) { | for (int i = 0; i < vsi->num_queues; i++, que++) { | ||||
struct tx_ring *txr = &que->txr; | struct tx_ring *txr = &que->txr; | ||||
struct rx_ring *rxr = &que->rxr; | struct rx_ring *rxr = &que->rxr; | ||||
wr32(hw, I40E_PFINT_ITRN(IXL_RX_ITR, i), | wr32(hw, I40E_PFINT_ITRN(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_PFINT_ITRN(IXL_TX_ITR, i), | wr32(hw, I40E_PFINT_ITRN(IXL_TX_ITR, i), | ||||
vsi->tx_itr_setting); | vsi->tx_itr_setting); | ||||
txr->itr = vsi->tx_itr_setting; | txr->itr = vsi->tx_itr_setting; | ||||
txr->latency = IXL_AVE_LATENCY; | txr->latency = IXL_AVE_LATENCY; | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
ixl_allocate_pci_resources(struct ixl_pf *pf) | ixl_allocate_pci_resources(struct ixl_pf *pf) | ||||
{ | { | ||||
int rid; | int rid; | ||||
device_t dev = pf->dev; | device_t dev = pf->dev; | ||||
rid = PCIR_BAR(0); | rid = PCIR_BAR(0); | ||||
pf->pci_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, | pf->pci_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, | ||||
&rid, RF_ACTIVE); | &rid, RF_ACTIVE); | ||||
if (!(pf->pci_mem)) { | if (!(pf->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); | ||||
} | } | ||||
pf->osdep.mem_bus_space_tag = | pf->osdep.mem_bus_space_tag = | ||||
rman_get_bustag(pf->pci_mem); | rman_get_bustag(pf->pci_mem); | ||||
pf->osdep.mem_bus_space_handle = | pf->osdep.mem_bus_space_handle = | ||||
rman_get_bushandle(pf->pci_mem); | rman_get_bushandle(pf->pci_mem); | ||||
pf->osdep.mem_bus_space_size = rman_get_size(pf->pci_mem); | pf->osdep.mem_bus_space_size = rman_get_size(pf->pci_mem); | ||||
pf->osdep.flush_reg = I40E_GLGEN_STAT; | pf->osdep.flush_reg = I40E_GLGEN_STAT; | ||||
pf->hw.hw_addr = (u8 *) &pf->osdep.mem_bus_space_handle; | pf->hw.hw_addr = (u8 *) &pf->osdep.mem_bus_space_handle; | ||||
pf->hw.back = &pf->osdep; | pf->hw.back = &pf->osdep; | ||||
/* | /* | ||||
** Now setup MSI or MSI/X, should | ** Now setup MSI or MSI/X, should | ||||
** return us the number of supported | ** return us the number of supported | ||||
** vectors. (Will be 1 for MSI) | ** vectors. (Will be 1 for MSI) | ||||
*/ | */ | ||||
pf->msix = ixl_init_msix(pf); | pf->msix = ixl_init_msix(pf); | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | /* | ||||
ixl_free_pci_resources(struct ixl_pf * pf) | * Teardown and release the admin queue/misc vector | ||||
* interrupt. | |||||
*/ | |||||
static int | |||||
ixl_teardown_adminq_msix(struct ixl_pf *pf) | |||||
{ | { | ||||
struct ixl_vsi *vsi = &pf->vsi; | |||||
struct ixl_queue *que = vsi->queues; | |||||
device_t dev = pf->dev; | device_t dev = pf->dev; | ||||
int rid, memrid; | int rid; | ||||
memrid = PCIR_BAR(IXL_BAR); | if (pf->admvec) /* we are doing MSIX */ | ||||
rid = pf->admvec + 1; | |||||
else | |||||
(pf->msix != 0) ? (rid = 1):(rid = 0); | |||||
// TODO: Check for errors from bus_teardown_intr | |||||
// TODO: Check for errors from bus_release_resource | |||||
if (pf->tag != NULL) { | |||||
bus_teardown_intr(dev, pf->res, pf->tag); | |||||
pf->tag = NULL; | |||||
} | |||||
if (pf->res != NULL) { | |||||
bus_release_resource(dev, SYS_RES_IRQ, rid, pf->res); | |||||
pf->res = NULL; | |||||
} | |||||
return (0); | |||||
} | |||||
static int | |||||
ixl_teardown_queue_msix(struct ixl_vsi *vsi) | |||||
{ | |||||
struct ixl_queue *que = vsi->queues; | |||||
device_t dev = vsi->dev; | |||||
int rid, error = 0; | |||||
/* We may get here before stations are setup */ | /* We may get here before stations are setup */ | ||||
if ((!ixl_enable_msix) || (que == NULL)) | if ((!ixl_enable_msix) || (que == NULL)) | ||||
goto early; | return (0); | ||||
/* | /* Release all MSIX queue resources */ | ||||
** Release all msix VSI resources: | |||||
*/ | |||||
for (int i = 0; i < vsi->num_queues; i++, que++) { | for (int i = 0; i < vsi->num_queues; i++, que++) { | ||||
rid = que->msix + 1; | rid = que->msix + 1; | ||||
if (que->tag != NULL) { | if (que->tag != NULL) { | ||||
bus_teardown_intr(dev, que->res, que->tag); | error = bus_teardown_intr(dev, que->res, que->tag); | ||||
if (error) { | |||||
device_printf(dev, "bus_teardown_intr() for" | |||||
" Queue %d interrupt failed\n", | |||||
que->me); | |||||
// return (ENXIO); | |||||
} | |||||
que->tag = NULL; | que->tag = NULL; | ||||
} | } | ||||
if (que->res != NULL) | if (que->res != NULL) { | ||||
bus_release_resource(dev, SYS_RES_IRQ, rid, que->res); | error = bus_release_resource(dev, SYS_RES_IRQ, rid, que->res); | ||||
if (error) { | |||||
device_printf(dev, "bus_release_resource() for" | |||||
" Queue %d interrupt failed [rid=%d]\n", | |||||
que->me, rid); | |||||
// return (ENXIO); | |||||
} | } | ||||
que->res = NULL; | |||||
} | |||||
} | |||||
early: | return (0); | ||||
/* Clean the AdminQ interrupt last */ | |||||
if (pf->admvec) /* we are doing MSIX */ | |||||
rid = pf->admvec + 1; | |||||
else | |||||
(pf->msix != 0) ? (rid = 1):(rid = 0); | |||||
if (pf->tag != NULL) { | |||||
bus_teardown_intr(dev, pf->res, pf->tag); | |||||
pf->tag = NULL; | |||||
} | } | ||||
if (pf->res != NULL) | |||||
bus_release_resource(dev, SYS_RES_IRQ, rid, pf->res); | |||||
static void | |||||
ixl_free_pci_resources(struct ixl_pf *pf) | |||||
{ | |||||
device_t dev = pf->dev; | |||||
int memrid; | |||||
ixl_teardown_queue_msix(&pf->vsi); | |||||
ixl_teardown_adminq_msix(pf); | |||||
if (pf->msix) | if (pf->msix) | ||||
pci_release_msi(dev); | pci_release_msi(dev); | ||||
memrid = PCIR_BAR(IXL_BAR); | |||||
if (pf->msix_mem != NULL) | if (pf->msix_mem != NULL) | ||||
bus_release_resource(dev, SYS_RES_MEMORY, | bus_release_resource(dev, SYS_RES_MEMORY, | ||||
memrid, pf->msix_mem); | memrid, pf->msix_mem); | ||||
if (pf->pci_mem != NULL) | if (pf->pci_mem != NULL) | ||||
bus_release_resource(dev, SYS_RES_MEMORY, | bus_release_resource(dev, SYS_RES_MEMORY, | ||||
PCIR_BAR(0), pf->pci_mem); | PCIR_BAR(0), pf->pci_mem); | ||||
▲ Show 20 Lines • Show All 184 Lines • ▼ Show 20 Lines | #endif | ||||
ifmedia_set(&vsi->media, IFM_ETHER | IFM_AUTO); | ifmedia_set(&vsi->media, IFM_ETHER | IFM_AUTO); | ||||
ether_ifattach(ifp, hw->mac.addr); | ether_ifattach(ifp, hw->mac.addr); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
** Run when the Admin Queue gets a | ** Run when the Admin Queue gets a link state change interrupt. | ||||
** link transition interrupt. | |||||
*/ | */ | ||||
static void | static void | ||||
ixl_link_event(struct ixl_pf *pf, struct i40e_arq_event_info *e) | ixl_link_event(struct ixl_pf *pf, struct i40e_arq_event_info *e) | ||||
{ | { | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
device_t dev = pf->dev; | |||||
struct i40e_aqc_get_link_status *status = | struct i40e_aqc_get_link_status *status = | ||||
(struct i40e_aqc_get_link_status *)&e->desc.params.raw; | (struct i40e_aqc_get_link_status *)&e->desc.params.raw; | ||||
bool check; | |||||
/* Request link status from adapter */ | |||||
hw->phy.get_link_info = TRUE; | hw->phy.get_link_info = TRUE; | ||||
i40e_get_link_status(hw, &check); | i40e_get_link_status(hw, &pf->link_up); | ||||
pf->link_up = check; | |||||
#ifdef IXL_DEBUG | /* Print out message if an unqualified module is found */ | ||||
printf("Link is %s\n", check ? "up":"down"); | |||||
#endif | |||||
/* Report if Unqualified modules are found */ | |||||
if ((status->link_info & I40E_AQ_MEDIA_AVAILABLE) && | if ((status->link_info & I40E_AQ_MEDIA_AVAILABLE) && | ||||
(!(status->an_info & I40E_AQ_QUALIFIED_MODULE)) && | (!(status->an_info & I40E_AQ_QUALIFIED_MODULE)) && | ||||
(!(status->link_info & I40E_AQ_LINK_UP))) | (!(status->link_info & I40E_AQ_LINK_UP))) | ||||
device_printf(pf->dev, "Link failed because " | device_printf(dev, "Link failed because " | ||||
"an unqualified module was detected\n"); | "an unqualified module was detected!\n"); | ||||
return; | /* Update OS link info */ | ||||
ixl_update_link_status(pf); | |||||
} | } | ||||
/********************************************************************* | /********************************************************************* | ||||
* | * | ||||
* Get Firmware Switch configuration | * Get Firmware Switch configuration | ||||
* - this will need to be more robust when more complex | * - this will need to be more robust when more complex | ||||
* switch configurations are enabled. | * switch configurations are enabled. | ||||
* | * | ||||
Show All 9 Lines | ixl_switch_config(struct ixl_pf *pf) | ||||
int ret; | int ret; | ||||
u16 next = 0; | u16 next = 0; | ||||
memset(&aq_buf, 0, sizeof(aq_buf)); | memset(&aq_buf, 0, sizeof(aq_buf)); | ||||
sw_config = (struct i40e_aqc_get_switch_config_resp *)aq_buf; | sw_config = (struct i40e_aqc_get_switch_config_resp *)aq_buf; | ||||
ret = i40e_aq_get_switch_config(hw, sw_config, | ret = i40e_aq_get_switch_config(hw, sw_config, | ||||
sizeof(aq_buf), &next, NULL); | sizeof(aq_buf), &next, NULL); | ||||
if (ret) { | if (ret) { | ||||
device_printf(dev,"aq_get_switch_config failed (ret=%d)!!\n", | device_printf(dev, "aq_get_switch_config() failed, error %d," | ||||
ret); | " aq_error %d\n", ret, pf->hw.aq.asq_last_status); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
#ifdef IXL_DEBUG | #ifdef IXL_DEBUG | ||||
device_printf(dev, | device_printf(dev, | ||||
"Switch config: header reported: %d in structure, %d total\n", | "Switch config: header reported: %d in structure, %d total\n", | ||||
sw_config->header.num_reported, sw_config->header.num_total); | sw_config->header.num_reported, sw_config->header.num_total); | ||||
for (int i = 0; i < sw_config->header.num_reported; i++) { | for (int i = 0; i < sw_config->header.num_reported; i++) { | ||||
device_printf(dev, | device_printf(dev, | ||||
Show All 30 Lines | ixl_initialize_vsi(struct ixl_vsi *vsi) | ||||
memset(&ctxt, 0, sizeof(ctxt)); | memset(&ctxt, 0, sizeof(ctxt)); | ||||
ctxt.seid = vsi->seid; | ctxt.seid = vsi->seid; | ||||
if (pf->veb_seid != 0) | if (pf->veb_seid != 0) | ||||
ctxt.uplink_seid = pf->veb_seid; | ctxt.uplink_seid = pf->veb_seid; | ||||
ctxt.pf_num = hw->pf_id; | ctxt.pf_num = hw->pf_id; | ||||
err = i40e_aq_get_vsi_params(hw, &ctxt, NULL); | err = i40e_aq_get_vsi_params(hw, &ctxt, NULL); | ||||
if (err) { | if (err) { | ||||
device_printf(dev,"get vsi params failed %x!!\n", err); | device_printf(dev, "i40e_aq_get_vsi_params() failed, error %d" | ||||
" aq_error %d\n", err, hw->aq.asq_last_status); | |||||
return (err); | return (err); | ||||
} | } | ||||
#ifdef IXL_DEBUG | #ifdef IXL_DEBUG | ||||
printf("get_vsi_params: seid: %d, uplinkseid: %d, vsi_number: %d, " | device_printf(dev, "get_vsi_params: seid: %d, uplinkseid: %d, vsi_number: %d, " | ||||
"vsis_allocated: %d, vsis_unallocated: %d, flags: 0x%x, " | "vsis_allocated: %d, vsis_unallocated: %d, flags: 0x%x, " | ||||
"pfnum: %d, vfnum: %d, stat idx: %d, enabled: %d\n", ctxt.seid, | "pfnum: %d, vfnum: %d, stat idx: %d, enabled: %d\n", ctxt.seid, | ||||
ctxt.uplink_seid, ctxt.vsi_number, | ctxt.uplink_seid, ctxt.vsi_number, | ||||
ctxt.vsis_allocated, ctxt.vsis_unallocated, | ctxt.vsis_allocated, ctxt.vsis_unallocated, | ||||
ctxt.flags, ctxt.pf_num, ctxt.vf_num, | ctxt.flags, ctxt.pf_num, ctxt.vf_num, | ||||
ctxt.info.stat_counter_idx, ctxt.info.up_enable_bits); | ctxt.info.stat_counter_idx, ctxt.info.up_enable_bits); | ||||
#endif | #endif | ||||
/* | /* | ||||
** Set the queue and traffic class bits | ** Set the queue and traffic class bits | ||||
** - when multiple traffic classes are supported | ** - when multiple traffic classes are supported | ||||
** this will need to be more robust. | ** this will need to be more robust. | ||||
*/ | */ | ||||
ctxt.info.valid_sections = I40E_AQ_VSI_PROP_QUEUE_MAP_VALID; | ctxt.info.valid_sections = I40E_AQ_VSI_PROP_QUEUE_MAP_VALID; | ||||
ctxt.info.mapping_flags |= I40E_AQ_VSI_QUE_MAP_CONTIG; | ctxt.info.mapping_flags |= I40E_AQ_VSI_QUE_MAP_CONTIG; | ||||
/* In contig mode, que_mapping[0] is first queue index used by this VSI */ | |||||
ctxt.info.queue_mapping[0] = 0; | ctxt.info.queue_mapping[0] = 0; | ||||
/* | /* This VSI is assigned 64 queues (we may not use all of them) */ | ||||
* This VSI will only use traffic class 0; start traffic class 0's | ctxt.info.tc_mapping[0] = 0x0c00; | ||||
* queue allocation at queue 0, and assign it 64 (2^6) queues (though | |||||
* the driver may not use all of them). | |||||
*/ | |||||
ctxt.info.tc_mapping[0] = ((0 << I40E_AQ_VSI_TC_QUE_OFFSET_SHIFT) | |||||
& I40E_AQ_VSI_TC_QUE_OFFSET_MASK) | | |||||
((6 << I40E_AQ_VSI_TC_QUE_NUMBER_SHIFT) | |||||
& I40E_AQ_VSI_TC_QUE_NUMBER_MASK); | |||||
/* Set VLAN receive stripping mode */ | /* Set VLAN receive stripping mode */ | ||||
ctxt.info.valid_sections |= I40E_AQ_VSI_PROP_VLAN_VALID; | ctxt.info.valid_sections |= I40E_AQ_VSI_PROP_VLAN_VALID; | ||||
ctxt.info.port_vlan_flags = I40E_AQ_VSI_PVLAN_MODE_ALL; | ctxt.info.port_vlan_flags = I40E_AQ_VSI_PVLAN_MODE_ALL; | ||||
if (vsi->ifp->if_capenable & IFCAP_VLAN_HWTAGGING) | if (vsi->ifp->if_capenable & IFCAP_VLAN_HWTAGGING) | ||||
ctxt.info.port_vlan_flags |= I40E_AQ_VSI_PVLAN_EMOD_STR_BOTH; | ctxt.info.port_vlan_flags |= I40E_AQ_VSI_PVLAN_EMOD_STR_BOTH; | ||||
else | else | ||||
ctxt.info.port_vlan_flags |= I40E_AQ_VSI_PVLAN_EMOD_NOTHING; | ctxt.info.port_vlan_flags |= I40E_AQ_VSI_PVLAN_EMOD_NOTHING; | ||||
/* Keep copy of VSI info in VSI for statistic counters */ | /* Keep copy of VSI info in VSI for statistic counters */ | ||||
memcpy(&vsi->info, &ctxt.info, sizeof(ctxt.info)); | memcpy(&vsi->info, &ctxt.info, sizeof(ctxt.info)); | ||||
/* Reset VSI statistics */ | /* Reset VSI statistics */ | ||||
ixl_vsi_reset_stats(vsi); | ixl_vsi_reset_stats(vsi); | ||||
vsi->hw_filters_add = 0; | vsi->hw_filters_add = 0; | ||||
vsi->hw_filters_del = 0; | vsi->hw_filters_del = 0; | ||||
ctxt.flags = htole16(I40E_AQ_VSI_TYPE_PF); | ctxt.flags = htole16(I40E_AQ_VSI_TYPE_PF); | ||||
err = i40e_aq_update_vsi_params(hw, &ctxt, NULL); | err = i40e_aq_update_vsi_params(hw, &ctxt, NULL); | ||||
if (err) { | if (err) { | ||||
device_printf(dev,"update vsi params failed %x!!\n", | device_printf(dev, "i40e_aq_update_vsi_params() failed, error %d, aq_error %d\n", | ||||
hw->aq.asq_last_status); | err, hw->aq.asq_last_status); | ||||
return (err); | return (err); | ||||
} | } | ||||
for (int i = 0; i < vsi->num_queues; i++, que++) { | for (int i = 0; i < vsi->num_queues; i++, que++) { | ||||
struct tx_ring *txr = &que->txr; | struct tx_ring *txr = &que->txr; | ||||
struct rx_ring *rxr = &que->rxr; | struct rx_ring *rxr = &que->rxr; | ||||
struct i40e_hmc_obj_txq tctx; | struct i40e_hmc_obj_txq tctx; | ||||
struct i40e_hmc_obj_rxq rctx; | struct i40e_hmc_obj_rxq rctx; | ||||
u32 txctl; | u32 txctl; | ||||
u16 size; | u16 size; | ||||
/* Setup the HMC TX Context */ | /* Setup the HMC TX Context */ | ||||
size = que->num_desc * sizeof(struct i40e_tx_desc); | size = que->num_desc * sizeof(struct i40e_tx_desc); | ||||
memset(&tctx, 0, sizeof(struct i40e_hmc_obj_txq)); | memset(&tctx, 0, sizeof(struct i40e_hmc_obj_txq)); | ||||
tctx.new_context = 1; | tctx.new_context = 1; | ||||
tctx.base = (txr->dma.pa/IXL_TX_CTX_BASE_UNITS); | tctx.base = (txr->dma.pa/IXL_TX_CTX_BASE_UNITS); | ||||
tctx.qlen = que->num_desc; | tctx.qlen = que->num_desc; | ||||
tctx.fc_ena = 0; | tctx.fc_ena = 0; | ||||
tctx.rdylist = vsi->info.qs_handle[0]; /* index is TC */ | tctx.rdylist = vsi->info.qs_handle[0]; /* index is TC */ | ||||
▲ Show 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | |||||
**********************************************************************/ | **********************************************************************/ | ||||
void | void | ||||
ixl_free_vsi(struct ixl_vsi *vsi) | ixl_free_vsi(struct ixl_vsi *vsi) | ||||
{ | { | ||||
struct ixl_pf *pf = (struct ixl_pf *)vsi->back; | struct ixl_pf *pf = (struct ixl_pf *)vsi->back; | ||||
struct ixl_queue *que = vsi->queues; | struct ixl_queue *que = vsi->queues; | ||||
/* Free station queues */ | /* Free station queues */ | ||||
if (!vsi->queues) | |||||
goto free_filters; | |||||
for (int i = 0; i < vsi->num_queues; i++, que++) { | for (int i = 0; i < vsi->num_queues; i++, que++) { | ||||
struct tx_ring *txr = &que->txr; | struct tx_ring *txr = &que->txr; | ||||
struct rx_ring *rxr = &que->rxr; | struct rx_ring *rxr = &que->rxr; | ||||
if (!mtx_initialized(&txr->mtx)) /* uninitialized */ | if (!mtx_initialized(&txr->mtx)) /* uninitialized */ | ||||
continue; | continue; | ||||
IXL_TX_LOCK(txr); | IXL_TX_LOCK(txr); | ||||
ixl_free_que_tx(que); | ixl_free_que_tx(que); | ||||
Show All 9 Lines | for (int i = 0; i < vsi->num_queues; i++, que++) { | ||||
if (rxr->base) | if (rxr->base) | ||||
i40e_free_dma_mem(&pf->hw, &rxr->dma); | i40e_free_dma_mem(&pf->hw, &rxr->dma); | ||||
IXL_RX_UNLOCK(rxr); | IXL_RX_UNLOCK(rxr); | ||||
IXL_RX_LOCK_DESTROY(rxr); | IXL_RX_LOCK_DESTROY(rxr); | ||||
} | } | ||||
free(vsi->queues, M_DEVBUF); | free(vsi->queues, M_DEVBUF); | ||||
free_filters: | |||||
/* Free VSI filter list */ | /* Free VSI filter list */ | ||||
ixl_free_mac_filters(vsi); | ixl_free_mac_filters(vsi); | ||||
} | } | ||||
static void | static void | ||||
ixl_free_mac_filters(struct ixl_vsi *vsi) | ixl_free_mac_filters(struct ixl_vsi *vsi) | ||||
{ | { | ||||
struct ixl_mac_filter *f; | struct ixl_mac_filter *f; | ||||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | bzero((void *)txr->base, tsize); | ||||
if (ixl_allocate_tx_data(que)) { | if (ixl_allocate_tx_data(que)) { | ||||
device_printf(dev, | device_printf(dev, | ||||
"Critical Failure setting up TX structures\n"); | "Critical Failure setting up TX structures\n"); | ||||
error = ENOMEM; | error = ENOMEM; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
/* Allocate a buf ring */ | /* Allocate a buf ring */ | ||||
txr->br = buf_ring_alloc(4096, M_DEVBUF, | txr->br = buf_ring_alloc(4096, M_DEVBUF, | ||||
M_WAITOK, &txr->mtx); | M_NOWAIT, &txr->mtx); | ||||
if (txr->br == NULL) { | if (txr->br == NULL) { | ||||
device_printf(dev, | device_printf(dev, | ||||
"Critical Failure setting up TX buf ring\n"); | "Critical Failure setting up TX buf ring\n"); | ||||
error = ENOMEM; | error = ENOMEM; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
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; | ||||
/* Idle, do nothing */ | /* Idle, do nothing */ | ||||
if (rxr->bytes == 0) | if (rxr->bytes == 0) | ||||
return; | return; | ||||
if (ixl_dynamic_rx_itr) { | if (ixl_dynamic_rx_itr) { | ||||
rx_bytes = rxr->bytes/rxr->itr; | rx_bytes = rxr->bytes/rxr->itr; | ||||
rx_itr = rxr->itr; | rx_itr = rxr->itr; | ||||
▲ Show 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | ixl_add_vsi_sysctls(struct ixl_pf *pf, struct ixl_vsi *vsi, | ||||
child = SYSCTL_CHILDREN(tree); | child = SYSCTL_CHILDREN(tree); | ||||
vsi->vsi_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, sysctl_name, | vsi->vsi_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, sysctl_name, | ||||
CTLFLAG_RD, NULL, "VSI Number"); | CTLFLAG_RD, NULL, "VSI Number"); | ||||
vsi_list = SYSCTL_CHILDREN(vsi->vsi_node); | vsi_list = SYSCTL_CHILDREN(vsi->vsi_node); | ||||
ixl_add_sysctls_eth_stats(ctx, vsi_list, &vsi->eth_stats); | ixl_add_sysctls_eth_stats(ctx, vsi_list, &vsi->eth_stats); | ||||
} | } | ||||
#ifdef IXL_DEBUG | |||||
/** | |||||
* ixl_sysctl_qtx_tail_handler | |||||
* Retrieves I40E_QTX_TAIL value from hardware | |||||
* for a sysctl. | |||||
*/ | |||||
static int | |||||
ixl_sysctl_qtx_tail_handler(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct ixl_queue *que; | |||||
int error; | |||||
u32 val; | |||||
que = ((struct ixl_queue *)oidp->oid_arg1); | |||||
if (!que) return 0; | |||||
val = rd32(que->vsi->hw, que->txr.tail); | |||||
error = sysctl_handle_int(oidp, &val, 0, req); | |||||
if (error || !req->newptr) | |||||
return error; | |||||
return (0); | |||||
} | |||||
/** | |||||
* ixl_sysctl_qrx_tail_handler | |||||
* Retrieves I40E_QRX_TAIL value from hardware | |||||
* for a sysctl. | |||||
*/ | |||||
static int | |||||
ixl_sysctl_qrx_tail_handler(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct ixl_queue *que; | |||||
int error; | |||||
u32 val; | |||||
que = ((struct ixl_queue *)oidp->oid_arg1); | |||||
if (!que) return 0; | |||||
val = rd32(que->vsi->hw, que->rxr.tail); | |||||
error = sysctl_handle_int(oidp, &val, 0, req); | |||||
if (error || !req->newptr) | |||||
return error; | |||||
return (0); | |||||
} | |||||
#endif | |||||
static void | static void | ||||
ixl_add_hw_stats(struct ixl_pf *pf) | ixl_add_hw_stats(struct ixl_pf *pf) | ||||
{ | { | ||||
device_t dev = pf->dev; | device_t dev = pf->dev; | ||||
struct ixl_vsi *vsi = &pf->vsi; | struct ixl_vsi *vsi = &pf->vsi; | ||||
struct ixl_queue *queues = vsi->queues; | struct ixl_queue *queues = vsi->queues; | ||||
struct i40e_hw_port_stats *pf_stats = &pf->stats; | struct i40e_hw_port_stats *pf_stats = &pf->stats; | ||||
Show All 28 Lines | for (int q = 0; q < vsi->num_queues; q++) { | ||||
queue_list = SYSCTL_CHILDREN(queue_node); | queue_list = SYSCTL_CHILDREN(queue_node); | ||||
txr = &(queues[q].txr); | txr = &(queues[q].txr); | ||||
rxr = &(queues[q].rxr); | rxr = &(queues[q].rxr); | ||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "mbuf_defrag_failed", | SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "mbuf_defrag_failed", | ||||
CTLFLAG_RD, &(queues[q].mbuf_defrag_failed), | CTLFLAG_RD, &(queues[q].mbuf_defrag_failed), | ||||
"m_defrag() failed"); | "m_defrag() failed"); | ||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "dropped", | |||||
CTLFLAG_RD, &(queues[q].dropped_pkts), | |||||
"Driver dropped packets"); | |||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "irqs", | SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "irqs", | ||||
CTLFLAG_RD, &(queues[q].irqs), | CTLFLAG_RD, &(queues[q].irqs), | ||||
"irqs on this queue"); | "irqs on this queue"); | ||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "tso_tx", | SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "tso_tx", | ||||
CTLFLAG_RD, &(queues[q].tso), | CTLFLAG_RD, &(queues[q].tso), | ||||
"TSO"); | "TSO"); | ||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "tx_dma_setup", | SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "tx_dma_setup", | ||||
CTLFLAG_RD, &(queues[q].tx_dma_setup), | CTLFLAG_RD, &(queues[q].tx_dma_setup), | ||||
"Driver tx dma failure in xmit"); | "Driver tx dma failure in xmit"); | ||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "no_desc_avail", | SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "no_desc_avail", | ||||
CTLFLAG_RD, &(txr->no_desc), | CTLFLAG_RD, &(txr->no_desc), | ||||
"Queue No Descriptor Available"); | "Queue No Descriptor Available"); | ||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "tx_packets", | SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "tx_packets", | ||||
CTLFLAG_RD, &(txr->total_packets), | CTLFLAG_RD, &(txr->total_packets), | ||||
"Queue Packets Transmitted"); | "Queue Packets Transmitted"); | ||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "tx_bytes", | SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "tx_bytes", | ||||
CTLFLAG_RD, &(txr->tx_bytes), | CTLFLAG_RD, &(txr->tx_bytes), | ||||
"Queue Bytes Transmitted"); | "Queue Bytes Transmitted"); | ||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "rx_packets", | SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "rx_packets", | ||||
CTLFLAG_RD, &(rxr->rx_packets), | CTLFLAG_RD, &(rxr->rx_packets), | ||||
"Queue Packets Received"); | "Queue Packets Received"); | ||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "rx_bytes", | SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "rx_bytes", | ||||
CTLFLAG_RD, &(rxr->rx_bytes), | CTLFLAG_RD, &(rxr->rx_bytes), | ||||
"Queue Bytes Received"); | "Queue Bytes Received"); | ||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "rx_desc_err", | |||||
CTLFLAG_RD, &(rxr->desc_errs), | |||||
"Queue Rx Descriptor Errors"); | |||||
SYSCTL_ADD_UINT(ctx, queue_list, OID_AUTO, "rx_itr", | |||||
CTLFLAG_RD, &(rxr->itr), 0, | |||||
"Queue Rx ITR Interval"); | |||||
SYSCTL_ADD_UINT(ctx, queue_list, OID_AUTO, "tx_itr", | |||||
CTLFLAG_RD, &(txr->itr), 0, | |||||
"Queue Tx ITR Interval"); | |||||
// Not actual latency; just a calculated value to put in a register | |||||
// TODO: Put in better descriptions here | |||||
SYSCTL_ADD_UINT(ctx, queue_list, OID_AUTO, "rx_latency", | |||||
CTLFLAG_RD, &(rxr->latency), 0, | |||||
"Queue Rx ITRL Average Interval"); | |||||
SYSCTL_ADD_UINT(ctx, queue_list, OID_AUTO, "tx_latency", | |||||
CTLFLAG_RD, &(txr->latency), 0, | |||||
"Queue Tx ITRL Average Interval"); | |||||
#ifdef IXL_DEBUG | |||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "rx_not_done", | |||||
CTLFLAG_RD, &(rxr->not_done), | |||||
"Queue Rx Descriptors not Done"); | |||||
SYSCTL_ADD_UINT(ctx, queue_list, OID_AUTO, "rx_next_refresh", | |||||
CTLFLAG_RD, &(rxr->next_refresh), 0, | |||||
"Queue Rx Descriptors not Done"); | |||||
SYSCTL_ADD_UINT(ctx, queue_list, OID_AUTO, "rx_next_check", | |||||
CTLFLAG_RD, &(rxr->next_check), 0, | |||||
"Queue Rx Descriptors not Done"); | |||||
SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "qtx_tail", | |||||
CTLTYPE_UINT | CTLFLAG_RD, &queues[q], | |||||
sizeof(struct ixl_queue), | |||||
ixl_sysctl_qtx_tail_handler, "IU", | |||||
"Queue Transmit Descriptor Tail"); | |||||
SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "qrx_tail", | |||||
CTLTYPE_UINT | CTLFLAG_RD, &queues[q], | |||||
sizeof(struct ixl_queue), | |||||
ixl_sysctl_qrx_tail_handler, "IU", | |||||
"Queue Receive Descriptor Tail"); | |||||
#endif | |||||
} | } | ||||
/* MAC stats */ | /* MAC stats */ | ||||
ixl_add_sysctls_mac_stats(ctx, child, pf_stats); | ixl_add_sysctls_mac_stats(ctx, child, pf_stats); | ||||
} | } | ||||
static void | static void | ||||
ixl_add_sysctls_eth_stats(struct sysctl_ctx_list *ctx, | ixl_add_sysctls_eth_stats(struct sysctl_ctx_list *ctx, | ||||
▲ Show 20 Lines • Show All 89 Lines • ▼ Show 20 Lines | ixl_add_sysctls_mac_stats(struct sysctl_ctx_list *ctx, | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
** ixl_config_rss - setup RSS | ** ixl_config_rss - setup RSS | ||||
** - note this is done for the single vsi | ** - note this is done for the single vsi | ||||
*/ | */ | ||||
static void ixl_config_rss(struct ixl_vsi *vsi) | static void | ||||
ixl_config_rss(struct ixl_vsi *vsi) | |||||
{ | { | ||||
struct ixl_pf *pf = (struct ixl_pf *)vsi->back; | struct ixl_pf *pf = (struct ixl_pf *)vsi->back; | ||||
struct i40e_hw *hw = vsi->hw; | struct i40e_hw *hw = vsi->hw; | ||||
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; | ||||
#ifdef RSS | #ifdef RSS | ||||
u32 rss_hash_config; | u32 rss_hash_config; | ||||
u32 rss_seed[IXL_KEYSZ]; | u32 rss_seed[IXL_KEYSZ]; | ||||
#else | #else | ||||
u32 rss_seed[IXL_KEYSZ] = {0x41b01687, | u32 rss_seed[IXL_KEYSZ] = {0x41b01687, | ||||
0x183cfd8c, 0xce880440, 0x580cbc3c, | 0x183cfd8c, 0xce880440, 0x580cbc3c, | ||||
0x35897377, 0x328b25e1, 0x4fa98922, | 0x35897377, 0x328b25e1, 0x4fa98922, | ||||
0xb7d90c14, 0xd5bad70d, 0xcd15a2c1}; | 0xb7d90c14, 0xd5bad70d, 0xcd15a2c1}; | ||||
#endif | #endif | ||||
#ifdef RSS | #ifdef RSS | ||||
/* Fetch the configured RSS key */ | /* Fetch the configured RSS key */ | ||||
rss_getkey((uint8_t *) &rss_seed); | rss_getkey((uint8_t *) &rss_seed); | ||||
#endif | #endif | ||||
/* Fill out hash function seed */ | /* Fill out hash function seed */ | ||||
for (i = 0; i < IXL_KEYSZ; i++) | for (i = 0; i < IXL_KEYSZ; i++) | ||||
wr32(hw, I40E_PFQF_HKEY(i), rss_seed[i]); | i40e_write_rx_ctl(hw, I40E_PFQF_HKEY(i), rss_seed[i]); | ||||
/* Enable PCTYPES for RSS: */ | /* Enable PCTYPES for RSS: */ | ||||
#ifdef RSS | #ifdef RSS | ||||
rss_hash_config = rss_gethashconfig(); | rss_hash_config = rss_gethashconfig(); | ||||
if (rss_hash_config & RSS_HASHTYPE_RSS_IPV4) | if (rss_hash_config & RSS_HASHTYPE_RSS_IPV4) | ||||
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER); | set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER); | ||||
if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV4) | if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV4) | ||||
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP); | set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP); | ||||
Show All 16 Lines | set_hena = | ||||
((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4) | | ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4) | | ||||
((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) | | ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) | | ||||
((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP) | | ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP) | | ||||
((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_SCTP) | | ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_SCTP) | | ||||
((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_OTHER) | | ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_OTHER) | | ||||
((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6) | | ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6) | | ||||
((u64)1 << I40E_FILTER_PCTYPE_L2_PAYLOAD); | ((u64)1 << I40E_FILTER_PCTYPE_L2_PAYLOAD); | ||||
#endif | #endif | ||||
hena = (u64)rd32(hw, I40E_PFQF_HENA(0)) | | hena = (u64)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(0)) | | ||||
((u64)rd32(hw, I40E_PFQF_HENA(1)) << 32); | ((u64)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(1)) << 32); | ||||
hena |= set_hena; | hena |= set_hena; | ||||
wr32(hw, I40E_PFQF_HENA(0), (u32)hena); | i40e_write_rx_ctl(hw, I40E_PFQF_HENA(0), (u32)hena); | ||||
wr32(hw, I40E_PFQF_HENA(1), (u32)(hena >> 32)); | i40e_write_rx_ctl(hw, I40E_PFQF_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 = j = 0; i < pf->hw.func_caps.rss_table_size; i++, j++) { | for (i = j = 0; i < pf->hw.func_caps.rss_table_size; i++, j++) { | ||||
if (j == vsi->num_queues) | if (j == vsi->num_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. | ||||
▲ Show 20 Lines • Show All 100 Lines • ▼ Show 20 Lines | ixl_setup_vlan_filters(struct ixl_vsi *vsi) | ||||
flags |= (IXL_FILTER_ADD | IXL_FILTER_USED); | flags |= (IXL_FILTER_ADD | IXL_FILTER_USED); | ||||
ixl_add_hw_filters(vsi, flags, cnt); | ixl_add_hw_filters(vsi, flags, cnt); | ||||
return; | return; | ||||
} | } | ||||
/* | /* | ||||
** Initialize filter list and add filters that the hardware | ** Initialize filter list and add filters that the hardware | ||||
** needs to know about. | ** needs to know about. | ||||
** | |||||
** Requires VSI's filter list & seid to be set before calling. | |||||
*/ | */ | ||||
static void | static void | ||||
ixl_init_filters(struct ixl_vsi *vsi) | ixl_init_filters(struct ixl_vsi *vsi) | ||||
{ | { | ||||
/* Add broadcast address */ | /* Add broadcast address */ | ||||
ixl_add_filter(vsi, ixl_bcast_addr, IXL_VLAN_ANY); | ixl_add_filter(vsi, ixl_bcast_addr, IXL_VLAN_ANY); | ||||
/* | |||||
* Prevent Tx flow control frames from being sent out by | |||||
* non-firmware transmitters. | |||||
*/ | |||||
i40e_add_filter_to_drop_tx_flow_control_frames(vsi->hw, vsi->seid); | |||||
} | } | ||||
/* | /* | ||||
** This routine adds mulicast filters | ** This routine adds mulicast filters | ||||
*/ | */ | ||||
static void | static void | ||||
ixl_add_mc_filter(struct ixl_vsi *vsi, u8 *macaddr) | ixl_add_mc_filter(struct ixl_vsi *vsi, u8 *macaddr) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 347 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/** | /** | ||||
* ixl_handle_mdd_event | * ixl_handle_mdd_event | ||||
* | * | ||||
* Called from interrupt handler to identify possibly malicious vfs | * Called from interrupt handler to identify possibly malicious vfs | ||||
* (But also detects events from the PF, as well) | * (But also detects events from the PF, as well) | ||||
**/ | **/ | ||||
static void ixl_handle_mdd_event(struct ixl_pf *pf) | static void | ||||
ixl_handle_mdd_event(struct ixl_pf *pf) | |||||
{ | { | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
device_t dev = pf->dev; | device_t dev = pf->dev; | ||||
bool mdd_detected = false; | bool mdd_detected = false; | ||||
bool pf_mdd_detected = false; | bool pf_mdd_detected = false; | ||||
u32 reg; | u32 reg; | ||||
/* find what triggered the MDD event */ | /* find what triggered the MDD event */ | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | |||||
static void | static void | ||||
ixl_enable_intr(struct ixl_vsi *vsi) | ixl_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_queue *que = vsi->queues; | ||||
if (ixl_enable_msix) { | if (ixl_enable_msix) { | ||||
ixl_enable_adminq(hw); | |||||
for (int i = 0; i < vsi->num_queues; i++, que++) | for (int i = 0; i < vsi->num_queues; i++, que++) | ||||
ixl_enable_queue(hw, que->me); | ixl_enable_queue(hw, que->me); | ||||
} else | } else | ||||
ixl_enable_legacy(hw); | ixl_enable_legacy(hw); | ||||
} | } | ||||
static void | static void | ||||
ixl_disable_rings_intr(struct ixl_vsi *vsi) | ixl_disable_rings_intr(struct ixl_vsi *vsi) | ||||
Show All 21 Lines | |||||
{ | { | ||||
u32 reg; | u32 reg; | ||||
reg = I40E_PFINT_DYN_CTL0_INTENA_MASK | | reg = I40E_PFINT_DYN_CTL0_INTENA_MASK | | ||||
I40E_PFINT_DYN_CTL0_CLEARPBA_MASK | | I40E_PFINT_DYN_CTL0_CLEARPBA_MASK | | ||||
(IXL_ITR_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT); | (IXL_ITR_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT); | ||||
wr32(hw, I40E_PFINT_DYN_CTL0, reg); | wr32(hw, I40E_PFINT_DYN_CTL0, reg); | ||||
ixl_flush(hw); | ixl_flush(hw); | ||||
return; | |||||
} | } | ||||
static void | static void | ||||
ixl_disable_adminq(struct i40e_hw *hw) | ixl_disable_adminq(struct i40e_hw *hw) | ||||
{ | { | ||||
u32 reg; | u32 reg; | ||||
reg = IXL_ITR_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT; | reg = IXL_ITR_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT; | ||||
wr32(hw, I40E_PFINT_DYN_CTL0, reg); | wr32(hw, I40E_PFINT_DYN_CTL0, reg); | ||||
ixl_flush(hw); | |||||
return; | |||||
} | } | ||||
static void | static void | ||||
ixl_enable_queue(struct i40e_hw *hw, int id) | ixl_enable_queue(struct i40e_hw *hw, int id) | ||||
{ | { | ||||
u32 reg; | u32 reg; | ||||
reg = I40E_PFINT_DYN_CTLN_INTENA_MASK | | reg = I40E_PFINT_DYN_CTLN_INTENA_MASK | | ||||
I40E_PFINT_DYN_CTLN_CLEARPBA_MASK | | I40E_PFINT_DYN_CTLN_CLEARPBA_MASK | | ||||
(IXL_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT); | (IXL_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT); | ||||
wr32(hw, I40E_PFINT_DYN_CTLN(id), reg); | wr32(hw, I40E_PFINT_DYN_CTLN(id), reg); | ||||
} | } | ||||
static void | static void | ||||
ixl_disable_queue(struct i40e_hw *hw, int id) | ixl_disable_queue(struct i40e_hw *hw, int id) | ||||
{ | { | ||||
u32 reg; | u32 reg; | ||||
reg = IXL_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT; | reg = IXL_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT; | ||||
wr32(hw, I40E_PFINT_DYN_CTLN(id), reg); | wr32(hw, I40E_PFINT_DYN_CTLN(id), reg); | ||||
return; | |||||
} | } | ||||
static void | static void | ||||
ixl_enable_legacy(struct i40e_hw *hw) | ixl_enable_legacy(struct i40e_hw *hw) | ||||
{ | { | ||||
u32 reg; | u32 reg; | ||||
reg = I40E_PFINT_DYN_CTL0_INTENA_MASK | | reg = I40E_PFINT_DYN_CTL0_INTENA_MASK | | ||||
I40E_PFINT_DYN_CTL0_CLEARPBA_MASK | | I40E_PFINT_DYN_CTL0_CLEARPBA_MASK | | ||||
(IXL_ITR_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT); | (IXL_ITR_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT); | ||||
wr32(hw, I40E_PFINT_DYN_CTL0, reg); | wr32(hw, I40E_PFINT_DYN_CTL0, reg); | ||||
} | } | ||||
static void | static void | ||||
ixl_disable_legacy(struct i40e_hw *hw) | ixl_disable_legacy(struct i40e_hw *hw) | ||||
{ | { | ||||
u32 reg; | u32 reg; | ||||
reg = IXL_ITR_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT; | reg = IXL_ITR_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT; | ||||
wr32(hw, I40E_PFINT_DYN_CTL0, reg); | wr32(hw, I40E_PFINT_DYN_CTL0, reg); | ||||
return; | |||||
} | } | ||||
static void | static void | ||||
ixl_update_stats_counters(struct ixl_pf *pf) | ixl_update_stats_counters(struct ixl_pf *pf) | ||||
{ | { | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
struct ixl_vsi *vsi = &pf->vsi; | struct ixl_vsi *vsi = &pf->vsi; | ||||
struct ixl_vf *vf; | struct ixl_vf *vf; | ||||
▲ Show 20 Lines • Show All 162 Lines • ▼ Show 20 Lines | ixl_update_stats_counters(struct ixl_pf *pf) | ||||
for (int i = 0; i < pf->num_vfs; i++) { | for (int i = 0; i < pf->num_vfs; i++) { | ||||
vf = &pf->vfs[i]; | vf = &pf->vfs[i]; | ||||
if (vf->vf_flags & VF_FLAG_ENABLED) | if (vf->vf_flags & VF_FLAG_ENABLED) | ||||
ixl_update_eth_stats(&pf->vfs[i].vsi); | ixl_update_eth_stats(&pf->vfs[i].vsi); | ||||
} | } | ||||
} | } | ||||
static int | |||||
ixl_rebuild_hw_structs_after_reset(struct ixl_pf *pf) | |||||
{ | |||||
struct i40e_hw *hw = &pf->hw; | |||||
struct ixl_vsi *vsi = &pf->vsi; | |||||
device_t dev = pf->dev; | |||||
bool is_up = false; | |||||
int error = 0; | |||||
is_up = !!(vsi->ifp->if_drv_flags & IFF_DRV_RUNNING); | |||||
/* Teardown */ | |||||
if (is_up) | |||||
ixl_stop(pf); | |||||
error = i40e_shutdown_lan_hmc(hw); | |||||
if (error) | |||||
device_printf(dev, | |||||
"Shutdown LAN HMC failed with code %d\n", error); | |||||
ixl_disable_adminq(hw); | |||||
ixl_teardown_adminq_msix(pf); | |||||
error = i40e_shutdown_adminq(hw); | |||||
if (error) | |||||
device_printf(dev, | |||||
"Shutdown Admin queue failed with code %d\n", error); | |||||
/* Setup */ | |||||
error = i40e_init_adminq(hw); | |||||
if (error != 0 && error != I40E_ERR_FIRMWARE_API_VERSION) { | |||||
device_printf(dev, "Unable to initialize Admin Queue, error %d\n", | |||||
error); | |||||
} | |||||
error = ixl_setup_adminq_msix(pf); | |||||
if (error) { | |||||
device_printf(dev, "ixl_setup_adminq_msix error: %d\n", | |||||
error); | |||||
} | |||||
ixl_configure_intr0_msix(pf); | |||||
ixl_enable_adminq(hw); | |||||
error = i40e_init_lan_hmc(hw, hw->func_caps.num_tx_qp, | |||||
hw->func_caps.num_rx_qp, 0, 0); | |||||
if (error) { | |||||
device_printf(dev, "init_lan_hmc failed: %d\n", error); | |||||
} | |||||
error = i40e_configure_lan_hmc(hw, I40E_HMC_MODEL_DIRECT_ONLY); | |||||
if (error) { | |||||
device_printf(dev, "configure_lan_hmc failed: %d\n", error); | |||||
} | |||||
if (is_up) | |||||
ixl_init(pf); | |||||
return (0); | |||||
} | |||||
static void | |||||
ixl_handle_empr_reset(struct ixl_pf *pf) | |||||
{ | |||||
struct i40e_hw *hw = &pf->hw; | |||||
device_t dev = pf->dev; | |||||
int count = 0; | |||||
u32 reg; | |||||
/* Typically finishes within 3-4 seconds */ | |||||
while (count++ < 100) { | |||||
reg = rd32(hw, I40E_GLGEN_RSTAT) | |||||
& I40E_GLGEN_RSTAT_DEVSTATE_MASK; | |||||
if (reg) | |||||
i40e_msec_delay(100); | |||||
else | |||||
break; | |||||
} | |||||
#ifdef IXL_DEBUG | |||||
// Reset-related | |||||
device_printf(dev, "EMPR reset wait count: %d\n", count); | |||||
#endif | |||||
device_printf(dev, "Rebuilding driver state...\n"); | |||||
ixl_rebuild_hw_structs_after_reset(pf); | |||||
device_printf(dev, "Rebuilding driver state done.\n"); | |||||
atomic_clear_int(&pf->state, IXL_PF_STATE_EMPR_RESETTING); | |||||
} | |||||
/* | /* | ||||
** Tasklet handler for MSIX Adminq interrupts | ** Tasklet handler for MSIX Adminq interrupts | ||||
** - do outside interrupt since it might sleep | ** - do outside interrupt since it might sleep | ||||
*/ | */ | ||||
static void | static void | ||||
ixl_do_adminq(void *context, int pending) | ixl_do_adminq(void *context, int pending) | ||||
{ | { | ||||
struct ixl_pf *pf = context; | struct ixl_pf *pf = context; | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
struct ixl_vsi *vsi = &pf->vsi; | |||||
struct i40e_arq_event_info event; | struct i40e_arq_event_info event; | ||||
i40e_status ret; | i40e_status ret; | ||||
u32 reg, loop = 0; | device_t dev = pf->dev; | ||||
u32 loop = 0; | |||||
u16 opcode, result; | u16 opcode, result; | ||||
if (pf->state & IXL_PF_STATE_EMPR_RESETTING) { | |||||
/* Flag cleared at end of this function */ | |||||
ixl_handle_empr_reset(pf); | |||||
return; | |||||
} | |||||
/* Admin Queue handling */ | |||||
event.buf_len = IXL_AQ_BUF_SZ; | event.buf_len = IXL_AQ_BUF_SZ; | ||||
event.msg_buf = malloc(event.buf_len, | event.msg_buf = malloc(event.buf_len, | ||||
M_DEVBUF, M_NOWAIT | M_ZERO); | M_DEVBUF, M_NOWAIT | M_ZERO); | ||||
if (!event.msg_buf) { | if (!event.msg_buf) { | ||||
printf("Unable to allocate adminq memory\n"); | device_printf(dev, "%s: Unable to allocate memory for Admin" | ||||
" Queue event!\n", __func__); | |||||
return; | return; | ||||
} | } | ||||
IXL_PF_LOCK(pf); | IXL_PF_LOCK(pf); | ||||
/* clean and process any events */ | /* clean and process any events */ | ||||
do { | do { | ||||
ret = i40e_clean_arq_element(hw, &event, &result); | ret = i40e_clean_arq_element(hw, &event, &result); | ||||
if (ret) | if (ret) | ||||
break; | break; | ||||
opcode = LE16_TO_CPU(event.desc.opcode); | opcode = LE16_TO_CPU(event.desc.opcode); | ||||
#ifdef IXL_DEBUG | |||||
device_printf(dev, "%s: Admin Queue event: %#06x\n", __func__, | |||||
opcode); | |||||
#endif | |||||
switch (opcode) { | switch (opcode) { | ||||
case i40e_aqc_opc_get_link_status: | case i40e_aqc_opc_get_link_status: | ||||
ixl_link_event(pf, &event); | ixl_link_event(pf, &event); | ||||
ixl_update_link_status(pf); | |||||
break; | break; | ||||
case i40e_aqc_opc_send_msg_to_pf: | case i40e_aqc_opc_send_msg_to_pf: | ||||
#ifdef PCI_IOV | #ifdef PCI_IOV | ||||
ixl_handle_vf_msg(pf, &event); | ixl_handle_vf_msg(pf, &event); | ||||
#endif | #endif | ||||
break; | break; | ||||
case i40e_aqc_opc_event_lan_overflow: | case i40e_aqc_opc_event_lan_overflow: | ||||
break; | |||||
default: | default: | ||||
#ifdef IXL_DEBUG | |||||
printf("AdminQ unknown event %x\n", opcode); | |||||
#endif | |||||
break; | break; | ||||
} | } | ||||
} while (result && (loop++ < IXL_ADM_LIMIT)); | } while (result && (loop++ < IXL_ADM_LIMIT)); | ||||
reg = rd32(hw, I40E_PFINT_ICR0_ENA); | |||||
reg |= I40E_PFINT_ICR0_ENA_ADMINQ_MASK; | |||||
wr32(hw, I40E_PFINT_ICR0_ENA, reg); | |||||
free(event.msg_buf, M_DEVBUF); | free(event.msg_buf, M_DEVBUF); | ||||
/* | /* | ||||
* If there are still messages to process, reschedule ourselves. | * If there are still messages to process, reschedule ourselves. | ||||
* Otherwise, re-enable our interrupt and go to sleep. | * Otherwise, re-enable our interrupt and go to sleep. | ||||
*/ | */ | ||||
if (result > 0) | if (result > 0) | ||||
taskqueue_enqueue(pf->tq, &pf->adminq); | taskqueue_enqueue(pf->tq, &pf->adminq); | ||||
else | else | ||||
ixl_enable_intr(vsi); | ixl_enable_adminq(hw); | ||||
IXL_PF_UNLOCK(pf); | IXL_PF_UNLOCK(pf); | ||||
} | } | ||||
#ifdef IXL_DEBUG_SYSCTL | |||||
static int | |||||
ixl_debug_info(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct ixl_pf *pf; | |||||
int error, input = 0; | |||||
error = sysctl_handle_int(oidp, &input, 0, req); | |||||
if (error || !req->newptr) | |||||
return (error); | |||||
if (input == 1) { | |||||
pf = (struct ixl_pf *)arg1; | |||||
ixl_print_debug_info(pf); | |||||
} | |||||
return (error); | |||||
} | |||||
static void | |||||
ixl_print_debug_info(struct ixl_pf *pf) | |||||
{ | |||||
struct i40e_hw *hw = &pf->hw; | |||||
struct ixl_vsi *vsi = &pf->vsi; | |||||
struct ixl_queue *que = vsi->queues; | |||||
struct rx_ring *rxr = &que->rxr; | |||||
struct tx_ring *txr = &que->txr; | |||||
u32 reg; | |||||
printf("Queue irqs = %jx\n", (uintmax_t)que->irqs); | |||||
printf("AdminQ irqs = %jx\n", (uintmax_t)pf->admin_irq); | |||||
printf("RX next check = %x\n", rxr->next_check); | |||||
printf("RX not ready = %jx\n", (uintmax_t)rxr->not_done); | |||||
printf("RX packets = %jx\n", (uintmax_t)rxr->rx_packets); | |||||
printf("TX desc avail = %x\n", txr->avail); | |||||
reg = rd32(hw, I40E_GLV_GORCL(0xc)); | |||||
printf("RX Bytes = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLPRT_GORCL(hw->port)); | |||||
printf("Port RX Bytes = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLV_RDPC(0xc)); | |||||
printf("RX discard = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLPRT_RDPC(hw->port)); | |||||
printf("Port RX discard = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLV_TEPC(0xc)); | |||||
printf("TX errors = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLV_GOTCL(0xc)); | |||||
printf("TX Bytes = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLPRT_RUC(hw->port)); | |||||
printf("RX undersize = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLPRT_RFC(hw->port)); | |||||
printf("RX fragments = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLPRT_ROC(hw->port)); | |||||
printf("RX oversize = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLPRT_RLEC(hw->port)); | |||||
printf("RX length error = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLPRT_MRFC(hw->port)); | |||||
printf("mac remote fault = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLPRT_MLFC(hw->port)); | |||||
printf("mac local fault = %x\n", reg); | |||||
} | |||||
#endif | |||||
/** | /** | ||||
* Update VSI-specific ethernet statistics counters. | * Update VSI-specific ethernet statistics counters. | ||||
**/ | **/ | ||||
void ixl_update_eth_stats(struct ixl_vsi *vsi) | void | ||||
ixl_update_eth_stats(struct ixl_vsi *vsi) | |||||
{ | { | ||||
struct ixl_pf *pf = (struct ixl_pf *)vsi->back; | struct ixl_pf *pf = (struct ixl_pf *)vsi->back; | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
struct i40e_eth_stats *es; | struct i40e_eth_stats *es; | ||||
struct i40e_eth_stats *oes; | struct i40e_eth_stats *oes; | ||||
struct i40e_hw_port_stats *nsd; | struct i40e_hw_port_stats *nsd; | ||||
u16 stat_idx = vsi->info.stat_counter_idx; | u16 stat_idx = vsi->info.stat_counter_idx; | ||||
▲ Show 20 Lines • Show All 155 Lines • ▼ Show 20 Lines | ixl_stat_update32(struct i40e_hw *hw, u32 reg, | ||||
if (!offset_loaded) | if (!offset_loaded) | ||||
*offset = new_data; | *offset = new_data; | ||||
if (new_data >= *offset) | if (new_data >= *offset) | ||||
*stat = (u32)(new_data - *offset); | *stat = (u32)(new_data - *offset); | ||||
else | else | ||||
*stat = (u32)((new_data + ((u64)1 << 32)) - *offset); | *stat = (u32)((new_data + ((u64)1 << 32)) - *offset); | ||||
} | } | ||||
static void | |||||
ixl_add_device_sysctls(struct ixl_pf *pf) | |||||
{ | |||||
device_t dev = pf->dev; | |||||
/* Set up sysctls */ | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "fc", CTLTYPE_INT | CTLFLAG_RW, | |||||
pf, 0, ixl_set_flowcntl, "I", IXL_SYSCTL_HELP_FC); | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "advertise_speed", CTLTYPE_INT | CTLFLAG_RW, | |||||
pf, 0, ixl_set_advertise, "I", IXL_SYSCTL_HELP_SET_ADVERTISE); | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "current_speed", CTLTYPE_STRING | CTLFLAG_RD, | |||||
pf, 0, ixl_current_speed, "A", "Current Port Speed"); | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "fw_version", CTLTYPE_STRING | CTLFLAG_RD, | |||||
pf, 0, ixl_sysctl_show_fw, "A", "Firmware version"); | |||||
#if 0 | |||||
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "rx_itr", CTLFLAG_RW, | |||||
&ixl_rx_itr, 0, "RX ITR"); | |||||
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "dynamic_rx_itr", CTLFLAG_RW, | |||||
&ixl_dynamic_rx_itr, 0, "Dynamic RX ITR"); | |||||
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "tx_itr", CTLFLAG_RW, | |||||
&ixl_tx_itr, 0, "TX ITR"); | |||||
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "dynamic_tx_itr", CTLFLAG_RW, | |||||
&ixl_dynamic_tx_itr, 0, "Dynamic TX ITR"); | |||||
#endif | |||||
#ifdef IXL_DEBUG_SYSCTL | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "debug", CTLTYPE_INT|CTLFLAG_RW, pf, 0, | |||||
ixl_debug_info, "I", "Debug Information"); | |||||
/* Shared-code debug message level */ | |||||
SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "debug_mask", CTLFLAG_RW, | |||||
&pf->hw.debug_mask, 0, "Debug Message Level"); | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "link_status", CTLTYPE_STRING | CTLFLAG_RD, | |||||
pf, 0, ixl_sysctl_link_status, "A", IXL_SYSCTL_HELP_LINK_STATUS); | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "phy_abilities", CTLTYPE_STRING | CTLFLAG_RD, | |||||
pf, 0, ixl_sysctl_phy_abilities, "A", "PHY Abilities"); | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "filter_list", CTLTYPE_STRING | CTLFLAG_RD, | |||||
pf, 0, ixl_sysctl_sw_filter_list, "A", "SW Filter List"); | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "hw_res_alloc", CTLTYPE_STRING | CTLFLAG_RD, | |||||
pf, 0, ixl_sysctl_hw_res_alloc, "A", "HW Resource Allocation"); | |||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "switch_config", CTLTYPE_STRING | CTLFLAG_RD, | |||||
pf, 0, ixl_sysctl_switch_config, "A", "HW Switch Configuration"); | |||||
#ifdef PCI_IOV | |||||
SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "vc_debug_level", CTLFLAG_RW, &pf->vc_debug_lvl, | |||||
0, "PF/VF Virtual Channel debug level"); | |||||
#endif | |||||
#endif | |||||
} | |||||
/* | /* | ||||
** Set flow control using sysctl: | ** Set flow control using sysctl: | ||||
** 0 - off | ** 0 - off | ||||
** 1 - rx pause | ** 1 - rx pause | ||||
** 2 - tx pause | ** 2 - tx pause | ||||
** 3 - full | ** 3 - full | ||||
*/ | */ | ||||
static int | static int | ||||
ixl_set_flowcntl(SYSCTL_HANDLER_ARGS) | ixl_set_flowcntl(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
/* | /* | ||||
* TODO: ensure flow control is disabled if | |||||
* priority flow control is enabled | |||||
* | |||||
* TODO: ensure tx CRC by hardware should be enabled | * TODO: ensure tx CRC by hardware should be enabled | ||||
* if tx flow control is enabled. | * if tx flow control is enabled. | ||||
* ^ N/A for 40G ports | |||||
*/ | */ | ||||
struct ixl_pf *pf = (struct ixl_pf *)arg1; | struct ixl_pf *pf = (struct ixl_pf *)arg1; | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
device_t dev = pf->dev; | device_t dev = pf->dev; | ||||
int error = 0; | int requested_fc, error = 0; | ||||
enum i40e_status_code aq_error = 0; | enum i40e_status_code aq_error = 0; | ||||
u8 fc_aq_err = 0; | u8 fc_aq_err = 0; | ||||
/* Get request */ | /* Get request */ | ||||
error = sysctl_handle_int(oidp, &pf->fc, 0, req); | requested_fc = pf->fc; | ||||
error = sysctl_handle_int(oidp, &requested_fc, 0, req); | |||||
if ((error) || (req->newptr == NULL)) | if ((error) || (req->newptr == NULL)) | ||||
return (error); | return (error); | ||||
if (pf->fc < 0 || pf->fc > 3) { | if (requested_fc < 0 || requested_fc > 3) { | ||||
device_printf(dev, | device_printf(dev, | ||||
"Invalid fc mode; valid modes are 0 through 3\n"); | "Invalid fc mode; valid modes are 0 through 3\n"); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
/* | |||||
** Changing flow control mode currently does not work on | |||||
** 40GBASE-CR4 PHYs | |||||
*/ | |||||
if (hw->phy.link_info.phy_type == I40E_PHY_TYPE_40GBASE_CR4 | |||||
|| hw->phy.link_info.phy_type == I40E_PHY_TYPE_40GBASE_CR4_CU) { | |||||
device_printf(dev, "Changing flow control mode unsupported" | |||||
" on 40GBase-CR4 media.\n"); | |||||
return (ENODEV); | |||||
} | |||||
/* Set fc ability for port */ | /* Set fc ability for port */ | ||||
hw->fc.requested_mode = pf->fc; | hw->fc.requested_mode = requested_fc; | ||||
aq_error = i40e_set_fc(hw, &fc_aq_err, TRUE); | aq_error = i40e_set_fc(hw, &fc_aq_err, TRUE); | ||||
if (aq_error) { | if (aq_error) { | ||||
device_printf(dev, | device_printf(dev, | ||||
"%s: Error setting new fc mode %d; fc_err %#x\n", | "%s: Error setting new fc mode %d; fc_err %#x\n", | ||||
__func__, aq_error, fc_aq_err); | __func__, aq_error, fc_aq_err); | ||||
return (EAGAIN); | return (EIO); | ||||
} | } | ||||
pf->fc = requested_fc; | |||||
/* Get new link state */ | |||||
i40e_msec_delay(250); | |||||
hw->phy.get_link_info = TRUE; | |||||
i40e_get_link_status(hw, &pf->link_up); | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
ixl_current_speed(SYSCTL_HANDLER_ARGS) | ixl_current_speed(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct ixl_pf *pf = (struct ixl_pf *)arg1; | struct ixl_pf *pf = (struct ixl_pf *)arg1; | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | ixl_set_advertised_speeds(struct ixl_pf *pf, int speeds) | ||||
bzero(&config, sizeof(config)); | bzero(&config, sizeof(config)); | ||||
config.phy_type = abilities.phy_type; | config.phy_type = abilities.phy_type; | ||||
config.abilities = abilities.abilities | config.abilities = abilities.abilities | ||||
| I40E_AQ_PHY_ENABLE_ATOMIC_LINK; | | I40E_AQ_PHY_ENABLE_ATOMIC_LINK; | ||||
config.eee_capability = abilities.eee_capability; | config.eee_capability = abilities.eee_capability; | ||||
config.eeer = abilities.eeer_val; | config.eeer = abilities.eeer_val; | ||||
config.low_power_ctrl = abilities.d3_lpan; | config.low_power_ctrl = abilities.d3_lpan; | ||||
/* Translate into aq cmd link_speed */ | /* Translate into aq cmd link_speed */ | ||||
if (speeds & 0x10) | |||||
config.link_speed |= I40E_LINK_SPEED_40GB; | |||||
if (speeds & 0x8) | if (speeds & 0x8) | ||||
config.link_speed |= I40E_LINK_SPEED_20GB; | config.link_speed |= I40E_LINK_SPEED_20GB; | ||||
if (speeds & 0x4) | if (speeds & 0x4) | ||||
config.link_speed |= I40E_LINK_SPEED_10GB; | config.link_speed |= I40E_LINK_SPEED_10GB; | ||||
if (speeds & 0x2) | if (speeds & 0x2) | ||||
config.link_speed |= I40E_LINK_SPEED_1GB; | config.link_speed |= I40E_LINK_SPEED_1GB; | ||||
if (speeds & 0x1) | if (speeds & 0x1) | ||||
config.link_speed |= I40E_LINK_SPEED_100MB; | config.link_speed |= I40E_LINK_SPEED_100MB; | ||||
/* Do aq command & restart link */ | /* Do aq command & restart link */ | ||||
aq_error = i40e_aq_set_phy_config(hw, &config, NULL); | aq_error = i40e_aq_set_phy_config(hw, &config, NULL); | ||||
if (aq_error) { | if (aq_error) { | ||||
device_printf(dev, | device_printf(dev, | ||||
"%s: Error setting new phy config %d," | "%s: Error setting new phy config %d," | ||||
" aq error: %d\n", __func__, aq_error, | " aq error: %d\n", __func__, aq_error, | ||||
hw->aq.asq_last_status); | hw->aq.asq_last_status); | ||||
return (EAGAIN); | return (EAGAIN); | ||||
} | } | ||||
/* | /* | ||||
** This seems a bit heavy handed, but we | ** This seems a bit heavy handed, but we | ||||
** need to get a reinit on some devices | ** need to get a reinit on some devices | ||||
*/ | */ | ||||
IXL_PF_LOCK(pf); | IXL_PF_LOCK(pf); | ||||
ixl_stop(pf); | ixl_stop_locked(pf); | ||||
ixl_init_locked(pf); | ixl_init_locked(pf); | ||||
IXL_PF_UNLOCK(pf); | IXL_PF_UNLOCK(pf); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
** Control link advertise speed: | ** Control link advertise speed: | ||||
** Flags: | ** Flags: | ||||
** 0x1 - advertise 100 Mb | ** 0x1 - advertise 100 Mb | ||||
** 0x2 - advertise 1G | ** 0x2 - advertise 1G | ||||
** 0x4 - advertise 10G | ** 0x4 - advertise 10G | ||||
** 0x8 - advertise 20G | ** 0x8 - advertise 20G | ||||
** 0x10 - advertise 40G | |||||
** | ** | ||||
** Does not work on 40G devices. | ** Set to 0 to disable link | ||||
*/ | */ | ||||
static int | static int | ||||
ixl_set_advertise(SYSCTL_HANDLER_ARGS) | ixl_set_advertise(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct ixl_pf *pf = (struct ixl_pf *)arg1; | struct ixl_pf *pf = (struct ixl_pf *)arg1; | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
device_t dev = pf->dev; | device_t dev = pf->dev; | ||||
int requested_ls = 0; | int requested_ls = 0; | ||||
int error = 0; | int error = 0; | ||||
/* | |||||
** FW doesn't support changing advertised speed | |||||
** for 40G devices; speed is always 40G. | |||||
*/ | |||||
if (i40e_is_40G_device(hw->device_id)) | |||||
return (ENODEV); | |||||
/* Read in new mode */ | /* Read in new mode */ | ||||
requested_ls = pf->advertised_speed; | requested_ls = pf->advertised_speed; | ||||
error = sysctl_handle_int(oidp, &requested_ls, 0, req); | error = sysctl_handle_int(oidp, &requested_ls, 0, req); | ||||
if ((error) || (req->newptr == NULL)) | if ((error) || (req->newptr == NULL)) | ||||
return (error); | return (error); | ||||
/* Check for sane value */ | /* Check for sane value */ | ||||
if (requested_ls < 0x1 || requested_ls > 0xE) { | if (requested_ls > 0x10) { | ||||
device_printf(dev, "Invalid advertised speed; " | device_printf(dev, "Invalid advertised speed; " | ||||
"valid modes are 0x1 through 0xE\n"); | "valid modes are 0x1 through 0x10\n"); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
/* Then check for validity based on adapter type */ | /* Then check for validity based on adapter type */ | ||||
switch (hw->device_id) { | switch (hw->device_id) { | ||||
case I40E_DEV_ID_10G_BASE_T: | case I40E_DEV_ID_10G_BASE_T: | ||||
if (requested_ls & 0x8) { | case I40E_DEV_ID_10G_BASE_T4: | ||||
/* BaseT */ | |||||
if (requested_ls & ~(0x7)) { | |||||
device_printf(dev, | device_printf(dev, | ||||
"20Gbs speed not supported on this device.\n"); | "Only 100M/1G/10G speeds supported on this device.\n"); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
break; | break; | ||||
case I40E_DEV_ID_20G_KR2: | case I40E_DEV_ID_20G_KR2: | ||||
if (requested_ls & 0x1) { | case I40E_DEV_ID_20G_KR2_A: | ||||
/* 20G */ | |||||
if (requested_ls & ~(0xE)) { | |||||
device_printf(dev, | device_printf(dev, | ||||
"100Mbs speed not supported on this device.\n"); | "Only 1G/10G/20G speeds supported on this device.\n"); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
break; | break; | ||||
case I40E_DEV_ID_KX_B: | |||||
case I40E_DEV_ID_QSFP_A: | |||||
case I40E_DEV_ID_QSFP_B: | |||||
/* 40G */ | |||||
if (requested_ls & ~(0x10)) { | |||||
device_printf(dev, | |||||
"Only 40G speeds supported on this device.\n"); | |||||
return (EINVAL); | |||||
} | |||||
break; | |||||
default: | default: | ||||
if (requested_ls & ~0x6) { | /* 10G (1G) */ | ||||
if (requested_ls & ~(0x6)) { | |||||
device_printf(dev, | device_printf(dev, | ||||
"Only 1/10Gbs speeds are supported on this device.\n"); | "Only 1/10Gbs speeds are supported on this device.\n"); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
/* Exit if no change */ | /* Exit if no change */ | ||||
Show All 14 Lines | |||||
** the bus this adapter is plugged into. | ** the bus this adapter is plugged into. | ||||
*/ | */ | ||||
static u16 | static u16 | ||||
ixl_get_bus_info(struct i40e_hw *hw, device_t dev) | ixl_get_bus_info(struct i40e_hw *hw, device_t dev) | ||||
{ | { | ||||
u16 link; | u16 link; | ||||
u32 offset; | u32 offset; | ||||
/* Get the PCI Express Capabilities offset */ | /* Get the PCI Express Capabilities offset */ | ||||
pci_find_cap(dev, PCIY_EXPRESS, &offset); | pci_find_cap(dev, PCIY_EXPRESS, &offset); | ||||
/* ...and read the Link Status Register */ | /* ...and read the Link Status Register */ | ||||
link = pci_read_config(dev, offset + PCIER_LINK_STA, 2); | link = pci_read_config(dev, offset + PCIER_LINK_STA, 2); | ||||
switch (link & I40E_PCI_LINK_WIDTH) { | switch (link & I40E_PCI_LINK_WIDTH) { | ||||
case I40E_PCI_LINK_WIDTH_1: | case I40E_PCI_LINK_WIDTH_1: | ||||
Show All 23 Lines | ixl_get_bus_info(struct i40e_hw *hw, device_t dev) | ||||
case I40E_PCI_LINK_SPEED_8000: | case I40E_PCI_LINK_SPEED_8000: | ||||
hw->bus.speed = i40e_bus_speed_8000; | hw->bus.speed = i40e_bus_speed_8000; | ||||
break; | break; | ||||
default: | default: | ||||
hw->bus.speed = i40e_bus_speed_unknown; | hw->bus.speed = i40e_bus_speed_unknown; | ||||
break; | break; | ||||
} | } | ||||
device_printf(dev,"PCI Express Bus: Speed %s %s\n", | device_printf(dev,"PCI Express Bus: Speed %s %s\n", | ||||
((hw->bus.speed == i40e_bus_speed_8000) ? "8.0GT/s": | ((hw->bus.speed == i40e_bus_speed_8000) ? "8.0GT/s": | ||||
(hw->bus.speed == i40e_bus_speed_5000) ? "5.0GT/s": | (hw->bus.speed == i40e_bus_speed_5000) ? "5.0GT/s": | ||||
(hw->bus.speed == i40e_bus_speed_2500) ? "2.5GT/s":"Unknown"), | (hw->bus.speed == i40e_bus_speed_2500) ? "2.5GT/s":"Unknown"), | ||||
(hw->bus.width == i40e_bus_width_pcie_x8) ? "Width x8" : | (hw->bus.width == i40e_bus_width_pcie_x8) ? "Width x8" : | ||||
(hw->bus.width == i40e_bus_width_pcie_x4) ? "Width x4" : | (hw->bus.width == i40e_bus_width_pcie_x4) ? "Width x4" : | ||||
(hw->bus.width == i40e_bus_width_pcie_x1) ? "Width x1" : | (hw->bus.width == i40e_bus_width_pcie_x1) ? "Width x1" : | ||||
("Unknown")); | ("Unknown")); | ||||
Show All 10 Lines | ixl_get_bus_info(struct i40e_hw *hw, device_t dev) | ||||
return (link); | return (link); | ||||
} | } | ||||
static int | static int | ||||
ixl_sysctl_show_fw(SYSCTL_HANDLER_ARGS) | ixl_sysctl_show_fw(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct ixl_pf *pf = (struct ixl_pf *)arg1; | struct ixl_pf *pf = (struct ixl_pf *)arg1; | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
char buf[32]; | struct sbuf *sbuf; | ||||
snprintf(buf, sizeof(buf), | sbuf = sbuf_new_for_sysctl(NULL, NULL, 128, req); | ||||
"f%d.%d a%d.%d n%02x.%02x e%08x", | ixl_nvm_version_str(hw, sbuf); | ||||
hw->aq.fw_maj_ver, hw->aq.fw_min_ver, | sbuf_finish(sbuf); | ||||
hw->aq.api_maj_ver, hw->aq.api_min_ver, | sbuf_delete(sbuf); | ||||
(hw->nvm.version & IXL_NVM_VERSION_HI_MASK) >> | |||||
IXL_NVM_VERSION_HI_SHIFT, | return 0; | ||||
(hw->nvm.version & IXL_NVM_VERSION_LO_MASK) >> | |||||
IXL_NVM_VERSION_LO_SHIFT, | |||||
hw->nvm.eetrack); | |||||
return (sysctl_handle_string(oidp, buf, strlen(buf), req)); | |||||
} | } | ||||
#ifdef IXL_DEBUG | |||||
static void | |||||
ixl_print_nvm_cmd(device_t dev, struct i40e_nvm_access *nvma) | |||||
{ | |||||
if ((nvma->command == I40E_NVM_READ) && | |||||
((nvma->config & 0xFF) == 0xF) && | |||||
(((nvma->config & 0xF00) >> 8) == 0xF) && | |||||
(nvma->offset == 0) && | |||||
(nvma->data_size == 1)) { | |||||
// device_printf(dev, "- Get Driver Status Command\n"); | |||||
} | |||||
else if (nvma->command == I40E_NVM_READ) { | |||||
} | |||||
else { | |||||
switch (nvma->command) { | |||||
case 0xB: | |||||
device_printf(dev, "- command: I40E_NVM_READ\n"); | |||||
break; | |||||
case 0xC: | |||||
device_printf(dev, "- command: I40E_NVM_WRITE\n"); | |||||
break; | |||||
default: | |||||
device_printf(dev, "- command: unknown 0x%08x\n", nvma->command); | |||||
break; | |||||
} | |||||
device_printf(dev, "- config (ptr) : 0x%02x\n", nvma->config & 0xFF); | |||||
device_printf(dev, "- config (flags): 0x%01x\n", (nvma->config & 0xF00) >> 8); | |||||
device_printf(dev, "- offset : 0x%08x\n", nvma->offset); | |||||
device_printf(dev, "- data_s : 0x%08x\n", nvma->data_size); | |||||
} | |||||
} | |||||
#endif | |||||
static int | |||||
ixl_handle_nvmupd_cmd(struct ixl_pf *pf, struct ifdrv *ifd) | |||||
{ | |||||
struct i40e_hw *hw = &pf->hw; | |||||
struct i40e_nvm_access *nvma; | |||||
device_t dev = pf->dev; | |||||
enum i40e_status_code status = 0; | |||||
int perrno; | |||||
DEBUGFUNC("ixl_handle_nvmupd_cmd"); | |||||
/* Sanity checks */ | |||||
if (ifd->ifd_len < sizeof(struct i40e_nvm_access) || | |||||
ifd->ifd_data == NULL) { | |||||
device_printf(dev, "%s: incorrect ifdrv length or data pointer\n", | |||||
__func__); | |||||
device_printf(dev, "%s: ifdrv length: %lu, sizeof(struct i40e_nvm_access): %lu\n", | |||||
__func__, ifd->ifd_len, sizeof(struct i40e_nvm_access)); | |||||
device_printf(dev, "%s: data pointer: %p\n", __func__, | |||||
ifd->ifd_data); | |||||
return (EINVAL); | |||||
} | |||||
nvma = (struct i40e_nvm_access *)ifd->ifd_data; | |||||
#ifdef IXL_DEBUG | |||||
ixl_print_nvm_cmd(dev, nvma); | |||||
#endif | |||||
if (pf->state & IXL_PF_STATE_EMPR_RESETTING) { | |||||
int count = 0; | |||||
while (count++ < 100) { | |||||
i40e_msec_delay(100); | |||||
if (!(pf->state & IXL_PF_STATE_EMPR_RESETTING)) | |||||
break; | |||||
} | |||||
} | |||||
if (!(pf->state & IXL_PF_STATE_EMPR_RESETTING)) { | |||||
IXL_PF_LOCK(pf); | |||||
status = i40e_nvmupd_command(hw, nvma, nvma->data, &perrno); | |||||
IXL_PF_UNLOCK(pf); | |||||
} else { | |||||
perrno = -EBUSY; | |||||
} | |||||
if (status) | |||||
device_printf(dev, "i40e_nvmupd_command status %d, perrno %d\n", | |||||
status, perrno); | |||||
/* | |||||
* -EPERM is actually ERESTART, which the kernel interprets as it needing | |||||
* to run this ioctl again. So use -EACCES for -EPERM instead. | |||||
*/ | |||||
if (perrno == -EPERM) | |||||
return (-EACCES); | |||||
else | |||||
return (perrno); | |||||
} | |||||
#ifdef IXL_DEBUG_SYSCTL | #ifdef IXL_DEBUG_SYSCTL | ||||
static int | static int | ||||
ixl_sysctl_link_status(SYSCTL_HANDLER_ARGS) | ixl_sysctl_link_status(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct ixl_pf *pf = (struct ixl_pf *)arg1; | struct ixl_pf *pf = (struct ixl_pf *)arg1; | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
struct i40e_link_status link_status; | struct i40e_link_status link_status; | ||||
char buf[512]; | char buf[512]; | ||||
enum i40e_status_code aq_error = 0; | enum i40e_status_code aq_error = 0; | ||||
aq_error = i40e_aq_get_link_info(hw, TRUE, &link_status, NULL); | aq_error = i40e_aq_get_link_info(hw, TRUE, &link_status, NULL); | ||||
if (aq_error) { | if (aq_error) { | ||||
printf("i40e_aq_get_link_info() error %d\n", aq_error); | printf("i40e_aq_get_link_info() error %d\n", aq_error); | ||||
return (EPERM); | return (EPERM); | ||||
} | } | ||||
sprintf(buf, "\n" | sprintf(buf, "\n" | ||||
"PHY Type : %#04x\n" | "PHY Type : %#04x\n" | ||||
"Speed : %#04x\n" | "Speed : %#04x\n" | ||||
"Link info: %#04x\n" | "Link info: %#04x\n" | ||||
"AN info : %#04x\n" | "AN info : %#04x\n" | ||||
"Ext info : %#04x", | "Ext info : %#04x\n" | ||||
"Max Frame: %d\n" | |||||
"Pacing : %#04x\n" | |||||
"CRC En? : %d", | |||||
link_status.phy_type, link_status.link_speed, | link_status.phy_type, link_status.link_speed, | ||||
link_status.link_info, link_status.an_info, | link_status.link_info, link_status.an_info, | ||||
link_status.ext_info); | link_status.ext_info, link_status.max_frame_size, | ||||
link_status.pacing, link_status.crc_enable); | |||||
return (sysctl_handle_string(oidp, buf, strlen(buf), req)); | return (sysctl_handle_string(oidp, buf, strlen(buf), req)); | ||||
} | } | ||||
static int | static int | ||||
ixl_sysctl_phy_abilities(SYSCTL_HANDLER_ARGS) | ixl_sysctl_phy_abilities(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct ixl_pf *pf = (struct ixl_pf *)arg1; | struct ixl_pf *pf = (struct ixl_pf *)arg1; | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
const struct i40e_aqc_switch_resource_alloc_element_resp *one, *two; | const struct i40e_aqc_switch_resource_alloc_element_resp *one, *two; | ||||
one = (const struct i40e_aqc_switch_resource_alloc_element_resp *)a; | one = (const struct i40e_aqc_switch_resource_alloc_element_resp *)a; | ||||
two = (const struct i40e_aqc_switch_resource_alloc_element_resp *)b; | two = (const struct i40e_aqc_switch_resource_alloc_element_resp *)b; | ||||
return ((int)one->resource_type - (int)two->resource_type); | return ((int)one->resource_type - (int)two->resource_type); | ||||
} | } | ||||
/* | |||||
* Longest string length: 25 | |||||
*/ | |||||
static char * | |||||
ixl_switch_res_type_string(u8 type) | |||||
{ | |||||
static char * ixl_switch_res_type_strings[0x14] = { | |||||
"VEB", | |||||
"VSI", | |||||
"Perfect Match MAC address", | |||||
"S-tag", | |||||
"(Reserved)", | |||||
"Multicast hash entry", | |||||
"Unicast hash entry", | |||||
"VLAN", | |||||
"VSI List entry", | |||||
"(Reserved)", | |||||
"VLAN Statistic Pool", | |||||
"Mirror Rule", | |||||
"Queue Set", | |||||
"Inner VLAN Forward filter", | |||||
"(Reserved)", | |||||
"Inner MAC", | |||||
"IP", | |||||
"GRE/VN1 Key", | |||||
"VN2 Key", | |||||
"Tunneling Port" | |||||
}; | |||||
if (type < 0x14) | |||||
return ixl_switch_res_type_strings[type]; | |||||
else | |||||
return "(Reserved)"; | |||||
} | |||||
static int | static int | ||||
ixl_sysctl_hw_res_alloc(SYSCTL_HANDLER_ARGS) | ixl_sysctl_hw_res_alloc(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct ixl_pf *pf = (struct ixl_pf *)arg1; | struct ixl_pf *pf = (struct ixl_pf *)arg1; | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
device_t dev = pf->dev; | device_t dev = pf->dev; | ||||
struct sbuf *buf; | struct sbuf *buf; | ||||
int error = 0; | int error = 0; | ||||
u8 num_entries; | u8 num_entries; | ||||
struct i40e_aqc_switch_resource_alloc_element_resp resp[IXL_SW_RES_SIZE]; | struct i40e_aqc_switch_resource_alloc_element_resp resp[IXL_SW_RES_SIZE]; | ||||
buf = sbuf_new_for_sysctl(NULL, NULL, 0, req); | buf = sbuf_new_for_sysctl(NULL, NULL, 128, req); | ||||
if (!buf) { | if (!buf) { | ||||
device_printf(dev, "Could not allocate sbuf for output.\n"); | device_printf(dev, "Could not allocate sbuf for output.\n"); | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
bzero(resp, sizeof(resp)); | bzero(resp, sizeof(resp)); | ||||
error = i40e_aq_get_switch_resource_alloc(hw, &num_entries, | error = i40e_aq_get_switch_resource_alloc(hw, &num_entries, | ||||
resp, | resp, | ||||
Show All 10 Lines | ixl_sysctl_hw_res_alloc(SYSCTL_HANDLER_ARGS) | ||||
/* Sort entries by type for display */ | /* Sort entries by type for display */ | ||||
qsort(resp, num_entries, | qsort(resp, num_entries, | ||||
sizeof(struct i40e_aqc_switch_resource_alloc_element_resp), | sizeof(struct i40e_aqc_switch_resource_alloc_element_resp), | ||||
&ixl_res_alloc_cmp); | &ixl_res_alloc_cmp); | ||||
sbuf_cat(buf, "\n"); | sbuf_cat(buf, "\n"); | ||||
sbuf_printf(buf, "# of entries: %d\n", num_entries); | sbuf_printf(buf, "# of entries: %d\n", num_entries); | ||||
sbuf_printf(buf, | sbuf_printf(buf, | ||||
"Type | Guaranteed | Total | Used | Un-allocated\n" | " Type | Guaranteed | Total | Used | Un-allocated\n" | ||||
" | (this) | (all) | (this) | (all) \n"); | " | (this) | (all) | (this) | (all) \n"); | ||||
for (int i = 0; i < num_entries; i++) { | for (int i = 0; i < num_entries; i++) { | ||||
sbuf_printf(buf, | sbuf_printf(buf, | ||||
"%#4x | %10d %5d %6d %12d", | "%25s | %10d %5d %6d %12d", | ||||
resp[i].resource_type, | ixl_switch_res_type_string(resp[i].resource_type), | ||||
resp[i].guaranteed, | resp[i].guaranteed, | ||||
resp[i].total, | resp[i].total, | ||||
resp[i].used, | resp[i].used, | ||||
resp[i].total_unalloced); | resp[i].total_unalloced); | ||||
if (i < num_entries - 1) | if (i < num_entries - 1) | ||||
sbuf_cat(buf, "\n"); | sbuf_cat(buf, "\n"); | ||||
} | } | ||||
error = sbuf_finish(buf); | error = sbuf_finish(buf); | ||||
sbuf_delete(buf); | if (error) | ||||
device_printf(dev, "Error finishing sbuf: %d\n", error); | |||||
return (error); | sbuf_delete(buf); | ||||
return error; | |||||
} | } | ||||
/* | /* | ||||
** Caller must init and delete sbuf; this function will clear and | ** Caller must init and delete sbuf; this function will clear and | ||||
** finish it for caller. | ** finish it for caller. | ||||
** | |||||
** XXX: Cannot use the SEID for this, since there is no longer a | |||||
** fixed mapping between SEID and element type. | |||||
*/ | */ | ||||
static char * | static char * | ||||
ixl_switch_element_string(struct sbuf *s, u16 seid, bool uplink) | ixl_switch_element_string(struct sbuf *s, | ||||
struct i40e_aqc_switch_config_element_resp *element) | |||||
{ | { | ||||
sbuf_clear(s); | sbuf_clear(s); | ||||
if (seid == 0 && uplink) | switch (element->element_type) { | ||||
sbuf_cat(s, "Network"); | case I40E_AQ_SW_ELEM_TYPE_MAC: | ||||
else if (seid == 0) | sbuf_printf(s, "MAC %3d", element->element_info); | ||||
sbuf_cat(s, "Host"); | break; | ||||
else if (seid == 1) | case I40E_AQ_SW_ELEM_TYPE_PF: | ||||
sbuf_printf(s, "PF %3d", element->element_info); | |||||
break; | |||||
case I40E_AQ_SW_ELEM_TYPE_VF: | |||||
sbuf_printf(s, "VF %3d", element->element_info); | |||||
break; | |||||
case I40E_AQ_SW_ELEM_TYPE_EMP: | |||||
sbuf_cat(s, "EMP"); | sbuf_cat(s, "EMP"); | ||||
else if (seid <= 5) | break; | ||||
sbuf_printf(s, "MAC %d", seid - 2); | case I40E_AQ_SW_ELEM_TYPE_BMC: | ||||
else if (seid <= 15) | sbuf_cat(s, "BMC"); | ||||
sbuf_cat(s, "Reserved"); | break; | ||||
else if (seid <= 31) | case I40E_AQ_SW_ELEM_TYPE_PV: | ||||
sbuf_printf(s, "PF %d", seid - 16); | sbuf_cat(s, "PV"); | ||||
else if (seid <= 159) | break; | ||||
sbuf_printf(s, "VF %d", seid - 32); | case I40E_AQ_SW_ELEM_TYPE_VEB: | ||||
else if (seid <= 287) | sbuf_cat(s, "VEB"); | ||||
sbuf_cat(s, "Reserved"); | break; | ||||
else if (seid <= 511) | case I40E_AQ_SW_ELEM_TYPE_PA: | ||||
sbuf_cat(s, "Other"); // for other structures | sbuf_cat(s, "PA"); | ||||
else if (seid <= 895) | break; | ||||
sbuf_printf(s, "VSI %d", seid - 512); | case I40E_AQ_SW_ELEM_TYPE_VSI: | ||||
else if (seid <= 1023) | sbuf_printf(s, "VSI %3d", element->element_info); | ||||
sbuf_printf(s, "Reserved"); | break; | ||||
else | default: | ||||
sbuf_cat(s, "Invalid"); | sbuf_cat(s, "?"); | ||||
break; | |||||
} | |||||
sbuf_finish(s); | sbuf_finish(s); | ||||
return sbuf_data(s); | return sbuf_data(s); | ||||
} | } | ||||
static int | static int | ||||
ixl_sysctl_switch_config(SYSCTL_HANDLER_ARGS) | ixl_sysctl_switch_config(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct ixl_pf *pf = (struct ixl_pf *)arg1; | struct ixl_pf *pf = (struct ixl_pf *)arg1; | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
device_t dev = pf->dev; | device_t dev = pf->dev; | ||||
struct sbuf *buf; | struct sbuf *buf; | ||||
struct sbuf *nmbuf; | struct sbuf *nmbuf; | ||||
int error = 0; | int error = 0; | ||||
u16 next = 0; | |||||
u8 aq_buf[I40E_AQ_LARGE_BUF]; | u8 aq_buf[I40E_AQ_LARGE_BUF]; | ||||
u16 next = 0; | |||||
struct i40e_aqc_get_switch_config_resp *sw_config; | struct i40e_aqc_get_switch_config_resp *sw_config; | ||||
sw_config = (struct i40e_aqc_get_switch_config_resp *)aq_buf; | sw_config = (struct i40e_aqc_get_switch_config_resp *)aq_buf; | ||||
buf = sbuf_new_for_sysctl(NULL, NULL, 0, req); | buf = sbuf_new_for_sysctl(NULL, NULL, 128, req); | ||||
if (!buf) { | if (!buf) { | ||||
device_printf(dev, "Could not allocate sbuf for sysctl output.\n"); | device_printf(dev, "Could not allocate sbuf for sysctl output.\n"); | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
error = i40e_aq_get_switch_config(hw, sw_config, | error = i40e_aq_get_switch_config(hw, sw_config, | ||||
sizeof(aq_buf), &next, NULL); | sizeof(aq_buf), &next, NULL); | ||||
if (error) { | if (error) { | ||||
device_printf(dev, | device_printf(dev, | ||||
"%s: aq_get_switch_config() error %d, aq error %d\n", | "%s: aq_get_switch_config() error %d, aq error %d\n", | ||||
__func__, error, hw->aq.asq_last_status); | __func__, error, hw->aq.asq_last_status); | ||||
sbuf_delete(buf); | sbuf_delete(buf); | ||||
return error; | return error; | ||||
} | } | ||||
if (next) | |||||
device_printf(dev, "%s: TODO: get more config with SEID %d\n", | |||||
__func__, next); | |||||
nmbuf = sbuf_new_auto(); | nmbuf = sbuf_new_auto(); | ||||
if (!nmbuf) { | if (!nmbuf) { | ||||
device_printf(dev, "Could not allocate sbuf for name output.\n"); | device_printf(dev, "Could not allocate sbuf for name output.\n"); | ||||
sbuf_delete(buf); | |||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
sbuf_cat(buf, "\n"); | sbuf_cat(buf, "\n"); | ||||
// Assuming <= 255 elements in switch | // Assuming <= 255 elements in switch | ||||
sbuf_printf(buf, "# of elements: %d\n", sw_config->header.num_reported); | sbuf_printf(buf, "# of reported elements: %d\n", sw_config->header.num_reported); | ||||
sbuf_printf(buf, "total # of elements: %d\n", sw_config->header.num_total); | |||||
/* Exclude: | /* Exclude: | ||||
** Revision -- all elements are revision 1 for now | ** Revision -- all elements are revision 1 for now | ||||
*/ | */ | ||||
sbuf_printf(buf, | sbuf_printf(buf, | ||||
"SEID ( Name ) | Uplink | Downlink | Conn Type\n" | "SEID ( Name ) | Uplink | Downlink | Conn Type\n" | ||||
" | | | (uplink)\n"); | " | | | (uplink)\n"); | ||||
for (int i = 0; i < sw_config->header.num_reported; i++) { | for (int i = 0; i < sw_config->header.num_reported; i++) { | ||||
// "%4d (%8s) | %8s %8s %#8x", | // "%4d (%8s) | %8s %8s %#8x", | ||||
sbuf_printf(buf, "%4d", sw_config->element[i].seid); | sbuf_printf(buf, "%4d", sw_config->element[i].seid); | ||||
sbuf_cat(buf, " "); | sbuf_cat(buf, " "); | ||||
sbuf_printf(buf, "(%8s)", ixl_switch_element_string(nmbuf, | sbuf_printf(buf, "(%8s)", ixl_switch_element_string(nmbuf, | ||||
sw_config->element[i].seid, false)); | &sw_config->element[i])); | ||||
sbuf_cat(buf, " | "); | sbuf_cat(buf, " | "); | ||||
sbuf_printf(buf, "%8s", ixl_switch_element_string(nmbuf, | sbuf_printf(buf, "%8d", sw_config->element[i].uplink_seid); | ||||
sw_config->element[i].uplink_seid, true)); | |||||
sbuf_cat(buf, " "); | sbuf_cat(buf, " "); | ||||
sbuf_printf(buf, "%8s", ixl_switch_element_string(nmbuf, | sbuf_printf(buf, "%8d", sw_config->element[i].downlink_seid); | ||||
sw_config->element[i].downlink_seid, false)); | |||||
sbuf_cat(buf, " "); | sbuf_cat(buf, " "); | ||||
sbuf_printf(buf, "%#8x", sw_config->element[i].connection_type); | sbuf_printf(buf, "%#8x", sw_config->element[i].connection_type); | ||||
if (i < sw_config->header.num_reported - 1) | if (i < sw_config->header.num_reported - 1) | ||||
sbuf_cat(buf, "\n"); | sbuf_cat(buf, "\n"); | ||||
} | } | ||||
sbuf_delete(nmbuf); | sbuf_delete(nmbuf); | ||||
error = sbuf_finish(buf); | error = sbuf_finish(buf); | ||||
if (error) | |||||
device_printf(dev, "Error finishing sbuf: %d\n", error); | |||||
sbuf_delete(buf); | sbuf_delete(buf); | ||||
return (error); | return (error); | ||||
} | } | ||||
#endif /* IXL_DEBUG_SYSCTL */ | |||||
static int | |||||
ixl_debug_info(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct ixl_pf *pf; | |||||
int error, input = 0; | |||||
error = sysctl_handle_int(oidp, &input, 0, req); | |||||
if (error || !req->newptr) | |||||
return (error); | |||||
if (input == 1) { | |||||
pf = (struct ixl_pf *)arg1; | |||||
ixl_print_debug_info(pf); | |||||
} | |||||
return (error); | |||||
} | |||||
static void | |||||
ixl_print_debug_info(struct ixl_pf *pf) | |||||
{ | |||||
struct i40e_hw *hw = &pf->hw; | |||||
struct ixl_vsi *vsi = &pf->vsi; | |||||
struct ixl_queue *que = vsi->queues; | |||||
struct rx_ring *rxr = &que->rxr; | |||||
struct tx_ring *txr = &que->txr; | |||||
u32 reg; | |||||
printf("Queue irqs = %jx\n", (uintmax_t)que->irqs); | |||||
printf("AdminQ irqs = %jx\n", (uintmax_t)pf->admin_irq); | |||||
printf("RX next check = %x\n", rxr->next_check); | |||||
printf("RX not ready = %jx\n", (uintmax_t)rxr->not_done); | |||||
printf("RX packets = %jx\n", (uintmax_t)rxr->rx_packets); | |||||
printf("TX desc avail = %x\n", txr->avail); | |||||
reg = rd32(hw, I40E_GLV_GORCL(0xc)); | |||||
printf("RX Bytes = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLPRT_GORCL(hw->port)); | |||||
printf("Port RX Bytes = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLV_RDPC(0xc)); | |||||
printf("RX discard = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLPRT_RDPC(hw->port)); | |||||
printf("Port RX discard = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLV_TEPC(0xc)); | |||||
printf("TX errors = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLV_GOTCL(0xc)); | |||||
printf("TX Bytes = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLPRT_RUC(hw->port)); | |||||
printf("RX undersize = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLPRT_RFC(hw->port)); | |||||
printf("RX fragments = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLPRT_ROC(hw->port)); | |||||
printf("RX oversize = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLPRT_RLEC(hw->port)); | |||||
printf("RX length error = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLPRT_MRFC(hw->port)); | |||||
printf("mac remote fault = %x\n", reg); | |||||
reg = rd32(hw, I40E_GLPRT_MLFC(hw->port)); | |||||
printf("mac local fault = %x\n", reg); | |||||
} | |||||
#endif /* IXL_DEBUG_SYSCTL */ | |||||
#ifdef PCI_IOV | #ifdef PCI_IOV | ||||
static int | static int | ||||
ixl_vf_alloc_vsi(struct ixl_pf *pf, struct ixl_vf *vf) | ixl_vf_alloc_vsi(struct ixl_pf *pf, struct ixl_vf *vf) | ||||
{ | { | ||||
struct i40e_hw *hw; | struct i40e_hw *hw; | ||||
struct ixl_vsi *vsi; | struct ixl_vsi *vsi; | ||||
struct i40e_vsi_context vsi_ctx; | struct i40e_vsi_context vsi_ctx; | ||||
int i; | int i; | ||||
Show All 14 Lines | ixl_vf_alloc_vsi(struct ixl_pf *pf, struct ixl_vf *vf) | ||||
vsi_ctx.info.valid_sections = htole16(I40E_AQ_VSI_PROP_SWITCH_VALID); | vsi_ctx.info.valid_sections = htole16(I40E_AQ_VSI_PROP_SWITCH_VALID); | ||||
vsi_ctx.info.switch_id = htole16(0); | vsi_ctx.info.switch_id = htole16(0); | ||||
vsi_ctx.info.valid_sections |= htole16(I40E_AQ_VSI_PROP_SECURITY_VALID); | vsi_ctx.info.valid_sections |= htole16(I40E_AQ_VSI_PROP_SECURITY_VALID); | ||||
vsi_ctx.info.sec_flags = 0; | vsi_ctx.info.sec_flags = 0; | ||||
if (vf->vf_flags & VF_FLAG_MAC_ANTI_SPOOF) | if (vf->vf_flags & VF_FLAG_MAC_ANTI_SPOOF) | ||||
vsi_ctx.info.sec_flags |= I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK; | vsi_ctx.info.sec_flags |= I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK; | ||||
/* TODO: If a port VLAN is set, then this needs to be changed */ | |||||
vsi_ctx.info.valid_sections |= htole16(I40E_AQ_VSI_PROP_VLAN_VALID); | vsi_ctx.info.valid_sections |= htole16(I40E_AQ_VSI_PROP_VLAN_VALID); | ||||
vsi_ctx.info.port_vlan_flags = I40E_AQ_VSI_PVLAN_MODE_ALL | | vsi_ctx.info.port_vlan_flags = I40E_AQ_VSI_PVLAN_MODE_ALL | | ||||
I40E_AQ_VSI_PVLAN_EMOD_NOTHING; | I40E_AQ_VSI_PVLAN_EMOD_NOTHING; | ||||
vsi_ctx.info.valid_sections |= | vsi_ctx.info.valid_sections |= | ||||
htole16(I40E_AQ_VSI_PROP_QUEUE_MAP_VALID); | htole16(I40E_AQ_VSI_PROP_QUEUE_MAP_VALID); | ||||
vsi_ctx.info.mapping_flags = htole16(I40E_AQ_VSI_QUE_MAP_NONCONTIG); | vsi_ctx.info.mapping_flags = htole16(I40E_AQ_VSI_QUE_MAP_NONCONTIG); | ||||
first_queue = vsi->num_queues + vf->vf_num * IXLV_MAX_QUEUES; | first_queue = vsi->num_queues + vf->vf_num * IXLV_MAX_QUEUES; | ||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | ixl_vf_map_vsi_queue(struct i40e_hw *hw, struct ixl_vf *vf, int qnum, | ||||
/* | /* | ||||
* Two queues are mapped in a single register, so we have to do some | * Two queues are mapped in a single register, so we have to do some | ||||
* gymnastics to convert the queue number into a register index and | * gymnastics to convert the queue number into a register index and | ||||
* shift. | * shift. | ||||
*/ | */ | ||||
index = qnum / 2; | index = qnum / 2; | ||||
shift = (qnum % 2) * I40E_VSILAN_QTABLE_QINDEX_1_SHIFT; | shift = (qnum % 2) * I40E_VSILAN_QTABLE_QINDEX_1_SHIFT; | ||||
qtable = rd32(hw, I40E_VSILAN_QTABLE(index, vf->vsi.vsi_num)); | qtable = i40e_read_rx_ctl(hw, I40E_VSILAN_QTABLE(index, vf->vsi.vsi_num)); | ||||
qtable &= ~(I40E_VSILAN_QTABLE_QINDEX_0_MASK << shift); | qtable &= ~(I40E_VSILAN_QTABLE_QINDEX_0_MASK << shift); | ||||
qtable |= val << shift; | qtable |= val << shift; | ||||
wr32(hw, I40E_VSILAN_QTABLE(index, vf->vsi.vsi_num), qtable); | i40e_write_rx_ctl(hw, I40E_VSILAN_QTABLE(index, vf->vsi.vsi_num), qtable); | ||||
} | } | ||||
static void | static void | ||||
ixl_vf_map_queues(struct ixl_pf *pf, struct ixl_vf *vf) | ixl_vf_map_queues(struct ixl_pf *pf, struct ixl_vf *vf) | ||||
{ | { | ||||
struct i40e_hw *hw; | struct i40e_hw *hw; | ||||
uint32_t qtable; | uint32_t qtable; | ||||
int i; | int i; | ||||
hw = &pf->hw; | hw = &pf->hw; | ||||
/* | /* | ||||
* Contiguous mappings aren't actually supported by the hardware, | * Contiguous mappings aren't actually supported by the hardware, | ||||
* so we have to use non-contiguous mappings. | * so we have to use non-contiguous mappings. | ||||
*/ | */ | ||||
wr32(hw, I40E_VSILAN_QBASE(vf->vsi.vsi_num), | i40e_write_rx_ctl(hw, I40E_VSILAN_QBASE(vf->vsi.vsi_num), | ||||
I40E_VSILAN_QBASE_VSIQTABLE_ENA_MASK); | I40E_VSILAN_QBASE_VSIQTABLE_ENA_MASK); | ||||
wr32(hw, I40E_VPLAN_MAPENA(vf->vf_num), | wr32(hw, I40E_VPLAN_MAPENA(vf->vf_num), | ||||
I40E_VPLAN_MAPENA_TXRX_ENA_MASK); | I40E_VPLAN_MAPENA_TXRX_ENA_MASK); | ||||
for (i = 0; i < vf->vsi.num_queues; i++) { | for (i = 0; i < vf->vsi.num_queues; i++) { | ||||
qtable = (vf->vsi.first_queue + i) << | qtable = (vf->vsi.first_queue + i) << | ||||
I40E_VPLAN_QTABLE_QINDEX_SHIFT; | I40E_VPLAN_QTABLE_QINDEX_SHIFT; | ||||
▲ Show 20 Lines • Show All 197 Lines • ▼ Show 20 Lines | ixl_vc_opcode_str(uint16_t op) | ||||
default: | default: | ||||
return ("UNKNOWN"); | return ("UNKNOWN"); | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
ixl_vc_opcode_level(uint16_t opcode) | ixl_vc_opcode_level(uint16_t opcode) | ||||
{ | { | ||||
switch (opcode) { | switch (opcode) { | ||||
case I40E_VIRTCHNL_OP_GET_STATS: | case I40E_VIRTCHNL_OP_GET_STATS: | ||||
return (10); | return (10); | ||||
default: | default: | ||||
return (5); | return (5); | ||||
} | } | ||||
} | } | ||||
Show All 39 Lines | ixl_vf_version_msg(struct ixl_pf *pf, struct ixl_vf *vf, void *msg, | ||||
struct i40e_virtchnl_version_info reply; | struct i40e_virtchnl_version_info reply; | ||||
if (msg_size != sizeof(struct i40e_virtchnl_version_info)) { | if (msg_size != sizeof(struct i40e_virtchnl_version_info)) { | ||||
i40e_send_vf_nack(pf, vf, I40E_VIRTCHNL_OP_VERSION, | i40e_send_vf_nack(pf, vf, I40E_VIRTCHNL_OP_VERSION, | ||||
I40E_ERR_PARAM); | I40E_ERR_PARAM); | ||||
return; | return; | ||||
} | } | ||||
vf->version = ((struct i40e_virtchnl_version_info *)msg)->minor; | |||||
reply.major = I40E_VIRTCHNL_VERSION_MAJOR; | reply.major = I40E_VIRTCHNL_VERSION_MAJOR; | ||||
reply.minor = I40E_VIRTCHNL_VERSION_MINOR; | reply.minor = I40E_VIRTCHNL_VERSION_MINOR; | ||||
ixl_send_vf_msg(pf, vf, I40E_VIRTCHNL_OP_VERSION, I40E_SUCCESS, &reply, | ixl_send_vf_msg(pf, vf, I40E_VIRTCHNL_OP_VERSION, I40E_SUCCESS, &reply, | ||||
sizeof(reply)); | sizeof(reply)); | ||||
} | } | ||||
static void | static void | ||||
ixl_vf_reset_msg(struct ixl_pf *pf, struct ixl_vf *vf, void *msg, | ixl_vf_reset_msg(struct ixl_pf *pf, struct ixl_vf *vf, void *msg, | ||||
Show All 12 Lines | |||||
} | } | ||||
static void | static void | ||||
ixl_vf_get_resources_msg(struct ixl_pf *pf, struct ixl_vf *vf, void *msg, | ixl_vf_get_resources_msg(struct ixl_pf *pf, struct ixl_vf *vf, void *msg, | ||||
uint16_t msg_size) | uint16_t msg_size) | ||||
{ | { | ||||
struct i40e_virtchnl_vf_resource reply; | struct i40e_virtchnl_vf_resource reply; | ||||
if (msg_size != 0) { | if ((vf->version == 0 && msg_size != 0) || | ||||
(vf->version == 1 && msg_size != 4)) { | |||||
device_printf(pf->dev, "Invalid GET_VF_RESOURCES message size," | |||||
" for VF version %d.%d\n", I40E_VIRTCHNL_VERSION_MAJOR, | |||||
vf->version); | |||||
i40e_send_vf_nack(pf, vf, I40E_VIRTCHNL_OP_GET_VF_RESOURCES, | i40e_send_vf_nack(pf, vf, I40E_VIRTCHNL_OP_GET_VF_RESOURCES, | ||||
I40E_ERR_PARAM); | I40E_ERR_PARAM); | ||||
return; | return; | ||||
} | } | ||||
bzero(&reply, sizeof(reply)); | bzero(&reply, sizeof(reply)); | ||||
reply.vf_offload_flags = I40E_VIRTCHNL_VF_OFFLOAD_L2; | if (vf->version == I40E_VIRTCHNL_VERSION_MINOR_NO_VF_CAPS) | ||||
reply.vf_offload_flags = I40E_VIRTCHNL_VF_OFFLOAD_L2 | | |||||
I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG | | |||||
I40E_VIRTCHNL_VF_OFFLOAD_VLAN; | |||||
else | |||||
reply.vf_offload_flags = *(u32 *)msg; | |||||
reply.num_vsis = 1; | reply.num_vsis = 1; | ||||
reply.num_queue_pairs = vf->vsi.num_queues; | reply.num_queue_pairs = vf->vsi.num_queues; | ||||
reply.max_vectors = pf->hw.func_caps.num_msix_vectors_vf; | reply.max_vectors = pf->hw.func_caps.num_msix_vectors_vf; | ||||
reply.vsi_res[0].vsi_id = vf->vsi.vsi_num; | reply.vsi_res[0].vsi_id = vf->vsi.vsi_num; | ||||
reply.vsi_res[0].vsi_type = I40E_VSI_SRIOV; | reply.vsi_res[0].vsi_type = I40E_VSI_SRIOV; | ||||
reply.vsi_res[0].num_queue_pairs = vf->vsi.num_queues; | reply.vsi_res[0].num_queue_pairs = vf->vsi.num_queues; | ||||
memcpy(reply.vsi_res[0].default_mac_addr, vf->mac, ETHER_ADDR_LEN); | memcpy(reply.vsi_res[0].default_mac_addr, vf->mac, ETHER_ADDR_LEN); | ||||
▲ Show 20 Lines • Show All 876 Lines • ▼ Show 20 Lines | if (pf->vfs == NULL) { | ||||
error = ENOMEM; | error = ENOMEM; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
for (i = 0; i < num_vfs; i++) | for (i = 0; i < num_vfs; i++) | ||||
sysctl_ctx_init(&pf->vfs[i].ctx); | sysctl_ctx_init(&pf->vfs[i].ctx); | ||||
ret = i40e_aq_add_veb(hw, pf_vsi->uplink_seid, pf_vsi->seid, | ret = i40e_aq_add_veb(hw, pf_vsi->uplink_seid, pf_vsi->seid, | ||||
1, FALSE, FALSE, &pf->veb_seid, NULL); | 1, FALSE, &pf->veb_seid, FALSE, NULL); | ||||
if (ret != I40E_SUCCESS) { | if (ret != I40E_SUCCESS) { | ||||
error = ixl_adminq_err_to_errno(hw->aq.asq_last_status); | error = ixl_adminq_err_to_errno(hw->aq.asq_last_status); | ||||
device_printf(dev, "add_veb failed; code=%d error=%d", ret, | device_printf(dev, "add_veb failed; code=%d error=%d", ret, | ||||
error); | error); | ||||
goto fail; | goto fail; | ||||
} | } | ||||
ixl_configure_msix(pf); | // TODO: [Configure MSI-X here] | ||||
ixl_enable_adminq(hw); | ixl_enable_adminq(hw); | ||||
pf->num_vfs = num_vfs; | pf->num_vfs = num_vfs; | ||||
IXL_PF_UNLOCK(pf); | IXL_PF_UNLOCK(pf); | ||||
return (0); | return (0); | ||||
fail: | fail: | ||||
free(pf->vfs, M_IXL); | free(pf->vfs, M_IXL); | ||||
Show All 23 Lines | if (pf->vfs[i].vsi.seid != 0) | ||||
i40e_aq_delete_element(hw, pf->vfs[i].vsi.seid, NULL); | i40e_aq_delete_element(hw, pf->vfs[i].vsi.seid, NULL); | ||||
} | } | ||||
if (pf->veb_seid != 0) { | if (pf->veb_seid != 0) { | ||||
i40e_aq_delete_element(hw, pf->veb_seid, NULL); | i40e_aq_delete_element(hw, pf->veb_seid, NULL); | ||||
pf->veb_seid = 0; | pf->veb_seid = 0; | ||||
} | } | ||||
#if __FreeBSD_version > 1100022 | |||||
if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) | if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) | ||||
#else | |||||
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) | |||||
#endif | |||||
ixl_disable_intr(vsi); | ixl_disable_intr(vsi); | ||||
vfs = pf->vfs; | vfs = pf->vfs; | ||||
num_vfs = pf->num_vfs; | num_vfs = pf->num_vfs; | ||||
pf->vfs = NULL; | pf->vfs = NULL; | ||||
pf->num_vfs = 0; | pf->num_vfs = 0; | ||||
IXL_PF_UNLOCK(pf); | IXL_PF_UNLOCK(pf); | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | if (nvlist_exists_binary(params, "mac-addr")) { | ||||
*/ | */ | ||||
vf->vf_flags |= VF_FLAG_SET_MAC_CAP; | vf->vf_flags |= VF_FLAG_SET_MAC_CAP; | ||||
if (nvlist_get_bool(params, "mac-anti-spoof")) | if (nvlist_get_bool(params, "mac-anti-spoof")) | ||||
vf->vf_flags |= VF_FLAG_MAC_ANTI_SPOOF; | vf->vf_flags |= VF_FLAG_MAC_ANTI_SPOOF; | ||||
if (nvlist_get_bool(params, "allow-promisc")) | if (nvlist_get_bool(params, "allow-promisc")) | ||||
vf->vf_flags |= VF_FLAG_PROMISC_CAP; | vf->vf_flags |= VF_FLAG_PROMISC_CAP; | ||||
/* TODO: Get VLAN that PF has set for the VF */ | |||||
vf->vf_flags |= VF_FLAG_VLAN_CAP; | vf->vf_flags |= VF_FLAG_VLAN_CAP; | ||||
ixl_reset_vf(pf, vf); | ixl_reset_vf(pf, vf); | ||||
out: | out: | ||||
IXL_PF_UNLOCK(pf); | IXL_PF_UNLOCK(pf); | ||||
if (error == 0) { | if (error == 0) { | ||||
snprintf(sysctl_name, sizeof(sysctl_name), "vf%d", vfnum); | snprintf(sysctl_name, sizeof(sysctl_name), "vf%d", vfnum); | ||||
ixl_add_vsi_sysctls(pf, &vf->vsi, &vf->ctx, sysctl_name); | ixl_add_vsi_sysctls(pf, &vf->vsi, &vf->ctx, sysctl_name); | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
#endif /* PCI_IOV */ | #endif /* PCI_IOV */ |
Possible spelling error. You wrote 'reenable', but did you mean 're-enable'?