Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ixl/ixl_pf_main.c
Show All 40 Lines | |||||
#ifdef IXL_IW | #ifdef IXL_IW | ||||
#include "ixl_iw.h" | #include "ixl_iw.h" | ||||
#include "ixl_iw_int.h" | #include "ixl_iw_int.h" | ||||
#endif | #endif | ||||
static u8 ixl_convert_sysctl_aq_link_speed(u8, bool); | static u8 ixl_convert_sysctl_aq_link_speed(u8, bool); | ||||
static void ixl_sbuf_print_bytes(struct sbuf *, u8 *, int, int, bool); | static void ixl_sbuf_print_bytes(struct sbuf *, u8 *, int, int, bool); | ||||
static void ixl_del_default_hw_filters(struct ixl_vsi *); | |||||
/* Sysctls */ | /* Sysctls */ | ||||
static int ixl_sysctl_set_flowcntl(SYSCTL_HANDLER_ARGS); | static int ixl_sysctl_set_flowcntl(SYSCTL_HANDLER_ARGS); | ||||
static int ixl_sysctl_set_advertise(SYSCTL_HANDLER_ARGS); | static int ixl_sysctl_set_advertise(SYSCTL_HANDLER_ARGS); | ||||
static int ixl_sysctl_supported_speeds(SYSCTL_HANDLER_ARGS); | static int ixl_sysctl_supported_speeds(SYSCTL_HANDLER_ARGS); | ||||
static int ixl_sysctl_current_speed(SYSCTL_HANDLER_ARGS); | static int ixl_sysctl_current_speed(SYSCTL_HANDLER_ARGS); | ||||
static int ixl_sysctl_show_fw(SYSCTL_HANDLER_ARGS); | static int ixl_sysctl_show_fw(SYSCTL_HANDLER_ARGS); | ||||
static int ixl_sysctl_unallocated_queues(SYSCTL_HANDLER_ARGS); | static int ixl_sysctl_unallocated_queues(SYSCTL_HANDLER_ARGS); | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | |||||
static char *ixl_fec_string[3] = { | static char *ixl_fec_string[3] = { | ||||
"CL108 RS-FEC", | "CL108 RS-FEC", | ||||
"CL74 FC-FEC/BASE-R", | "CL74 FC-FEC/BASE-R", | ||||
"None" | "None" | ||||
}; | }; | ||||
MALLOC_DEFINE(M_IXL, "ixl", "ixl driver allocations"); | MALLOC_DEFINE(M_IXL, "ixl", "ixl driver allocations"); | ||||
void | |||||
ixl_debug_core(struct ixl_pf *pf, enum ixl_dbg_mask mask, char *fmt, ...) | |||||
{ | |||||
va_list args; | |||||
if (!(mask & pf->dbg_mask)) | |||||
return; | |||||
/* Re-implement device_printf() */ | |||||
device_print_prettyname(pf->dev); | |||||
va_start(args, fmt); | |||||
vprintf(fmt, args); | |||||
va_end(args); | |||||
} | |||||
/* | /* | ||||
** Put the FW, API, NVM, EEtrackID, and OEM version information into a string | ** Put the FW, API, NVM, EEtrackID, and OEM version information into a string | ||||
*/ | */ | ||||
void | void | ||||
ixl_nvm_version_str(struct i40e_hw *hw, struct sbuf *buf) | ixl_nvm_version_str(struct i40e_hw *hw, struct sbuf *buf) | ||||
{ | { | ||||
u8 oem_ver = (u8)(hw->nvm.oem_ver >> 24); | u8 oem_ver = (u8)(hw->nvm.oem_ver >> 24); | ||||
u16 oem_build = (u16)((hw->nvm.oem_ver >> 16) & 0xFFFF); | u16 oem_build = (u16)((hw->nvm.oem_ver >> 16) & 0xFFFF); | ||||
▲ Show 20 Lines • Show All 210 Lines • ▼ Show 20 Lines | ixl_teardown_hw_structs(struct ixl_pf *pf) | ||||
/* Shutdown admin queue */ | /* Shutdown admin queue */ | ||||
ixl_disable_intr0(hw); | ixl_disable_intr0(hw); | ||||
status = i40e_shutdown_adminq(hw); | status = i40e_shutdown_adminq(hw); | ||||
if (status) | if (status) | ||||
device_printf(dev, | device_printf(dev, | ||||
"init: Admin Queue shutdown failure; status %s\n", | "init: Admin Queue shutdown failure; status %s\n", | ||||
i40e_stat_str(hw, status)); | i40e_stat_str(hw, status)); | ||||
ixl_pf_qmgr_release(&pf->qmgr, &pf->qtag); | |||||
err_out: | err_out: | ||||
return (status); | return (status); | ||||
} | } | ||||
int | int | ||||
ixl_reset(struct ixl_pf *pf) | ixl_reset(struct ixl_pf *pf) | ||||
{ | { | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | #if 0 | ||||
if (pf->msix > 1) { | if (pf->msix > 1) { | ||||
ixl_configure_intr0_msix(pf); | ixl_configure_intr0_msix(pf); | ||||
ixl_enable_intr0(hw); | ixl_enable_intr0(hw); | ||||
} | } | ||||
err_out: | err_out: | ||||
return (error); | return (error); | ||||
#endif | #endif | ||||
// TODO: Fix second parameter | ixl_rebuild_hw_structs_after_reset(pf); | ||||
ixl_rebuild_hw_structs_after_reset(pf, false); | |||||
/* The PF reset should have cleared any critical errors */ | /* The PF reset should have cleared any critical errors */ | ||||
atomic_clear_32(&pf->state, IXL_PF_STATE_PF_CRIT_ERR); | atomic_clear_32(&pf->state, IXL_PF_STATE_PF_CRIT_ERR); | ||||
atomic_clear_32(&pf->state, IXL_PF_STATE_PF_RESET_REQ); | atomic_clear_32(&pf->state, IXL_PF_STATE_PF_RESET_REQ); | ||||
reg = rd32(hw, I40E_PFINT_ICR0_ENA); | reg = rd32(hw, I40E_PFINT_ICR0_ENA); | ||||
reg |= IXL_ICR0_CRIT_ERR_MASK; | reg |= IXL_ICR0_CRIT_ERR_MASK; | ||||
wr32(hw, I40E_PFINT_ICR0_ENA, reg); | wr32(hw, I40E_PFINT_ICR0_ENA, reg); | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | |||||
/********************************************************************* | /********************************************************************* | ||||
* | * | ||||
* MSIX VSI Interrupt Service routine | * MSIX VSI Interrupt Service routine | ||||
* | * | ||||
**********************************************************************/ | **********************************************************************/ | ||||
int | int | ||||
ixl_msix_que(void *arg) | ixl_msix_que(void *arg) | ||||
{ | { | ||||
struct ixl_rx_queue *que = arg; | struct ixl_rx_queue *rx_que = arg; | ||||
++que->irqs; | ++rx_que->irqs; | ||||
ixl_set_queue_rx_itr(que); | ixl_set_queue_rx_itr(rx_que); | ||||
// ixl_set_queue_tx_itr(que); | // ixl_set_queue_tx_itr(que); | ||||
return (FILTER_SCHEDULE_THREAD); | return (FILTER_SCHEDULE_THREAD); | ||||
} | } | ||||
/********************************************************************* | /********************************************************************* | ||||
* | * | ||||
Show All 9 Lines | ixl_msix_adminq(void *arg) | ||||
u32 reg, mask, rstat_reg; | u32 reg, mask, rstat_reg; | ||||
bool do_task = FALSE; | bool do_task = FALSE; | ||||
DDPRINTF(dev, "begin"); | DDPRINTF(dev, "begin"); | ||||
++pf->admin_irq; | ++pf->admin_irq; | ||||
reg = rd32(hw, I40E_PFINT_ICR0); | reg = rd32(hw, I40E_PFINT_ICR0); | ||||
// For masking off interrupt causes that need to be handled before | /* | ||||
// they can be re-enabled | * For masking off interrupt causes that need to be handled before | ||||
* they can be re-enabled | |||||
*/ | |||||
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_ENA_ADMINQ_MASK; | ||||
do_task = TRUE; | do_task = TRUE; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | if (reg & I40E_PFHMC_ERRORINFO_ERROR_DETECTED_MASK) { | ||||
device_printf(dev, "DATA 0x%08x\n", reg); | device_printf(dev, "DATA 0x%08x\n", reg); | ||||
wr32(hw, I40E_PFHMC_ERRORINFO, 0); | wr32(hw, I40E_PFHMC_ERRORINFO, 0); | ||||
} | } | ||||
} | } | ||||
#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; | ||||
atomic_set_32(&pf->state, IXL_PF_STATE_VF_RESET_REQ); | iflib_iov_intr_deferred(pf->vsi.ctx); | ||||
do_task = TRUE; | |||||
} | } | ||||
#endif | #endif | ||||
wr32(hw, I40E_PFINT_ICR0_ENA, mask); | wr32(hw, I40E_PFINT_ICR0_ENA, mask); | ||||
ixl_enable_intr0(hw); | |||||
if (do_task) | if (do_task) | ||||
return (FILTER_SCHEDULE_THREAD); | return (FILTER_SCHEDULE_THREAD); | ||||
else | else | ||||
return (FILTER_HANDLED); | return (FILTER_HANDLED); | ||||
} | } | ||||
/********************************************************************* | /********************************************************************* | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | ixl_add_multi(struct ixl_vsi *vsi) | ||||
if (mcnt > 0) { | if (mcnt > 0) { | ||||
flags = (IXL_FILTER_ADD | IXL_FILTER_USED | IXL_FILTER_MC); | flags = (IXL_FILTER_ADD | IXL_FILTER_USED | IXL_FILTER_MC); | ||||
ixl_add_hw_filters(vsi, flags, mcnt); | ixl_add_hw_filters(vsi, flags, mcnt); | ||||
} | } | ||||
IOCTL_DEBUGOUT("ixl_add_multi: end"); | IOCTL_DEBUGOUT("ixl_add_multi: end"); | ||||
} | } | ||||
void | int | ||||
ixl_del_multi(struct ixl_vsi *vsi) | ixl_del_multi(struct ixl_vsi *vsi) | ||||
{ | { | ||||
struct ifnet *ifp = vsi->ifp; | struct ifnet *ifp = vsi->ifp; | ||||
struct ifmultiaddr *ifma; | struct ifmultiaddr *ifma; | ||||
struct ixl_mac_filter *f; | struct ixl_mac_filter *f; | ||||
int mcnt = 0; | int mcnt = 0; | ||||
bool match = FALSE; | bool match = FALSE; | ||||
Show All 18 Lines | if ((f->flags & IXL_FILTER_USED) && (f->flags & IXL_FILTER_MC)) { | ||||
mcnt++; | mcnt++; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if_maddr_runlock(ifp); | if_maddr_runlock(ifp); | ||||
if (mcnt > 0) | if (mcnt > 0) | ||||
ixl_del_hw_filters(vsi, mcnt); | ixl_del_hw_filters(vsi, mcnt); | ||||
return (mcnt); | |||||
} | } | ||||
void | void | ||||
ixl_link_up_msg(struct ixl_pf *pf) | ixl_link_up_msg(struct ixl_pf *pf) | ||||
{ | { | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
struct ifnet *ifp = pf->vsi.ifp; | struct ifnet *ifp = pf->vsi.ifp; | ||||
char *req_fec_string, *neg_fec_string; | char *req_fec_string, *neg_fec_string; | ||||
▲ Show 20 Lines • Show All 274 Lines • ▼ Show 20 Lines | ixl_setup_interface(device_t dev, struct ixl_pf *pf) | ||||
if_ctx_t ctx = vsi->ctx; | if_ctx_t ctx = vsi->ctx; | ||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
struct ifnet *ifp = iflib_get_ifp(ctx); | struct ifnet *ifp = iflib_get_ifp(ctx); | ||||
struct i40e_aq_get_phy_abilities_resp abilities; | struct i40e_aq_get_phy_abilities_resp abilities; | ||||
enum i40e_status_code aq_error = 0; | enum i40e_status_code aq_error = 0; | ||||
INIT_DBG_DEV(dev, "begin"); | INIT_DBG_DEV(dev, "begin"); | ||||
/* TODO: Remove VLAN_ENCAP_LEN? */ | |||||
vsi->shared->isc_max_frame_size = | vsi->shared->isc_max_frame_size = | ||||
ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN | ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN | ||||
+ ETHER_VLAN_ENCAP_LEN; | + ETHER_VLAN_ENCAP_LEN; | ||||
aq_error = i40e_aq_get_phy_capabilities(hw, | aq_error = i40e_aq_get_phy_capabilities(hw, | ||||
FALSE, TRUE, &abilities, NULL); | FALSE, TRUE, &abilities, NULL); | ||||
/* May need delay to detect fiber correctly */ | /* May need delay to detect fiber correctly */ | ||||
if (aq_error == I40E_ERR_UNKNOWN_PHY) { | if (aq_error == I40E_ERR_UNKNOWN_PHY) { | ||||
Show All 23 Lines | #endif | ||||
/* Use autoselect media by default */ | /* Use autoselect media by default */ | ||||
ifmedia_add(vsi->media, IFM_ETHER | IFM_AUTO, 0, NULL); | ifmedia_add(vsi->media, IFM_ETHER | IFM_AUTO, 0, NULL); | ||||
ifmedia_set(vsi->media, IFM_ETHER | IFM_AUTO); | ifmedia_set(vsi->media, IFM_ETHER | IFM_AUTO); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Input: bitmap of enum i40e_aq_link_speed | |||||
*/ | |||||
u64 | |||||
ixl_max_aq_speed_to_value(u8 link_speeds) | |||||
{ | |||||
if (link_speeds & I40E_LINK_SPEED_40GB) | |||||
return IF_Gbps(40); | |||||
if (link_speeds & I40E_LINK_SPEED_25GB) | |||||
return IF_Gbps(25); | |||||
if (link_speeds & I40E_LINK_SPEED_20GB) | |||||
return IF_Gbps(20); | |||||
if (link_speeds & I40E_LINK_SPEED_10GB) | |||||
return IF_Gbps(10); | |||||
if (link_speeds & I40E_LINK_SPEED_1GB) | |||||
return IF_Gbps(1); | |||||
if (link_speeds & I40E_LINK_SPEED_100MB) | |||||
return IF_Mbps(100); | |||||
else | |||||
/* Minimum supported link speed */ | |||||
return IF_Mbps(100); | |||||
} | |||||
/* | |||||
** Run when the Admin Queue gets a link state change interrupt. | ** Run when the Admin Queue gets a link state change interrupt. | ||||
*/ | */ | ||||
void | 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 = iflib_get_dev(pf->vsi.ctx); | device_t dev = iflib_get_dev(pf->vsi.ctx); | ||||
struct i40e_aqc_get_link_status *status = | struct i40e_aqc_get_link_status *status = | ||||
▲ Show 20 Lines • Show All 110 Lines • ▼ Show 20 Lines | ixl_initialize_vsi(struct ixl_vsi *vsi) | ||||
/* In contig mode, que_mapping[0] is first queue index used by this VSI */ | /* 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 will only use traffic class 0; start traffic class 0's | * This VSI will only use traffic class 0; start traffic class 0's | ||||
* queue allocation at queue 0, and assign it 2^tc_queues queues (though | * queue allocation at queue 0, and assign it 2^tc_queues queues (though | ||||
* the driver may not use all of them). | * the driver may not use all of them). | ||||
*/ | */ | ||||
tc_queues = fls(pf->qtag.num_allocated) - 1; | tc_queues = fls(pf->qtag.num_allocated) - 1; | ||||
ctxt.info.tc_mapping[0] = ((0 << I40E_AQ_VSI_TC_QUE_OFFSET_SHIFT) | ctxt.info.tc_mapping[0] = ((pf->qtag.first_qidx << I40E_AQ_VSI_TC_QUE_OFFSET_SHIFT) | ||||
& I40E_AQ_VSI_TC_QUE_OFFSET_MASK) | | & I40E_AQ_VSI_TC_QUE_OFFSET_MASK) | | ||||
((tc_queues << I40E_AQ_VSI_TC_QUE_NUMBER_SHIFT) | ((tc_queues << I40E_AQ_VSI_TC_QUE_NUMBER_SHIFT) | ||||
& I40E_AQ_VSI_TC_QUE_NUMBER_MASK); | & 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 (if_getcapenable(vsi->ifp) & IFCAP_VLAN_HWTAGGING) | if (if_getcapenable(vsi->ifp) & IFCAP_VLAN_HWTAGGING) | ||||
▲ Show 20 Lines • Show All 282 Lines • ▼ Show 20 Lines | if (txr->itr != vsi->tx_itr_setting) { | ||||
txr->me), txr->itr); | txr->me), txr->itr); | ||||
} | } | ||||
} | } | ||||
txr->bytes = 0; | txr->bytes = 0; | ||||
txr->packets = 0; | txr->packets = 0; | ||||
return; | return; | ||||
} | } | ||||
void | |||||
ixl_add_vsi_sysctls(struct ixl_pf *pf, struct ixl_vsi *vsi, | |||||
struct sysctl_ctx_list *ctx, const char *sysctl_name) | |||||
{ | |||||
struct sysctl_oid *tree; | |||||
struct sysctl_oid_list *child; | |||||
struct sysctl_oid_list *vsi_list; | |||||
tree = device_get_sysctl_tree(pf->dev); | |||||
child = SYSCTL_CHILDREN(tree); | |||||
vsi->vsi_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, sysctl_name, | |||||
CTLFLAG_RD, NULL, "VSI Number"); | |||||
vsi_list = SYSCTL_CHILDREN(vsi->vsi_node); | |||||
ixl_add_sysctls_eth_stats(ctx, vsi_list, &vsi->eth_stats); | |||||
} | |||||
#ifdef IXL_DEBUG | #ifdef IXL_DEBUG | ||||
/** | /** | ||||
* ixl_sysctl_qtx_tail_handler | * ixl_sysctl_qtx_tail_handler | ||||
* Retrieves I40E_QTX_TAIL value from hardware | * Retrieves I40E_QTX_TAIL value from hardware | ||||
* for a sysctl. | * for a sysctl. | ||||
*/ | */ | ||||
int | int | ||||
ixl_sysctl_qtx_tail_handler(SYSCTL_HANDLER_ARGS) | ixl_sysctl_qtx_tail_handler(SYSCTL_HANDLER_ARGS) | ||||
▲ Show 20 Lines • Show All 108 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
struct ixl_vsi *vsi = &pf->vsi; | struct ixl_vsi *vsi = &pf->vsi; | ||||
device_t dev = iflib_get_dev(vsi->ctx); | device_t dev = iflib_get_dev(vsi->ctx); | ||||
struct i40e_hw_port_stats *pf_stats = &pf->stats; | struct i40e_hw_port_stats *pf_stats = &pf->stats; | ||||
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); | struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); | ||||
struct sysctl_oid *tree = device_get_sysctl_tree(dev); | struct sysctl_oid *tree = device_get_sysctl_tree(dev); | ||||
struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree); | struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree); | ||||
struct sysctl_oid_list *vsi_list, *queue_list; | |||||
struct sysctl_oid *queue_node; | |||||
char queue_namebuf[32]; | |||||
struct ixl_rx_queue *rx_que; | |||||
struct ixl_tx_queue *tx_que; | |||||
struct tx_ring *txr; | |||||
struct rx_ring *rxr; | |||||
/* Driver statistics */ | /* Driver statistics */ | ||||
SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "watchdog_events", | |||||
CTLFLAG_RD, &pf->watchdog_events, | |||||
"Watchdog timeouts"); | |||||
SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "admin_irq", | SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "admin_irq", | ||||
CTLFLAG_RD, &pf->admin_irq, | CTLFLAG_RD, &pf->admin_irq, | ||||
"Admin Queue IRQ Handled"); | "Admin Queue IRQs received"); | ||||
ixl_add_vsi_sysctls(pf, &pf->vsi, ctx, "pf"); | ixl_add_vsi_sysctls(dev, vsi, ctx, "pf"); | ||||
vsi_list = SYSCTL_CHILDREN(pf->vsi.vsi_node); | |||||
/* Queue statistics */ | ixl_add_queues_sysctls(dev, vsi); | ||||
for (int q = 0; q < vsi->num_rx_queues; q++) { | |||||
snprintf(queue_namebuf, QUEUE_NAME_LEN, "rxq%02d", q); | |||||
queue_node = SYSCTL_ADD_NODE(ctx, vsi_list, | |||||
OID_AUTO, queue_namebuf, CTLFLAG_RD, NULL, "RX Queue #"); | |||||
queue_list = SYSCTL_CHILDREN(queue_node); | |||||
rx_que = &(vsi->rx_queues[q]); | |||||
rxr = &(rx_que->rxr); | |||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "irqs", | |||||
CTLFLAG_RD, &(rx_que->irqs), | |||||
"irqs on this queue (both Tx and Rx)"); | |||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "packets", | |||||
CTLFLAG_RD, &(rxr->rx_packets), | |||||
"Queue Packets Received"); | |||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "bytes", | |||||
CTLFLAG_RD, &(rxr->rx_bytes), | |||||
"Queue Bytes Received"); | |||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "desc_err", | |||||
CTLFLAG_RD, &(rxr->desc_errs), | |||||
"Queue Rx Descriptor Errors"); | |||||
SYSCTL_ADD_UINT(ctx, queue_list, OID_AUTO, "itr", | |||||
CTLFLAG_RD, &(rxr->itr), 0, | |||||
"Queue Rx ITR Interval"); | |||||
#ifdef IXL_DEBUG | |||||
SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "qrx_tail", | |||||
CTLTYPE_UINT | CTLFLAG_RD, rx_que, | |||||
sizeof(struct ixl_rx_queue), | |||||
ixl_sysctl_qrx_tail_handler, "IU", | |||||
"Queue Receive Descriptor Tail"); | |||||
#endif | |||||
} | |||||
for (int q = 0; q < vsi->num_tx_queues; q++) { | |||||
snprintf(queue_namebuf, QUEUE_NAME_LEN, "txq%02d", q); | |||||
queue_node = SYSCTL_ADD_NODE(ctx, vsi_list, | |||||
OID_AUTO, queue_namebuf, CTLFLAG_RD, NULL, "TX Queue #"); | |||||
queue_list = SYSCTL_CHILDREN(queue_node); | |||||
tx_que = &(vsi->tx_queues[q]); | |||||
txr = &(tx_que->txr); | |||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "tso", | |||||
CTLFLAG_RD, &(tx_que->tso), | |||||
"TSO"); | |||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "mss_too_small", | |||||
CTLFLAG_RD, &(txr->mss_too_small), | |||||
"TSO sends with an MSS less than 64"); | |||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "packets", | |||||
CTLFLAG_RD, &(txr->tx_packets), | |||||
"Queue Packets Transmitted"); | |||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "bytes", | |||||
CTLFLAG_RD, &(txr->tx_bytes), | |||||
"Queue Bytes Transmitted"); | |||||
SYSCTL_ADD_UINT(ctx, queue_list, OID_AUTO, "itr", | |||||
CTLFLAG_RD, &(txr->itr), 0, | |||||
"Queue Tx ITR Interval"); | |||||
#ifdef IXL_DEBUG | |||||
SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "qtx_tail", | |||||
CTLTYPE_UINT | CTLFLAG_RD, tx_que, | |||||
sizeof(struct ixl_tx_queue), | |||||
ixl_sysctl_qtx_tail_handler, "IU", | |||||
"Queue Transmit Descriptor Tail"); | |||||
#endif | |||||
} | |||||
/* MAC stats */ | |||||
ixl_add_sysctls_mac_stats(ctx, child, pf_stats); | ixl_add_sysctls_mac_stats(ctx, child, pf_stats); | ||||
} | } | ||||
void | void | ||||
ixl_add_sysctls_eth_stats(struct sysctl_ctx_list *ctx, | |||||
struct sysctl_oid_list *child, | |||||
struct i40e_eth_stats *eth_stats) | |||||
{ | |||||
struct ixl_sysctl_info ctls[] = | |||||
{ | |||||
{ð_stats->rx_bytes, "good_octets_rcvd", "Good Octets Received"}, | |||||
{ð_stats->rx_unicast, "ucast_pkts_rcvd", | |||||
"Unicast Packets Received"}, | |||||
{ð_stats->rx_multicast, "mcast_pkts_rcvd", | |||||
"Multicast Packets Received"}, | |||||
{ð_stats->rx_broadcast, "bcast_pkts_rcvd", | |||||
"Broadcast Packets Received"}, | |||||
{ð_stats->rx_discards, "rx_discards", "Discarded RX packets"}, | |||||
{ð_stats->tx_bytes, "good_octets_txd", "Good Octets Transmitted"}, | |||||
{ð_stats->tx_unicast, "ucast_pkts_txd", "Unicast Packets Transmitted"}, | |||||
{ð_stats->tx_multicast, "mcast_pkts_txd", | |||||
"Multicast Packets Transmitted"}, | |||||
{ð_stats->tx_broadcast, "bcast_pkts_txd", | |||||
"Broadcast Packets Transmitted"}, | |||||
// end | |||||
{0,0,0} | |||||
}; | |||||
struct ixl_sysctl_info *entry = ctls; | |||||
while (entry->stat != 0) | |||||
{ | |||||
SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, entry->name, | |||||
CTLFLAG_RD, entry->stat, | |||||
entry->description); | |||||
entry++; | |||||
} | |||||
} | |||||
void | |||||
ixl_add_sysctls_mac_stats(struct sysctl_ctx_list *ctx, | ixl_add_sysctls_mac_stats(struct sysctl_ctx_list *ctx, | ||||
struct sysctl_oid_list *child, | struct sysctl_oid_list *child, | ||||
struct i40e_hw_port_stats *stats) | struct i40e_hw_port_stats *stats) | ||||
{ | { | ||||
struct sysctl_oid *stat_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "mac", | struct sysctl_oid *stat_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "mac", | ||||
CTLFLAG_RD, NULL, "Mac Statistics"); | CTLFLAG_RD, NULL, "Mac Statistics"); | ||||
struct sysctl_oid_list *stat_list = SYSCTL_CHILDREN(stat_node); | struct sysctl_oid_list *stat_list = SYSCTL_CHILDREN(stat_node); | ||||
▲ Show 20 Lines • Show All 208 Lines • ▼ Show 20 Lines | ixl_setup_vlan_filters(struct ixl_vsi *vsi) | ||||
ixl_add_hw_filters(vsi, flags, cnt); | ixl_add_hw_filters(vsi, flags, cnt); | ||||
} | } | ||||
/* | /* | ||||
* In some firmware versions there is default MAC/VLAN filter | * In some firmware versions there is default MAC/VLAN filter | ||||
* configured which interferes with filters managed by driver. | * configured which interferes with filters managed by driver. | ||||
* Make sure it's removed. | * Make sure it's removed. | ||||
*/ | */ | ||||
static void | void | ||||
ixl_del_default_hw_filters(struct ixl_vsi *vsi) | ixl_del_default_hw_filters(struct ixl_vsi *vsi) | ||||
{ | { | ||||
struct i40e_aqc_remove_macvlan_element_data e; | struct i40e_aqc_remove_macvlan_element_data e; | ||||
bzero(&e, sizeof(e)); | bzero(&e, sizeof(e)); | ||||
bcopy(vsi->hw->mac.perm_addr, e.mac_addr, ETHER_ADDR_LEN); | bcopy(vsi->hw->mac.perm_addr, e.mac_addr, ETHER_ADDR_LEN); | ||||
e.vlan_tag = 0; | e.vlan_tag = 0; | ||||
e.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH; | e.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH; | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | ixl_add_mc_filter(struct ixl_vsi *vsi, u8 *macaddr) | ||||
if (f != NULL) | if (f != NULL) | ||||
return; | return; | ||||
f = ixl_new_filter(vsi, macaddr, IXL_VLAN_ANY); | f = ixl_new_filter(vsi, macaddr, IXL_VLAN_ANY); | ||||
if (f != NULL) | if (f != NULL) | ||||
f->flags |= IXL_FILTER_MC; | f->flags |= IXL_FILTER_MC; | ||||
else | else | ||||
printf("WARNING: no filter available!!\n"); | printf("WARNING: no filter available!!\n"); | ||||
return; | |||||
} | } | ||||
void | void | ||||
ixl_reconfigure_filters(struct ixl_vsi *vsi) | ixl_reconfigure_filters(struct ixl_vsi *vsi) | ||||
{ | { | ||||
ixl_add_hw_filters(vsi, IXL_FILTER_USED, vsi->num_macs); | ixl_add_hw_filters(vsi, IXL_FILTER_USED, vsi->num_macs); | ||||
} | } | ||||
/* | /* | ||||
** This routine adds macvlan filters | * This routine adds a MAC/VLAN filter to the software filter | ||||
* list, then adds that new filter to the HW if it doesn't already | |||||
* exist in the SW filter list. | |||||
*/ | */ | ||||
void | void | ||||
ixl_add_filter(struct ixl_vsi *vsi, const u8 *macaddr, s16 vlan) | ixl_add_filter(struct ixl_vsi *vsi, const u8 *macaddr, s16 vlan) | ||||
{ | { | ||||
struct ixl_mac_filter *f, *tmp; | struct ixl_mac_filter *f, *tmp; | ||||
struct ixl_pf *pf; | struct ixl_pf *pf; | ||||
device_t dev; | device_t dev; | ||||
DEBUGOUT("ixl_add_filter: begin"); | DEBUGOUT("ixl_add_filter: begin"); | ||||
Show All 23 Lines | if (f == NULL) { | ||||
device_printf(dev, "WARNING: no filter available!!\n"); | device_printf(dev, "WARNING: no filter available!!\n"); | ||||
return; | return; | ||||
} | } | ||||
if (f->vlan != IXL_VLAN_ANY) | if (f->vlan != IXL_VLAN_ANY) | ||||
f->flags |= IXL_FILTER_VLAN; | f->flags |= IXL_FILTER_VLAN; | ||||
else | else | ||||
vsi->num_macs++; | vsi->num_macs++; | ||||
f->flags |= IXL_FILTER_USED; | |||||
ixl_add_hw_filters(vsi, f->flags, 1); | ixl_add_hw_filters(vsi, f->flags, 1); | ||||
return; | |||||
} | } | ||||
void | void | ||||
ixl_del_filter(struct ixl_vsi *vsi, const u8 *macaddr, s16 vlan) | ixl_del_filter(struct ixl_vsi *vsi, const u8 *macaddr, s16 vlan) | ||||
{ | { | ||||
struct ixl_mac_filter *f; | struct ixl_mac_filter *f; | ||||
f = ixl_find_filter(vsi, macaddr, vlan); | f = ixl_find_filter(vsi, macaddr, vlan); | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | ixl_add_hw_filters(struct ixl_vsi *vsi, int flags, int cnt) | ||||
struct i40e_aqc_add_macvlan_element_data *a, *b; | struct i40e_aqc_add_macvlan_element_data *a, *b; | ||||
struct ixl_mac_filter *f; | struct ixl_mac_filter *f; | ||||
struct ixl_pf *pf; | struct ixl_pf *pf; | ||||
struct i40e_hw *hw; | struct i40e_hw *hw; | ||||
device_t dev; | device_t dev; | ||||
enum i40e_status_code status; | enum i40e_status_code status; | ||||
int j = 0; | int j = 0; | ||||
MPASS(cnt > 0); | |||||
pf = vsi->back; | pf = vsi->back; | ||||
dev = iflib_get_dev(vsi->ctx); | dev = vsi->dev; | ||||
hw = &pf->hw; | hw = &pf->hw; | ||||
if (cnt < 1) { | |||||
ixl_dbg_info(pf, "ixl_add_hw_filters: cnt == 0\n"); | |||||
return; | |||||
} | |||||
a = malloc(sizeof(struct i40e_aqc_add_macvlan_element_data) * cnt, | a = malloc(sizeof(struct i40e_aqc_add_macvlan_element_data) * cnt, | ||||
M_DEVBUF, M_NOWAIT | M_ZERO); | M_DEVBUF, M_NOWAIT | M_ZERO); | ||||
if (a == NULL) { | if (a == NULL) { | ||||
device_printf(dev, "add_hw_filters failed to get memory\n"); | device_printf(dev, "add_hw_filters failed to get memory\n"); | ||||
return; | return; | ||||
} | } | ||||
/* | /* | ||||
Show All 10 Lines | if ((f->flags & flags) == flags) { | ||||
b->flags = I40E_AQC_MACVLAN_ADD_IGNORE_VLAN; | b->flags = I40E_AQC_MACVLAN_ADD_IGNORE_VLAN; | ||||
} else { | } else { | ||||
b->vlan_tag = f->vlan; | b->vlan_tag = f->vlan; | ||||
b->flags = 0; | b->flags = 0; | ||||
} | } | ||||
b->flags |= I40E_AQC_MACVLAN_ADD_PERFECT_MATCH; | b->flags |= I40E_AQC_MACVLAN_ADD_PERFECT_MATCH; | ||||
f->flags &= ~IXL_FILTER_ADD; | f->flags &= ~IXL_FILTER_ADD; | ||||
j++; | j++; | ||||
ixl_dbg_filter(pf, "ADD: " MAC_FORMAT "\n", | |||||
MAC_FORMAT_ARGS(f->macaddr)); | |||||
} | } | ||||
if (j == cnt) | if (j == cnt) | ||||
break; | break; | ||||
} | } | ||||
if (j > 0) { | if (j > 0) { | ||||
status = i40e_aq_add_macvlan(hw, vsi->seid, a, j, NULL); | status = i40e_aq_add_macvlan(hw, vsi->seid, a, j, NULL); | ||||
if (status) | if (status) | ||||
device_printf(dev, "i40e_aq_add_macvlan status %s, " | device_printf(dev, "i40e_aq_add_macvlan status %s, " | ||||
Show All 19 Lines | ixl_del_hw_filters(struct ixl_vsi *vsi, int cnt) | ||||
struct i40e_hw *hw; | struct i40e_hw *hw; | ||||
device_t dev; | device_t dev; | ||||
struct ixl_mac_filter *f, *f_temp; | struct ixl_mac_filter *f, *f_temp; | ||||
enum i40e_status_code status; | enum i40e_status_code status; | ||||
int j = 0; | int j = 0; | ||||
pf = vsi->back; | pf = vsi->back; | ||||
hw = &pf->hw; | hw = &pf->hw; | ||||
dev = iflib_get_dev(vsi->ctx); | dev = vsi->dev; | ||||
d = malloc(sizeof(struct i40e_aqc_remove_macvlan_element_data) * cnt, | d = malloc(sizeof(struct i40e_aqc_remove_macvlan_element_data) * cnt, | ||||
M_DEVBUF, M_NOWAIT | M_ZERO); | M_DEVBUF, M_NOWAIT | M_ZERO); | ||||
if (d == NULL) { | if (d == NULL) { | ||||
device_printf(dev, "%s: failed to get memory\n", __func__); | device_printf(dev, "%s: failed to get memory\n", __func__); | ||||
return; | return; | ||||
} | } | ||||
SLIST_FOREACH_SAFE(f, &vsi->ftl, next, f_temp) { | SLIST_FOREACH_SAFE(f, &vsi->ftl, next, f_temp) { | ||||
if (f->flags & IXL_FILTER_DEL) { | if (f->flags & IXL_FILTER_DEL) { | ||||
e = &d[j]; // a pox on fvl long names :) | e = &d[j]; // a pox on fvl long names :) | ||||
bcopy(f->macaddr, e->mac_addr, ETHER_ADDR_LEN); | bcopy(f->macaddr, e->mac_addr, ETHER_ADDR_LEN); | ||||
e->flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH; | e->flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH; | ||||
if (f->vlan == IXL_VLAN_ANY) { | if (f->vlan == IXL_VLAN_ANY) { | ||||
e->vlan_tag = 0; | e->vlan_tag = 0; | ||||
e->flags |= I40E_AQC_MACVLAN_DEL_IGNORE_VLAN; | e->flags |= I40E_AQC_MACVLAN_DEL_IGNORE_VLAN; | ||||
} else { | } else { | ||||
e->vlan_tag = f->vlan; | e->vlan_tag = f->vlan; | ||||
} | } | ||||
ixl_dbg_filter(pf, "DEL: " MAC_FORMAT "\n", | |||||
MAC_FORMAT_ARGS(f->macaddr)); | |||||
/* delete entry from vsi list */ | /* delete entry from vsi list */ | ||||
SLIST_REMOVE(&vsi->ftl, f, ixl_mac_filter, next); | SLIST_REMOVE(&vsi->ftl, f, ixl_mac_filter, next); | ||||
free(f, M_DEVBUF); | free(f, M_DEVBUF); | ||||
j++; | j++; | ||||
} | } | ||||
if (j == cnt) | if (j == cnt) | ||||
break; | break; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 188 Lines • ▼ Show 20 Lines | ixl_disable_ring(struct ixl_pf *pf, struct ixl_pf_qtag *qtag, u16 vsi_qidx) | ||||
error = ixl_disable_tx_ring(pf, qtag, vsi_qidx); | error = ixl_disable_tx_ring(pf, qtag, vsi_qidx); | ||||
/* Called function already prints error message */ | /* Called function already prints error message */ | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
error = ixl_disable_rx_ring(pf, qtag, vsi_qidx); | error = ixl_disable_rx_ring(pf, qtag, vsi_qidx); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* For PF VSI only */ | |||||
int | int | ||||
ixl_disable_rings(struct ixl_vsi *vsi) | ixl_disable_rings(struct ixl_pf *pf, struct ixl_vsi *vsi, struct ixl_pf_qtag *qtag) | ||||
{ | { | ||||
struct ixl_pf *pf = vsi->back; | |||||
int error = 0; | int error = 0; | ||||
for (int i = 0; i < vsi->num_tx_queues; i++) | for (int i = 0; i < vsi->num_tx_queues; i++) | ||||
error = ixl_disable_tx_ring(pf, &pf->qtag, i); | error = ixl_disable_tx_ring(pf, qtag, i); | ||||
for (int i = 0; i < vsi->num_rx_queues; i++) | for (int i = 0; i < vsi->num_rx_queues; i++) | ||||
error = ixl_disable_rx_ring(pf, &pf->qtag, i); | error = ixl_disable_rx_ring(pf, qtag, i); | ||||
return (error); | return (error); | ||||
} | } | ||||
/** | static void | ||||
* ixl_handle_mdd_event | ixl_handle_tx_mdd_event(struct ixl_pf *pf) | ||||
* | |||||
* Called from interrupt handler to identify possibly malicious vfs | |||||
* (But also detects events from the PF, as well) | |||||
**/ | |||||
void | |||||
ixl_handle_mdd_event(struct ixl_pf *pf) | |||||
{ | { | ||||
gallatin: I just hate that there are 2 functions which do almost the same thing. Can't you just change… | |||||
struct i40e_hw *hw = &pf->hw; | struct i40e_hw *hw = &pf->hw; | ||||
device_t dev = pf->dev; | device_t dev = pf->dev; | ||||
struct ixl_vf *vf; | struct ixl_vf *vf; | ||||
bool mdd_detected = false; | bool mdd_detected = false; | ||||
bool pf_mdd_detected = false; | bool pf_mdd_detected = false; | ||||
bool vf_mdd_detected = false; | bool vf_mdd_detected = false; | ||||
u16 vf_num, queue; | |||||
u8 pf_num, event; | |||||
u8 pf_mdet_num, vp_mdet_num; | |||||
u32 reg; | u32 reg; | ||||
/* find what triggered the MDD event */ | /* find what triggered the MDD event */ | ||||
reg = rd32(hw, I40E_GL_MDET_TX); | reg = rd32(hw, I40E_GL_MDET_TX); | ||||
if (reg & I40E_GL_MDET_TX_VALID_MASK) { | if (reg & I40E_GL_MDET_TX_VALID_MASK) { | ||||
u8 pf_num = (reg & I40E_GL_MDET_TX_PF_NUM_MASK) >> | pf_num = (reg & I40E_GL_MDET_TX_PF_NUM_MASK) >> | ||||
I40E_GL_MDET_TX_PF_NUM_SHIFT; | I40E_GL_MDET_TX_PF_NUM_SHIFT; | ||||
u8 event = (reg & I40E_GL_MDET_TX_EVENT_MASK) >> | vf_num = (reg & I40E_GL_MDET_TX_VF_NUM_MASK) >> | ||||
I40E_GL_MDET_TX_VF_NUM_SHIFT; | |||||
event = (reg & I40E_GL_MDET_TX_EVENT_MASK) >> | |||||
I40E_GL_MDET_TX_EVENT_SHIFT; | I40E_GL_MDET_TX_EVENT_SHIFT; | ||||
u16 queue = (reg & I40E_GL_MDET_TX_QUEUE_MASK) >> | queue = (reg & I40E_GL_MDET_TX_QUEUE_MASK) >> | ||||
I40E_GL_MDET_TX_QUEUE_SHIFT; | I40E_GL_MDET_TX_QUEUE_SHIFT; | ||||
device_printf(dev, | |||||
"Malicious Driver Detection event %d" | |||||
" on TX queue %d, pf number %d\n", | |||||
event, queue, pf_num); | |||||
wr32(hw, I40E_GL_MDET_TX, 0xffffffff); | wr32(hw, I40E_GL_MDET_TX, 0xffffffff); | ||||
mdd_detected = true; | mdd_detected = true; | ||||
} | } | ||||
if (!mdd_detected) | |||||
return; | |||||
reg = rd32(hw, I40E_PF_MDET_TX); | |||||
if (reg & I40E_PF_MDET_TX_VALID_MASK) { | |||||
wr32(hw, I40E_PF_MDET_TX, 0xFFFF); | |||||
pf_mdet_num = hw->pf_id; | |||||
pf_mdd_detected = true; | |||||
} | |||||
/* Check if MDD was caused by a VF */ | |||||
for (int i = 0; i < pf->num_vfs; i++) { | |||||
vf = &(pf->vfs[i]); | |||||
reg = rd32(hw, I40E_VP_MDET_TX(i)); | |||||
if (reg & I40E_VP_MDET_TX_VALID_MASK) { | |||||
wr32(hw, I40E_VP_MDET_TX(i), 0xFFFF); | |||||
vp_mdet_num = i; | |||||
vf->num_mdd_events++; | |||||
vf_mdd_detected = true; | |||||
} | |||||
} | |||||
/* Print out an error message */ | |||||
if (vf_mdd_detected && pf_mdd_detected) | |||||
device_printf(dev, | |||||
"Malicious Driver Detection event %d" | |||||
" on TX queue %d, pf number %d (PF-%d), vf number %d (VF-%d)\n", | |||||
event, queue, pf_num, pf_mdet_num, vf_num, vp_mdet_num); | |||||
else if (vf_mdd_detected && !pf_mdd_detected) | |||||
device_printf(dev, | |||||
"Malicious Driver Detection event %d" | |||||
" on TX queue %d, pf number %d, vf number %d (VF-%d)\n", | |||||
event, queue, pf_num, vf_num, vp_mdet_num); | |||||
else if (!vf_mdd_detected && pf_mdd_detected) | |||||
device_printf(dev, | |||||
"Malicious Driver Detection event %d" | |||||
" on TX queue %d, pf number %d (PF-%d)\n", | |||||
event, queue, pf_num, pf_mdet_num); | |||||
/* Theoretically shouldn't happen */ | |||||
else | |||||
device_printf(dev, | |||||
"TX Malicious Driver Detection event (unknown)\n"); | |||||
} | |||||
static void | |||||
ixl_handle_rx_mdd_event(struct ixl_pf *pf) | |||||
{ | |||||
struct i40e_hw *hw = &pf->hw; | |||||
device_t dev = pf->dev; | |||||
struct ixl_vf *vf; | |||||
bool mdd_detected = false; | |||||
bool pf_mdd_detected = false; | |||||
bool vf_mdd_detected = false; | |||||
u16 queue; | |||||
u8 pf_num, event; | |||||
u8 pf_mdet_num, vp_mdet_num; | |||||
u32 reg; | |||||
/* | |||||
* GL_MDET_RX doesn't contain VF number information, unlike | |||||
* GL_MDET_TX. | |||||
*/ | |||||
reg = rd32(hw, I40E_GL_MDET_RX); | reg = rd32(hw, I40E_GL_MDET_RX); | ||||
if (reg & I40E_GL_MDET_RX_VALID_MASK) { | if (reg & I40E_GL_MDET_RX_VALID_MASK) { | ||||
u8 pf_num = (reg & I40E_GL_MDET_RX_FUNCTION_MASK) >> | pf_num = (reg & I40E_GL_MDET_RX_FUNCTION_MASK) >> | ||||
I40E_GL_MDET_RX_FUNCTION_SHIFT; | I40E_GL_MDET_RX_FUNCTION_SHIFT; | ||||
u8 event = (reg & I40E_GL_MDET_RX_EVENT_MASK) >> | event = (reg & I40E_GL_MDET_RX_EVENT_MASK) >> | ||||
I40E_GL_MDET_RX_EVENT_SHIFT; | I40E_GL_MDET_RX_EVENT_SHIFT; | ||||
u16 queue = (reg & I40E_GL_MDET_RX_QUEUE_MASK) >> | queue = (reg & I40E_GL_MDET_RX_QUEUE_MASK) >> | ||||
I40E_GL_MDET_RX_QUEUE_SHIFT; | I40E_GL_MDET_RX_QUEUE_SHIFT; | ||||
device_printf(dev, | |||||
"Malicious Driver Detection event %d" | |||||
" on RX queue %d, pf number %d\n", | |||||
event, queue, pf_num); | |||||
wr32(hw, I40E_GL_MDET_RX, 0xffffffff); | wr32(hw, I40E_GL_MDET_RX, 0xffffffff); | ||||
mdd_detected = true; | mdd_detected = true; | ||||
} | } | ||||
if (mdd_detected) { | if (!mdd_detected) | ||||
reg = rd32(hw, I40E_PF_MDET_TX); | return; | ||||
if (reg & I40E_PF_MDET_TX_VALID_MASK) { | |||||
wr32(hw, I40E_PF_MDET_TX, 0xFFFF); | |||||
device_printf(dev, | |||||
"MDD TX event is for this function!\n"); | |||||
pf_mdd_detected = true; | |||||
} | |||||
reg = rd32(hw, I40E_PF_MDET_RX); | reg = rd32(hw, I40E_PF_MDET_RX); | ||||
if (reg & I40E_PF_MDET_RX_VALID_MASK) { | if (reg & I40E_PF_MDET_RX_VALID_MASK) { | ||||
wr32(hw, I40E_PF_MDET_RX, 0xFFFF); | wr32(hw, I40E_PF_MDET_RX, 0xFFFF); | ||||
device_printf(dev, | pf_mdet_num = hw->pf_id; | ||||
"MDD RX event is for this function!\n"); | |||||
pf_mdd_detected = true; | pf_mdd_detected = true; | ||||
} | } | ||||
} | |||||
if (pf_mdd_detected) { | /* Check if MDD was caused by a VF */ | ||||
atomic_set_32(&pf->state, IXL_PF_STATE_PF_RESET_REQ); | for (int i = 0; i < pf->num_vfs; i++) { | ||||
goto end; | |||||
} | |||||
// Handle VF detection | |||||
for (int i = 0; i < pf->num_vfs && mdd_detected; i++) { | |||||
vf = &(pf->vfs[i]); | vf = &(pf->vfs[i]); | ||||
reg = rd32(hw, I40E_VP_MDET_TX(i)); | |||||
if (reg & I40E_VP_MDET_TX_VALID_MASK) { | |||||
wr32(hw, I40E_VP_MDET_TX(i), 0xFFFF); | |||||
vf->num_mdd_events++; | |||||
device_printf(dev, "MDD TX event is for VF %d\n", i); | |||||
vf_mdd_detected = true; | |||||
} | |||||
reg = rd32(hw, I40E_VP_MDET_RX(i)); | reg = rd32(hw, I40E_VP_MDET_RX(i)); | ||||
if (reg & I40E_VP_MDET_RX_VALID_MASK) { | if (reg & I40E_VP_MDET_RX_VALID_MASK) { | ||||
wr32(hw, I40E_VP_MDET_RX(i), 0xFFFF); | wr32(hw, I40E_VP_MDET_RX(i), 0xFFFF); | ||||
vp_mdet_num = i; | |||||
vf->num_mdd_events++; | vf->num_mdd_events++; | ||||
device_printf(dev, "MDD RX event is for VF %d\n", i); | |||||
vf_mdd_detected = true; | vf_mdd_detected = true; | ||||
} | } | ||||
} | |||||
// TODO: Disable VF if there are too many MDD events from it | /* Print out an error message */ | ||||
if (vf_mdd_detected && pf_mdd_detected) | |||||
device_printf(dev, | |||||
"Malicious Driver Detection event %d" | |||||
Not Done Inline ActionsNice! This would have been helpful during bringup of the iflib driver, but better late than never :) gallatin: Nice! This would have been helpful during bringup of the iflib driver, but better late than… | |||||
" on RX queue %d, pf number %d (PF-%d), (VF-%d)\n", | |||||
event, queue, pf_num, pf_mdet_num, vp_mdet_num); | |||||
else if (vf_mdd_detected && !pf_mdd_detected) | |||||
device_printf(dev, | |||||
"Malicious Driver Detection event %d" | |||||
" on RX queue %d, pf number %d, (VF-%d)\n", | |||||
event, queue, pf_num, vp_mdet_num); | |||||
else if (!vf_mdd_detected && pf_mdd_detected) | |||||
device_printf(dev, | |||||
"Malicious Driver Detection event %d" | |||||
" on RX queue %d, pf number %d (PF-%d)\n", | |||||
event, queue, pf_num, pf_mdet_num); | |||||
/* Theoretically shouldn't happen */ | |||||
else | |||||
device_printf(dev, | |||||
"RX Malicious Driver Detection event (unknown)\n"); | |||||
} | } | ||||
if (vf_mdd_detected) | /** | ||||
atomic_set_32(&pf->state, IXL_PF_STATE_VF_RESET_REQ); | * ixl_handle_mdd_event | ||||
* | |||||
* Called from interrupt handler to identify possibly malicious vfs | |||||
* (But also detects events from the PF, as well) | |||||
**/ | |||||
void | |||||
ixl_handle_mdd_event(struct ixl_pf *pf) | |||||
{ | |||||
struct i40e_hw *hw = &pf->hw; | |||||
u32 reg; | |||||
end: | /* | ||||
* Handle both TX/RX because it's possible they could | |||||
* both trigger in the same interrupt. | |||||
*/ | |||||
ixl_handle_tx_mdd_event(pf); | |||||
ixl_handle_rx_mdd_event(pf); | |||||
atomic_clear_32(&pf->state, IXL_PF_STATE_MDD_PENDING); | atomic_clear_32(&pf->state, IXL_PF_STATE_MDD_PENDING); | ||||
/* re-enable mdd interrupt cause */ | /* re-enable mdd interrupt cause */ | ||||
reg = rd32(hw, I40E_PFINT_ICR0_ENA); | reg = rd32(hw, I40E_PFINT_ICR0_ENA); | ||||
reg |= I40E_PFINT_ICR0_ENA_MAL_DETECT_MASK; | reg |= I40E_PFINT_ICR0_ENA_MAL_DETECT_MASK; | ||||
wr32(hw, I40E_PFINT_ICR0_ENA, reg); | wr32(hw, I40E_PFINT_ICR0_ENA, reg); | ||||
ixl_flush(hw); | ixl_flush(hw); | ||||
} | } | ||||
/* This only enables HW interrupts for the RX queues */ | |||||
void | 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_rx_queue *que = vsi->rx_queues; | struct ixl_rx_queue *que = vsi->rx_queues; | ||||
// TODO: Check iflib interrupt mode instead? | |||||
if (vsi->shared->isc_intr == IFLIB_INTR_MSIX) { | if (vsi->shared->isc_intr == IFLIB_INTR_MSIX) { | ||||
for (int i = 0; i < vsi->num_rx_queues; i++, que++) | for (int i = 0; i < vsi->num_rx_queues; i++, que++) | ||||
ixl_enable_queue(hw, que->rxr.me); | ixl_enable_queue(hw, que->rxr.me); | ||||
} else | } else | ||||
ixl_enable_intr0(hw); | ixl_enable_intr0(hw); | ||||
} | } | ||||
void | void | ||||
▲ Show 20 Lines • Show All 243 Lines • ▼ Show 20 Lines | if (error) | ||||
device_printf(dev, | device_printf(dev, | ||||
"Shutdown Admin queue failed with code %d\n", error); | "Shutdown Admin queue failed with code %d\n", error); | ||||
ixl_pf_qmgr_release(&pf->qmgr, &pf->qtag); | ixl_pf_qmgr_release(&pf->qmgr, &pf->qtag); | ||||
return (error); | return (error); | ||||
} | } | ||||
int | int | ||||
ixl_rebuild_hw_structs_after_reset(struct ixl_pf *pf, bool is_up) | ixl_rebuild_hw_structs_after_reset(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; | ||||
device_t dev = pf->dev; | device_t dev = pf->dev; | ||||
int error = 0; | int error = 0; | ||||
device_printf(dev, "Rebuilding driver state...\n"); | device_printf(dev, "Rebuilding driver state...\n"); | ||||
Show All 40 Lines | device_printf(dev, "Failed to reserve queues for PF LAN VSI, error %d\n", | ||||
error); | error); | ||||
/* TODO: error handling */ | /* TODO: error handling */ | ||||
} | } | ||||
error = ixl_switch_config(pf); | error = ixl_switch_config(pf); | ||||
if (error) { | if (error) { | ||||
device_printf(dev, "ixl_rebuild_hw_structs_after_reset: ixl_switch_config() failed: %d\n", | device_printf(dev, "ixl_rebuild_hw_structs_after_reset: ixl_switch_config() failed: %d\n", | ||||
error); | error); | ||||
error = EIO; | |||||
goto ixl_rebuild_hw_structs_after_reset_err; | goto ixl_rebuild_hw_structs_after_reset_err; | ||||
} | } | ||||
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 ixl_rebuild_hw_structs_after_reset_err; | |||||
} | |||||
u8 set_fc_err_mask; | |||||
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); | |||||
error = EIO; | |||||
goto ixl_rebuild_hw_structs_after_reset_err; | |||||
} | |||||
/* Remove default filters reinstalled by FW on reset */ | /* Remove default filters reinstalled by FW on reset */ | ||||
ixl_del_default_hw_filters(vsi); | ixl_del_default_hw_filters(vsi); | ||||
/* Determine link state */ | /* Determine link state */ | ||||
if (ixl_attach_get_link_status(pf)) { | if (ixl_attach_get_link_status(pf)) { | ||||
error = EINVAL; | error = EINVAL; | ||||
/* TODO: error handling */ | /* TODO: error handling */ | ||||
} | } | ||||
Show All 33 Lines | while (count++ < 100) { | ||||
if (reg) | if (reg) | ||||
i40e_msec_delay(100); | i40e_msec_delay(100); | ||||
else | else | ||||
break; | break; | ||||
} | } | ||||
ixl_dbg(pf, IXL_DBG_INFO, | ixl_dbg(pf, IXL_DBG_INFO, | ||||
"Reset wait count: %d\n", count); | "Reset wait count: %d\n", count); | ||||
ixl_rebuild_hw_structs_after_reset(pf, is_up); | ixl_rebuild_hw_structs_after_reset(pf); | ||||
atomic_clear_int(&pf->state, IXL_PF_STATE_ADAPTER_RESETTING); | atomic_clear_int(&pf->state, IXL_PF_STATE_ADAPTER_RESETTING); | ||||
} | } | ||||
/** | /** | ||||
* Update VSI-specific ethernet statistics counters. | * Update VSI-specific ethernet statistics counters. | ||||
**/ | **/ | ||||
void | void | ||||
▲ Show 20 Lines • Show All 343 Lines • ▼ Show 20 Lines | if (pf->has_i2c) { | ||||
SYSCTL_ADD_PROC(ctx, debug_list, | SYSCTL_ADD_PROC(ctx, debug_list, | ||||
OID_AUTO, "write_i2c_byte", CTLTYPE_INT | CTLFLAG_RW, | OID_AUTO, "write_i2c_byte", CTLTYPE_INT | CTLFLAG_RW, | ||||
pf, 0, ixl_sysctl_write_i2c_byte, "I", IXL_SYSCTL_HELP_WRITE_I2C); | pf, 0, ixl_sysctl_write_i2c_byte, "I", IXL_SYSCTL_HELP_WRITE_I2C); | ||||
SYSCTL_ADD_PROC(ctx, debug_list, | SYSCTL_ADD_PROC(ctx, debug_list, | ||||
OID_AUTO, "read_i2c_diag_data", CTLTYPE_STRING | CTLFLAG_RD, | OID_AUTO, "read_i2c_diag_data", CTLTYPE_STRING | CTLFLAG_RD, | ||||
pf, 0, ixl_sysctl_read_i2c_diag_data, "A", "Dump selected diagnostic data from FW"); | pf, 0, ixl_sysctl_read_i2c_diag_data, "A", "Dump selected diagnostic data from FW"); | ||||
} | } | ||||
#ifdef PCI_IOV | |||||
SYSCTL_ADD_UINT(ctx, debug_list, | |||||
OID_AUTO, "vc_debug_level", CTLFLAG_RW, &pf->vc_debug_lvl, | |||||
0, "PF/VF Virtual Channel debug level"); | |||||
#endif | |||||
} | } | ||||
/* | /* | ||||
* Primarily for finding out how many queues can be assigned to VFs, | * Primarily for finding out how many queues can be assigned to VFs, | ||||
* at runtime. | * at runtime. | ||||
*/ | */ | ||||
static int | static int | ||||
ixl_sysctl_unallocated_queues(SYSCTL_HANDLER_ARGS) | ixl_sysctl_unallocated_queues(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct ixl_pf *pf = (struct ixl_pf *)arg1; | struct ixl_pf *pf = (struct ixl_pf *)arg1; | ||||
int queues; | int queues; | ||||
//IXL_PF_LOCK(pf); | |||||
queues = (int)ixl_pf_qmgr_get_num_free(&pf->qmgr); | queues = (int)ixl_pf_qmgr_get_num_free(&pf->qmgr); | ||||
//IXL_PF_UNLOCK(pf); | |||||
return sysctl_handle_int(oidp, NULL, queues, req); | return sysctl_handle_int(oidp, NULL, queues, req); | ||||
} | } | ||||
/* | /* | ||||
** Set flow control using sysctl: | ** Set flow control using sysctl: | ||||
** 0 - off | ** 0 - off | ||||
** 1 - rx pause | ** 1 - rx pause | ||||
▲ Show 20 Lines • Show All 647 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static int | static int | ||||
ixl_sysctl_sw_filter_list(SYSCTL_HANDLER_ARGS) | ixl_sysctl_sw_filter_list(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct ixl_pf *pf = (struct ixl_pf *)arg1; | struct ixl_pf *pf = (struct ixl_pf *)arg1; | ||||
struct ixl_vsi *vsi = &pf->vsi; | struct ixl_vsi *vsi = &pf->vsi; | ||||
struct ixl_mac_filter *f; | struct ixl_mac_filter *f; | ||||
char *buf, *buf_i; | device_t dev = pf->dev; | ||||
int error = 0, ftl_len = 0, ftl_counter = 0; | |||||
int error = 0; | struct sbuf *buf; | ||||
int ftl_len = 0; | |||||
int ftl_counter = 0; | |||||
int buf_len = 0; | |||||
int entry_len = 42; | |||||
SLIST_FOREACH(f, &vsi->ftl, next) { | buf = sbuf_new_for_sysctl(NULL, NULL, 128, req); | ||||
ftl_len++; | if (!buf) { | ||||
device_printf(dev, "Could not allocate sbuf for output.\n"); | |||||
return (ENOMEM); | |||||
} | } | ||||
if (ftl_len < 1) { | sbuf_printf(buf, "\n"); | ||||
sysctl_handle_string(oidp, "(none)", 6, req); | |||||
return (0); | |||||
} | |||||
buf_len = sizeof(char) * (entry_len + 1) * ftl_len + 2; | /* Print MAC filters */ | ||||
buf = buf_i = malloc(buf_len, M_DEVBUF, M_WAITOK); | sbuf_printf(buf, "PF Filters:\n"); | ||||
SLIST_FOREACH(f, &vsi->ftl, next) | |||||
ftl_len++; | |||||
sprintf(buf_i++, "\n"); | if (ftl_len < 1) | ||||
sbuf_printf(buf, "(none)\n"); | |||||
else { | |||||
SLIST_FOREACH(f, &vsi->ftl, next) { | SLIST_FOREACH(f, &vsi->ftl, next) { | ||||
sprintf(buf_i, | sbuf_printf(buf, | ||||
MAC_FORMAT ", vlan %4d, flags %#06x", | MAC_FORMAT ", vlan %4d, flags %#06x", | ||||
MAC_FORMAT_ARGS(f->macaddr), f->vlan, f->flags); | MAC_FORMAT_ARGS(f->macaddr), f->vlan, f->flags); | ||||
buf_i += entry_len; | |||||
/* don't print '\n' for last entry */ | /* don't print '\n' for last entry */ | ||||
if (++ftl_counter != ftl_len) { | if (++ftl_counter != ftl_len) | ||||
sprintf(buf_i, "\n"); | sbuf_printf(buf, "\n"); | ||||
buf_i++; | |||||
} | } | ||||
} | } | ||||
error = sysctl_handle_string(oidp, buf, strlen(buf), req); | #ifdef PCI_IOV | ||||
/* TODO: Give each VF its own filter list sysctl */ | |||||
struct ixl_vf *vf; | |||||
if (pf->num_vfs > 0) { | |||||
sbuf_printf(buf, "\n\n"); | |||||
for (int i = 0; i < pf->num_vfs; i++) { | |||||
vf = &pf->vfs[i]; | |||||
if (!(vf->vf_flags & VF_FLAG_ENABLED)) | |||||
continue; | |||||
vsi = &vf->vsi; | |||||
ftl_len = 0, ftl_counter = 0; | |||||
sbuf_printf(buf, "VF-%d Filters:\n", vf->vf_num); | |||||
SLIST_FOREACH(f, &vsi->ftl, next) | |||||
ftl_len++; | |||||
if (ftl_len < 1) | |||||
sbuf_printf(buf, "(none)\n"); | |||||
else { | |||||
SLIST_FOREACH(f, &vsi->ftl, next) { | |||||
sbuf_printf(buf, | |||||
MAC_FORMAT ", vlan %4d, flags %#06x\n", | |||||
MAC_FORMAT_ARGS(f->macaddr), f->vlan, f->flags); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
#endif | |||||
error = sbuf_finish(buf); | |||||
if (error) | if (error) | ||||
printf("sysctl error: %d\n", error); | device_printf(dev, "Error finishing sbuf: %d\n", error); | ||||
free(buf, M_DEVBUF); | sbuf_delete(buf); | ||||
return error; | |||||
return (error); | |||||
} | } | ||||
#define IXL_SW_RES_SIZE 0x14 | #define IXL_SW_RES_SIZE 0x14 | ||||
int | int | ||||
ixl_res_alloc_cmp(const void *a, const void *b) | ixl_res_alloc_cmp(const void *a, const void *b) | ||||
{ | { | ||||
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; | ||||
▲ Show 20 Lines • Show All 985 Lines • Show Last 20 Lines |
I just hate that there are 2 functions which do almost the same thing. Can't you just change the args to ixl_disable_rings() to take pf and qtag and do this in a single function?