#include "ixl_pf.h"
#ifdef PCI_IOV
#include "ixl_pf_iov.h"
#endif
#ifdef IXL_IW
#include "ixl_iw.h"
#include "ixl_iw_int.h"
#endif
static u8 ixl_convert_sysctl_aq_link_speed(u8, bool);
static void ixl_sbuf_print_bytes(struct sbuf *, u8 *, int, int, bool);
static const char * ixl_link_speed_string(enum i40e_aq_link_speed);
static u_int ixl_add_maddr(void *, struct sockaddr_dl *, u_int);
static u_int ixl_match_maddr(void *, struct sockaddr_dl *, u_int);
static char * ixl_switch_element_string(struct sbuf *, u8, u16);
static enum ixl_fw_mode ixl_get_fw_mode(struct ixl_pf *);
static int ixl_sysctl_set_advertise(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_show_fw(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_unallocated_queues(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_pf_tx_itr(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_pf_rx_itr(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_eee_enable(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_set_link_active(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_link_status(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_phy_abilities(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_phy_statistics(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);
static int ixl_sysctl_switch_vlans(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_hkey(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_hena(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_hlut(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_fw_link_management(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_read_i2c_byte(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_write_i2c_byte(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_fec_fc_ability(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_fec_rs_ability(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_fec_fc_request(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_fec_rs_request(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_fec_auto_enable(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_dump_debug_data(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_fw_lldp(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_read_i2c_diag_data(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_do_pf_reset(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_do_core_reset(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_do_global_reset(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_queue_interrupt_table(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_debug_queue_int_ctln(SYSCTL_HANDLER_ARGS);
#ifdef IXL_DEBUG
static int ixl_sysctl_qtx_tail_handler(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_qrx_tail_handler(SYSCTL_HANDLER_ARGS);
#endif
#ifdef IXL_IW
extern int ixl_enable_iwarp;
extern int ixl_limit_iwarp_msix;
#endif
static const char * const ixl_fc_string[6] = {
"None",
"Rx",
"Tx",
"Full",
"Priority",
"Default"
};
static char *ixl_fec_string[3] = {
"CL108 RS-FEC",
"CL74 FC-FEC/BASE-R",
"None"
};
inline void
ixl_set_state(volatile u32 *s, enum ixl_state bit)
{
atomic_set_32(s, BIT(bit));
}
inline void
ixl_clear_state(volatile u32 *s, enum ixl_state bit)
{
atomic_clear_32(s, BIT(bit));
}
inline bool
ixl_test_state(volatile u32 *s, enum ixl_state bit)
{
return !!(*s & BIT(bit));
}
inline u32
ixl_testandset_state(volatile u32 *s, enum ixl_state bit)
{
return atomic_testandset_32(s, bit);
}
MALLOC_DEFINE(M_IXL, "ixl", "ixl driver allocations");
void
ixl_nvm_version_str(struct i40e_hw *hw, struct sbuf *buf)
{
u8 oem_ver = (u8)(hw->nvm.oem_ver >> 24);
u16 oem_build = (u16)((hw->nvm.oem_ver >> 16) & 0xFFFF);
u8 oem_patch = (u8)(hw->nvm.oem_ver & 0xFF);
sbuf_printf(buf,
"fw %d.%d.%05d api %d.%d nvm %x.%02x etid %08x oem %d.%d.%d",
hw->aq.fw_maj_ver, hw->aq.fw_min_ver, hw->aq.fw_build,
hw->aq.api_maj_ver, hw->aq.api_min_ver,
(hw->nvm.version & IXL_NVM_VERSION_HI_MASK) >>
IXL_NVM_VERSION_HI_SHIFT,
(hw->nvm.version & IXL_NVM_VERSION_LO_MASK) >>
IXL_NVM_VERSION_LO_SHIFT,
hw->nvm.eetrack,
oem_ver, oem_build, oem_patch);
}
void
ixl_print_nvm_version(struct ixl_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
device_t dev = pf->dev;
struct sbuf *sbuf;
sbuf = sbuf_new_auto();
ixl_nvm_version_str(hw, sbuf);
sbuf_finish(sbuf);
device_printf(dev, "%s\n", sbuf_data(sbuf));
sbuf_delete(sbuf);
}
static enum ixl_fw_mode
ixl_get_fw_mode(struct ixl_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
enum ixl_fw_mode fw_mode = IXL_FW_MODE_NORMAL;
u32 fwsts;
#ifdef IXL_DEBUG
if (pf->recovery_mode)
return IXL_FW_MODE_RECOVERY;
#endif
fwsts = rd32(hw, I40E_GL_FWSTS) & I40E_GL_FWSTS_FWS1B_MASK;
if ((fwsts >= I40E_XL710_GL_FWSTS_FWS1B_REC_MOD_CORER_MASK &&
fwsts <= I40E_XL710_GL_FWSTS_FWS1B_REC_MOD_NVM_MASK) ||
fwsts == I40E_X722_GL_FWSTS_FWS1B_REC_MOD_GLOBR_MASK ||
fwsts == I40E_X722_GL_FWSTS_FWS1B_REC_MOD_CORER_MASK)
fw_mode = IXL_FW_MODE_RECOVERY;
else {
if (fwsts > I40E_GL_FWSTS_FWS1B_EMPR_0 &&
fwsts <= I40E_GL_FWSTS_FWS1B_EMPR_10)
fw_mode = IXL_FW_MODE_UEMPR;
}
return (fw_mode);
}
int
ixl_pf_reset(struct ixl_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
enum i40e_status_code status;
enum ixl_fw_mode fw_mode;
fw_mode = ixl_get_fw_mode(pf);
ixl_dbg_info(pf, "%s: before PF reset FW mode: 0x%08x\n", __func__, fw_mode);
if (fw_mode == IXL_FW_MODE_RECOVERY) {
ixl_set_state(&pf->state, IXL_STATE_RECOVERY_MODE);
return (0);
}
status = i40e_pf_reset(hw);
if (status == I40E_SUCCESS)
return (0);
fw_mode = ixl_get_fw_mode(pf);
ixl_dbg_info(pf, "%s: after PF reset FW mode: 0x%08x\n", __func__, fw_mode);
if (fw_mode == IXL_FW_MODE_RECOVERY) {
ixl_set_state(&pf->state, IXL_STATE_RECOVERY_MODE);
return (0);
}
if (fw_mode == IXL_FW_MODE_UEMPR)
device_printf(pf->dev,
"Entering recovery mode due to repeated FW resets. This may take several minutes. Refer to the Intel(R) Ethernet Adapters and Devices User Guide.\n");
else
device_printf(pf->dev, "PF reset failure %s\n",
i40e_stat_str(hw, status));
return (EIO);
}
int
ixl_setup_hmc(struct ixl_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
enum i40e_status_code status;
status = i40e_init_lan_hmc(hw, hw->func_caps.num_tx_qp,
hw->func_caps.num_rx_qp, 0, 0);
if (status) {
device_printf(pf->dev, "init_lan_hmc failed: %s\n",
i40e_stat_str(hw, status));
return (EIO);
}
status = i40e_configure_lan_hmc(hw, I40E_HMC_MODEL_DIRECT_ONLY);
if (status) {
device_printf(pf->dev, "configure_lan_hmc failed: %s\n",
i40e_stat_str(hw, status));
return (EIO);
}
return (0);
}
void
ixl_shutdown_hmc(struct ixl_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
enum i40e_status_code status;
if (hw->hmc.hmc_obj == NULL)
return;
status = i40e_shutdown_lan_hmc(hw);
if (status)
device_printf(pf->dev,
"Shutdown LAN HMC failed with code %s\n",
i40e_stat_str(hw, status));
}
void
ixl_configure_itr(struct ixl_pf *pf)
{
ixl_configure_tx_itr(pf);
ixl_configure_rx_itr(pf);
}
int
ixl_get_hw_capabilities(struct ixl_pf *pf)
{
struct i40e_aqc_list_capabilities_element_resp *buf;
struct i40e_hw *hw = &pf->hw;
device_t dev = pf->dev;
enum i40e_status_code status;
int len, i2c_intfc_num;
bool again = TRUE;
u16 needed;
if (IXL_PF_IN_RECOVERY_MODE(pf)) {
hw->func_caps.iwarp = 0;
return (0);
}
len = 40 * sizeof(struct i40e_aqc_list_capabilities_element_resp);
retry:
if (!(buf = (struct i40e_aqc_list_capabilities_element_resp *)
malloc(len, M_IXL, M_NOWAIT | M_ZERO))) {
device_printf(dev, "Unable to allocate cap memory\n");
return (ENOMEM);
}
status = i40e_aq_discover_capabilities(hw, buf, len,
&needed, i40e_aqc_opc_list_func_capabilities, NULL);
free(buf, M_IXL);
if ((pf->hw.aq.asq_last_status == I40E_AQ_RC_ENOMEM) &&
(again == TRUE)) {
again = FALSE;
len = needed;
goto retry;
} else if (status != I40E_SUCCESS) {
device_printf(dev, "capability discovery failed; status %s, error %s\n",
i40e_stat_str(hw, status), i40e_aq_str(hw, hw->aq.asq_last_status));
return (ENODEV);
}
i2c_intfc_num = ixl_find_i2c_interface(pf);
if (i2c_intfc_num != -1)
pf->has_i2c = true;
switch (pf->i2c_access_method) {
case IXL_I2C_ACCESS_METHOD_BEST_AVAILABLE: {
if (hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE) {
pf->read_i2c_byte = ixl_read_i2c_byte_aq;
pf->write_i2c_byte = ixl_write_i2c_byte_aq;
} else {
pf->read_i2c_byte = ixl_read_i2c_byte_reg;
pf->write_i2c_byte = ixl_write_i2c_byte_reg;
}
break;
}
case IXL_I2C_ACCESS_METHOD_AQ:
pf->read_i2c_byte = ixl_read_i2c_byte_aq;
pf->write_i2c_byte = ixl_write_i2c_byte_aq;
break;
case IXL_I2C_ACCESS_METHOD_REGISTER_I2CCMD:
pf->read_i2c_byte = ixl_read_i2c_byte_reg;
pf->write_i2c_byte = ixl_write_i2c_byte_reg;
break;
case IXL_I2C_ACCESS_METHOD_BIT_BANG_I2CPARAMS:
pf->read_i2c_byte = ixl_read_i2c_byte_bb;
pf->write_i2c_byte = ixl_write_i2c_byte_bb;
break;
default:
device_printf(dev, "Error setting I2C access functions\n");
break;
}
ixl_set_state(&pf->state, IXL_STATE_LINK_ACTIVE_ON_DOWN);
device_printf(dev,
"PF-ID[%d]: VFs %d, MSI-X %d, VF MSI-X %d, QPs %d, %s\n",
hw->pf_id, hw->func_caps.num_vfs, hw->func_caps.num_msix_vectors,
hw->func_caps.num_msix_vectors_vf, hw->func_caps.num_tx_qp,
(hw->func_caps.mdio_port_mode == 2) ? "I2C" :
(hw->func_caps.mdio_port_mode == 1 && pf->has_i2c) ? "MDIO & I2C" :
(hw->func_caps.mdio_port_mode == 1) ? "MDIO dedicated" :
"MDIO shared");
return (0);
}
void
ixl_set_initial_advertised_speeds(struct ixl_pf *pf)
{
device_t dev = pf->dev;
int err;
err = ixl_set_advertised_speeds(pf, pf->supported_speeds, true);
if (err) {
device_printf(dev, "%s: ixl_set_advertised_speeds() error %d\n",
__func__, err);
return;
}
pf->advertised_speed =
ixl_convert_sysctl_aq_link_speed(pf->supported_speeds, false);
}
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;
if (hw->hmc.hmc_obj) {
status = i40e_shutdown_lan_hmc(hw);
if (status) {
device_printf(dev,
"init: LAN HMC shutdown failure; status %s\n",
i40e_stat_str(hw, status));
goto err_out;
}
}
ixl_disable_intr0(hw);
status = i40e_shutdown_adminq(hw);
if (status)
device_printf(dev,
"init: Admin Queue shutdown failure; status %s\n",
i40e_stat_str(hw, status));
ixl_pf_qmgr_release(&pf->qmgr, &pf->qtag);
err_out:
return (status);
}
static struct ixl_mac_filter *
ixl_new_filter(struct ixl_ftl_head *headp, const u8 *macaddr, s16 vlan)
{
struct ixl_mac_filter *f;
f = malloc(sizeof(struct ixl_mac_filter),
M_IXL, M_NOWAIT | M_ZERO);
if (f) {
LIST_INSERT_HEAD(headp, f, ftle);
bcopy(macaddr, f->macaddr, ETHER_ADDR_LEN);
f->vlan = vlan;
}
return (f);
}
void
ixl_free_filters(struct ixl_ftl_head *headp)
{
struct ixl_mac_filter *f, *nf;
f = LIST_FIRST(headp);
while (f != NULL) {
nf = LIST_NEXT(f, ftle);
free(f, M_IXL);
f = nf;
}
LIST_INIT(headp);
}
static u_int
ixl_add_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
{
struct ixl_add_maddr_arg *ama = arg;
struct ixl_vsi *vsi = ama->vsi;
const u8 *macaddr = (u8*)LLADDR(sdl);
struct ixl_mac_filter *f;
f = ixl_find_filter(&vsi->ftl, macaddr, IXL_VLAN_ANY);
if (f != NULL)
return (0);
f = ixl_new_filter(&ama->to_add, macaddr, IXL_VLAN_ANY);
if (f == NULL) {
device_printf(vsi->dev, "WARNING: no filter available!!\n");
return (0);
}
f->flags |= IXL_FILTER_MC;
return (1);
}
void
ixl_add_multi(struct ixl_vsi *vsi)
{
if_t ifp = vsi->ifp;
int mcnt = 0;
struct ixl_add_maddr_arg cb_arg;
IOCTL_DEBUGOUT("ixl_add_multi: begin");
cb_arg.vsi = vsi;
LIST_INIT(&cb_arg.to_add);
mcnt = if_foreach_llmaddr(ifp, ixl_add_maddr, &cb_arg);
if (mcnt > 0)
ixl_add_hw_filters(vsi, &cb_arg.to_add, mcnt);
IOCTL_DEBUGOUT("ixl_add_multi: end");
}
static u_int
ixl_match_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
{
struct ixl_mac_filter *f = arg;
if (ixl_ether_is_equal(f->macaddr, (u8 *)LLADDR(sdl)))
return (1);
else
return (0);
}
static void
ixl_dis_multi_promisc(struct ixl_vsi *vsi, int vsi_mcnt)
{
struct ifnet *ifp = vsi->ifp;
struct i40e_hw *hw = vsi->hw;
int ifp_mcnt = 0;
enum i40e_status_code status;
if (!(vsi->flags & IXL_FLAGS_MC_PROMISC) ||
(if_getflags(ifp) & (IFF_PROMISC | IFF_ALLMULTI)))
return;
ifp_mcnt = if_llmaddr_count(ifp);
if (ifp_mcnt == vsi_mcnt || ifp_mcnt == 0 ||
ifp_mcnt >= MAX_MULTICAST_ADDR)
return;
status = i40e_aq_set_vsi_multicast_promiscuous(hw, vsi->seid,
FALSE, NULL);
if (status != I40E_SUCCESS) {
if_printf(ifp, "Failed to disable multicast promiscuous "
"mode, status: %s\n", i40e_stat_str(hw, status));
return;
}
vsi->flags &= ~IXL_FLAGS_MC_PROMISC;
if_printf(ifp, "Disabled multicast promiscuous mode\n");
ixl_add_multi(vsi);
}
void
ixl_del_multi(struct ixl_vsi *vsi, bool all)
{
int to_del_cnt = 0, vsi_mcnt = 0;
if_t ifp = vsi->ifp;
struct ixl_mac_filter *f, *fn;
struct ixl_ftl_head to_del;
IOCTL_DEBUGOUT("ixl_del_multi: begin");
LIST_INIT(&to_del);
LIST_FOREACH_SAFE(f, &vsi->ftl, ftle, fn) {
if ((f->flags & IXL_FILTER_MC) == 0)
continue;
vsi_mcnt++;
if (!all && if_foreach_llmaddr(ifp, ixl_match_maddr, f) != 0)
continue;
LIST_REMOVE(f, ftle);
LIST_INSERT_HEAD(&to_del, f, ftle);
to_del_cnt++;
}
if (to_del_cnt > 0) {
ixl_del_hw_filters(vsi, &to_del, to_del_cnt);
return;
}
ixl_dis_multi_promisc(vsi, vsi_mcnt);
IOCTL_DEBUGOUT("ixl_del_multi: end");
}
void
ixl_link_up_msg(struct ixl_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
if_t ifp = pf->vsi.ifp;
char *req_fec_string, *neg_fec_string;
u8 fec_abilities;
fec_abilities = hw->phy.link_info.req_fec_info;
if (fec_abilities & I40E_AQ_REQUEST_FEC_RS)
req_fec_string = ixl_fec_string[0];
else if (fec_abilities & I40E_AQ_REQUEST_FEC_KR)
req_fec_string = ixl_fec_string[1];
else
req_fec_string = ixl_fec_string[2];
if (hw->phy.link_info.fec_info & I40E_AQ_CONFIG_FEC_RS_ENA)
neg_fec_string = ixl_fec_string[0];
else if (hw->phy.link_info.fec_info & I40E_AQ_CONFIG_FEC_KR_ENA)
neg_fec_string = ixl_fec_string[1];
else
neg_fec_string = ixl_fec_string[2];
log(LOG_NOTICE, "%s: Link is up, %s Full Duplex, Requested FEC: %s, Negotiated FEC: %s, Autoneg: %s, Flow Control: %s\n",
if_name(ifp),
ixl_link_speed_string(hw->phy.link_info.link_speed),
req_fec_string, neg_fec_string,
(hw->phy.link_info.an_info & I40E_AQ_AN_COMPLETED) ? "True" : "False",
(hw->phy.link_info.an_info & I40E_AQ_LINK_PAUSE_TX &&
hw->phy.link_info.an_info & I40E_AQ_LINK_PAUSE_RX) ?
ixl_fc_string[3] : (hw->phy.link_info.an_info & I40E_AQ_LINK_PAUSE_TX) ?
ixl_fc_string[2] : (hw->phy.link_info.an_info & I40E_AQ_LINK_PAUSE_RX) ?
ixl_fc_string[1] : ixl_fc_string[0]);
}
void
ixl_configure_intr0_msix(struct ixl_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
u32 reg;
wr32(hw, I40E_PFINT_ICR0_ENA, 0);
rd32(hw, I40E_PFINT_ICR0);
reg = I40E_PFINT_ICR0_ENA_ECC_ERR_MASK |
I40E_PFINT_ICR0_ENA_GRST_MASK |
I40E_PFINT_ICR0_ENA_HMC_ERR_MASK |
I40E_PFINT_ICR0_ENA_ADMINQ_MASK |
I40E_PFINT_ICR0_ENA_MAL_DETECT_MASK |
I40E_PFINT_ICR0_ENA_VFLR_MASK |
I40E_PFINT_ICR0_ENA_PE_CRITERR_MASK |
I40E_PFINT_ICR0_ENA_PCI_EXCEPTION_MASK;
wr32(hw, I40E_PFINT_ICR0_ENA, reg);
wr32(hw, I40E_PFINT_LNKLST0, 0x7FF);
wr32(hw, I40E_PFINT_ITR0(IXL_RX_ITR), 0x3E);
wr32(hw, I40E_PFINT_DYN_CTL0,
I40E_PFINT_DYN_CTL0_SW_ITR_INDX_MASK |
I40E_PFINT_DYN_CTL0_INTENA_MSK_MASK);
wr32(hw, I40E_PFINT_STAT_CTL0, 0);
}
void
ixl_add_ifmedia(struct ifmedia *media, u64 phy_types)
{
if (phy_types & (I40E_CAP_PHY_TYPE_100BASE_TX))
ifmedia_add(media, IFM_ETHER | IFM_100_TX, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_1000BASE_T))
ifmedia_add(media, IFM_ETHER | IFM_1000_T, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_1000BASE_SX))
ifmedia_add(media, IFM_ETHER | IFM_1000_SX, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_1000BASE_LX))
ifmedia_add(media, IFM_ETHER | IFM_1000_LX, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_2_5GBASE_T))
ifmedia_add(media, IFM_ETHER | IFM_2500_T, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_5GBASE_T))
ifmedia_add(media, IFM_ETHER | IFM_5000_T, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_XAUI) ||
phy_types & (I40E_CAP_PHY_TYPE_XFI) ||
phy_types & (I40E_CAP_PHY_TYPE_10GBASE_SFPP_CU))
ifmedia_add(media, IFM_ETHER | IFM_10G_TWINAX, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_10GBASE_SR))
ifmedia_add(media, IFM_ETHER | IFM_10G_SR, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_10GBASE_LR))
ifmedia_add(media, IFM_ETHER | IFM_10G_LR, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_10GBASE_T))
ifmedia_add(media, IFM_ETHER | IFM_10G_T, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_40GBASE_CR4) ||
phy_types & (I40E_CAP_PHY_TYPE_40GBASE_CR4_CU) ||
phy_types & (I40E_CAP_PHY_TYPE_40GBASE_AOC) ||
phy_types & (I40E_CAP_PHY_TYPE_XLAUI) ||
phy_types & (I40E_CAP_PHY_TYPE_40GBASE_KR4))
ifmedia_add(media, IFM_ETHER | IFM_40G_CR4, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_40GBASE_SR4))
ifmedia_add(media, IFM_ETHER | IFM_40G_SR4, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_40GBASE_LR4))
ifmedia_add(media, IFM_ETHER | IFM_40G_LR4, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_1000BASE_KX))
ifmedia_add(media, IFM_ETHER | IFM_1000_KX, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_10GBASE_CR1_CU)
|| phy_types & (I40E_CAP_PHY_TYPE_10GBASE_CR1))
ifmedia_add(media, IFM_ETHER | IFM_10G_CR1, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_10GBASE_AOC))
ifmedia_add(media, IFM_ETHER | IFM_10G_AOC, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_SFI))
ifmedia_add(media, IFM_ETHER | IFM_10G_SFI, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_10GBASE_KX4))
ifmedia_add(media, IFM_ETHER | IFM_10G_KX4, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_10GBASE_KR))
ifmedia_add(media, IFM_ETHER | IFM_10G_KR, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_20GBASE_KR2))
ifmedia_add(media, IFM_ETHER | IFM_20G_KR2, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_40GBASE_KR4))
ifmedia_add(media, IFM_ETHER | IFM_40G_KR4, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_XLPPI))
ifmedia_add(media, IFM_ETHER | IFM_40G_XLPPI, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_25GBASE_KR))
ifmedia_add(media, IFM_ETHER | IFM_25G_KR, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_25GBASE_CR))
ifmedia_add(media, IFM_ETHER | IFM_25G_CR, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_25GBASE_SR))
ifmedia_add(media, IFM_ETHER | IFM_25G_SR, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_25GBASE_LR))
ifmedia_add(media, IFM_ETHER | IFM_25G_LR, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_25GBASE_AOC))
ifmedia_add(media, IFM_ETHER | IFM_25G_AOC, 0, NULL);
if (phy_types & (I40E_CAP_PHY_TYPE_25GBASE_ACC))
ifmedia_add(media, IFM_ETHER | IFM_25G_ACC, 0, NULL);
}
int
ixl_switch_config(struct ixl_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
struct ixl_vsi *vsi = &pf->vsi;
device_t dev = iflib_get_dev(vsi->ctx);
struct i40e_aqc_get_switch_config_resp *sw_config;
u8 aq_buf[I40E_AQ_LARGE_BUF];
int ret;
u16 next = 0;
memset(&aq_buf, 0, sizeof(aq_buf));
sw_config = (struct i40e_aqc_get_switch_config_resp *)aq_buf;
ret = i40e_aq_get_switch_config(hw, sw_config,
sizeof(aq_buf), &next, NULL);
if (ret) {
device_printf(dev, "aq_get_switch_config() failed, error %d,"
" aq_error %d\n", ret, pf->hw.aq.asq_last_status);
return (ret);
}
if (pf->dbg_mask & IXL_DBG_SWITCH_INFO) {
device_printf(dev,
"Switch config: header reported: %d in structure, %d total\n",
LE16_TO_CPU(sw_config->header.num_reported),
LE16_TO_CPU(sw_config->header.num_total));
for (int i = 0;
i < LE16_TO_CPU(sw_config->header.num_reported); i++) {
device_printf(dev,
"-> %d: type=%d seid=%d uplink=%d downlink=%d\n", i,
sw_config->element[i].element_type,
LE16_TO_CPU(sw_config->element[i].seid),
LE16_TO_CPU(sw_config->element[i].uplink_seid),
LE16_TO_CPU(sw_config->element[i].downlink_seid));
}
}
vsi->uplink_seid = LE16_TO_CPU(sw_config->element[0].uplink_seid);
vsi->downlink_seid = LE16_TO_CPU(sw_config->element[0].downlink_seid);
vsi->seid = LE16_TO_CPU(sw_config->element[0].seid);
return (ret);
}
void
ixl_vsi_add_sysctls(struct ixl_vsi * vsi, const char * sysctl_name, bool queues_sysctls)
{
struct sysctl_oid *tree;
struct sysctl_oid_list *child;
struct sysctl_oid_list *vsi_list;
tree = device_get_sysctl_tree(vsi->dev);
child = SYSCTL_CHILDREN(tree);
vsi->vsi_node = SYSCTL_ADD_NODE(&vsi->sysctl_ctx, child, OID_AUTO, sysctl_name,
CTLFLAG_RD, NULL, "VSI Number");
vsi_list = SYSCTL_CHILDREN(vsi->vsi_node);
ixl_add_sysctls_eth_stats(&vsi->sysctl_ctx, vsi_list, &vsi->eth_stats);
SYSCTL_ADD_UQUAD(&vsi->sysctl_ctx, vsi_list, OID_AUTO, "rx_errors",
CTLFLAG_RD, &vsi->ierrors,
"RX packet errors");
if (queues_sysctls)
ixl_vsi_add_queues_stats(vsi, &vsi->sysctl_ctx);
}
static int
ixl_sysctl_pf_tx_itr(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
device_t dev = pf->dev;
int error = 0;
int requested_tx_itr;
requested_tx_itr = pf->tx_itr;
error = sysctl_handle_int(oidp, &requested_tx_itr, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
if (pf->dynamic_tx_itr) {
device_printf(dev,
"Cannot set TX itr value while dynamic TX itr is enabled\n");
return (EINVAL);
}
if (requested_tx_itr < 0 || requested_tx_itr > IXL_MAX_ITR) {
device_printf(dev,
"Invalid TX itr value; value must be between 0 and %d\n",
IXL_MAX_ITR);
return (EINVAL);
}
pf->tx_itr = requested_tx_itr;
ixl_configure_tx_itr(pf);
return (error);
}
static int
ixl_sysctl_pf_rx_itr(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
device_t dev = pf->dev;
int error = 0;
int requested_rx_itr;
requested_rx_itr = pf->rx_itr;
error = sysctl_handle_int(oidp, &requested_rx_itr, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
if (pf->dynamic_rx_itr) {
device_printf(dev,
"Cannot set RX itr value while dynamic RX itr is enabled\n");
return (EINVAL);
}
if (requested_rx_itr < 0 || requested_rx_itr > IXL_MAX_ITR) {
device_printf(dev,
"Invalid RX itr value; value must be between 0 and %d\n",
IXL_MAX_ITR);
return (EINVAL);
}
pf->rx_itr = requested_rx_itr;
ixl_configure_rx_itr(pf);
return (error);
}
void
ixl_add_sysctls_mac_stats(struct sysctl_ctx_list *ctx,
struct sysctl_oid_list *child,
struct i40e_hw_port_stats *stats)
{
struct sysctl_oid *stat_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO,
"mac", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Mac Statistics");
struct sysctl_oid_list *stat_list = SYSCTL_CHILDREN(stat_node);
struct i40e_eth_stats *eth_stats = &stats->eth;
ixl_add_sysctls_eth_stats(ctx, stat_list, eth_stats);
struct ixl_sysctl_info ctls[] =
{
{&stats->crc_errors, "crc_errors", "CRC Errors"},
{&stats->illegal_bytes, "illegal_bytes", "Illegal Byte Errors"},
{&stats->mac_local_faults, "local_faults", "MAC Local Faults"},
{&stats->mac_remote_faults, "remote_faults", "MAC Remote Faults"},
{&stats->rx_length_errors, "rx_length_errors", "Receive Length Errors"},
{&stats->rx_size_64, "rx_frames_64", "64 byte frames received"},
{&stats->rx_size_127, "rx_frames_65_127", "65-127 byte frames received"},
{&stats->rx_size_255, "rx_frames_128_255", "128-255 byte frames received"},
{&stats->rx_size_511, "rx_frames_256_511", "256-511 byte frames received"},
{&stats->rx_size_1023, "rx_frames_512_1023", "512-1023 byte frames received"},
{&stats->rx_size_1522, "rx_frames_1024_1522", "1024-1522 byte frames received"},
{&stats->rx_size_big, "rx_frames_big", "1523-9522 byte frames received"},
{&stats->rx_undersize, "rx_undersize", "Undersized packets received"},
{&stats->rx_fragments, "rx_fragmented", "Fragmented packets received"},
{&stats->rx_oversize, "rx_oversized", "Oversized packets received"},
{&stats->rx_jabber, "rx_jabber", "Received Jabber"},
{&stats->checksum_error, "checksum_errors", "Checksum Errors"},
{&stats->tx_size_64, "tx_frames_64", "64 byte frames transmitted"},
{&stats->tx_size_127, "tx_frames_65_127", "65-127 byte frames transmitted"},
{&stats->tx_size_255, "tx_frames_128_255", "128-255 byte frames transmitted"},
{&stats->tx_size_511, "tx_frames_256_511", "256-511 byte frames transmitted"},
{&stats->tx_size_1023, "tx_frames_512_1023", "512-1023 byte frames transmitted"},
{&stats->tx_size_1522, "tx_frames_1024_1522", "1024-1522 byte frames transmitted"},
{&stats->tx_size_big, "tx_frames_big", "1523-9522 byte frames transmitted"},
{&stats->link_xon_tx, "xon_txd", "Link XON transmitted"},
{&stats->link_xon_rx, "xon_recvd", "Link XON received"},
{&stats->link_xoff_tx, "xoff_txd", "Link XOFF transmitted"},
{&stats->link_xoff_rx, "xoff_recvd", "Link XOFF received"},
{0,0,0}
};
struct ixl_sysctl_info *entry = ctls;
while (entry->stat != 0)
{
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, entry->name,
CTLFLAG_RD, entry->stat,
entry->description);
entry++;
}
}
void
ixl_set_rss_key(struct ixl_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
struct ixl_vsi *vsi = &pf->vsi;
device_t dev = pf->dev;
u32 rss_seed[IXL_RSS_KEY_SIZE_REG];
enum i40e_status_code status;
rss_getkey((uint8_t *) &rss_seed);
if (hw->mac.type == I40E_MAC_X722) {
struct i40e_aqc_get_set_rss_key_data key_data;
bcopy(rss_seed, &key_data, 52);
status = i40e_aq_set_rss_key(hw, vsi->vsi_num, &key_data);
if (status)
device_printf(dev,
"i40e_aq_set_rss_key status %s, error %s\n",
i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
} else {
for (int i = 0; i < IXL_RSS_KEY_SIZE_REG; i++)
i40e_write_rx_ctl(hw, I40E_PFQF_HKEY(i), rss_seed[i]);
}
}
void
ixl_set_rss_pctypes(struct ixl_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
u64 set_hena = 0, hena;
u32 rss_hash_config;
rss_hash_config = rss_gethashconfig();
if (rss_hash_config & RSS_HASHTYPE_RSS_IPV4)
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER);
if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV4)
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP);
if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV4)
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP);
if (rss_hash_config & RSS_HASHTYPE_RSS_IPV6)
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_OTHER);
if (rss_hash_config & RSS_HASHTYPE_RSS_IPV6_EX)
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6);
if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV6)
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP);
if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV6)
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP);
hena = (u64)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(0)) |
((u64)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(1)) << 32);
hena |= set_hena;
i40e_write_rx_ctl(hw, I40E_PFQF_HENA(0), (u32)hena);
i40e_write_rx_ctl(hw, I40E_PFQF_HENA(1), (u32)(hena >> 32));
}
void
ixl_config_rss(struct ixl_pf *pf)
{
ixl_set_rss_key(pf);
ixl_set_rss_pctypes(pf);
ixl_set_rss_hlut(pf);
}
void
ixl_del_default_hw_filters(struct ixl_vsi *vsi)
{
struct i40e_aqc_remove_macvlan_element_data e;
bzero(&e, sizeof(e));
bcopy(vsi->hw->mac.perm_addr, e.mac_addr, ETHER_ADDR_LEN);
e.vlan_tag = 0;
e.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH;
i40e_aq_remove_macvlan(vsi->hw, vsi->seid, &e, 1, NULL);
bzero(&e, sizeof(e));
bcopy(vsi->hw->mac.perm_addr, e.mac_addr, ETHER_ADDR_LEN);
e.vlan_tag = 0;
e.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH |
I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
i40e_aq_remove_macvlan(vsi->hw, vsi->seid, &e, 1, NULL);
}
void
ixl_init_filters(struct ixl_vsi *vsi)
{
struct ixl_pf *pf = (struct ixl_pf *)vsi->back;
ixl_dbg_filter(pf, "%s: start\n", __func__);
LIST_INIT(&vsi->ftl);
vsi->num_hw_filters = 0;
i40e_aq_set_vsi_broadcast(&pf->hw, vsi->seid, TRUE, NULL);
if (IXL_VSI_IS_VF(vsi))
return;
ixl_del_default_hw_filters(vsi);
ixl_add_filter(vsi, vsi->hw->mac.addr, IXL_VLAN_ANY);
#ifndef IXL_DEBUG_FC
i40e_add_filter_to_drop_tx_flow_control_frames(vsi->hw, vsi->seid);
#else
if (pf->enable_tx_fc_filter)
i40e_add_filter_to_drop_tx_flow_control_frames(vsi->hw, vsi->seid);
#endif
}
void
ixl_reconfigure_filters(struct ixl_vsi *vsi)
{
struct i40e_hw *hw = vsi->hw;
struct ixl_ftl_head tmp;
int cnt;
LIST_INIT(&tmp);
LIST_CONCAT(&tmp, &vsi->ftl, ixl_mac_filter, ftle);
cnt = vsi->num_hw_filters;
vsi->num_hw_filters = 0;
ixl_add_hw_filters(vsi, &tmp, cnt);
if (hw == NULL)
return;
ixl_add_filter(vsi, hw->mac.addr, IXL_VLAN_ANY);
if ((if_getcapenable(vsi->ifp) & IFCAP_VLAN_HWFILTER) == 0)
return;
ixl_add_vlan_filters(vsi, hw->mac.addr);
}
void
ixl_add_filter(struct ixl_vsi *vsi, const u8 *macaddr, s16 vlan)
{
struct ixl_mac_filter *f, *tmp;
struct ixl_pf *pf;
device_t dev;
struct ixl_ftl_head to_add;
int to_add_cnt;
pf = vsi->back;
dev = pf->dev;
to_add_cnt = 1;
ixl_dbg_filter(pf, "ixl_add_filter: " MAC_FORMAT ", vlan %4d\n",
MAC_FORMAT_ARGS(macaddr), vlan);
f = ixl_find_filter(&vsi->ftl, macaddr, vlan);
if (f != NULL)
return;
LIST_INIT(&to_add);
f = ixl_new_filter(&to_add, macaddr, vlan);
if (f == NULL) {
device_printf(dev, "WARNING: no filter available!!\n");
return;
}
if (f->vlan != IXL_VLAN_ANY)
f->flags |= IXL_FILTER_VLAN;
else
vsi->num_macs++;
if ((vlan != IXL_VLAN_ANY) && (vsi->num_vlans == 1)) {
tmp = ixl_find_filter(&vsi->ftl, macaddr, IXL_VLAN_ANY);
if (tmp != NULL) {
struct ixl_ftl_head to_del;
f = ixl_new_filter(&to_add, macaddr, 0);
if (f == NULL) {
device_printf(dev, "WARNING: no filter available!!\n");
free(LIST_FIRST(&to_add), M_IXL);
return;
}
to_add_cnt++;
LIST_REMOVE(tmp, ftle);
LIST_INIT(&to_del);
LIST_INSERT_HEAD(&to_del, tmp, ftle);
ixl_del_hw_filters(vsi, &to_del, 1);
}
}
ixl_add_hw_filters(vsi, &to_add, to_add_cnt);
}
void
ixl_add_vlan_filters(struct ixl_vsi *vsi, const u8 *macaddr)
{
struct ixl_ftl_head to_add;
struct ixl_mac_filter *f;
int to_add_cnt = 0;
int i, vlan = 0;
if (vsi->num_vlans == 0 || vsi->num_vlans > IXL_MAX_VLAN_FILTERS) {
ixl_add_filter(vsi, macaddr, IXL_VLAN_ANY);
return;
}
LIST_INIT(&to_add);
f = ixl_find_filter(&vsi->ftl, macaddr, 0);
if (f == NULL) {
f = ixl_new_filter(&to_add, macaddr, 0);
if (f == NULL) {
device_printf(vsi->dev, "WARNING: no filter available!!\n");
return;
}
to_add_cnt++;
}
for (i = 1; i < EVL_VLID_MASK; i = vlan + 1) {
bit_ffs_at(vsi->vlans_map, i, IXL_VLANS_MAP_LEN, &vlan);
if (vlan == -1)
break;
f = ixl_find_filter(&vsi->ftl, macaddr, vlan);
if (f != NULL)
continue;
f = ixl_new_filter(&to_add, macaddr, vlan);
if (f == NULL) {
device_printf(vsi->dev, "WARNING: no filter available!!\n");
ixl_free_filters(&to_add);
return;
}
to_add_cnt++;
}
ixl_add_hw_filters(vsi, &to_add, to_add_cnt);
}
void
ixl_del_filter(struct ixl_vsi *vsi, const u8 *macaddr, s16 vlan)
{
struct ixl_mac_filter *f, *tmp;
struct ixl_ftl_head ftl_head;
int to_del_cnt = 1;
ixl_dbg_filter((struct ixl_pf *)vsi->back,
"ixl_del_filter: " MAC_FORMAT ", vlan %4d\n",
MAC_FORMAT_ARGS(macaddr), vlan);
f = ixl_find_filter(&vsi->ftl, macaddr, vlan);
if (f == NULL)
return;
LIST_REMOVE(f, ftle);
LIST_INIT(&ftl_head);
LIST_INSERT_HEAD(&ftl_head, f, ftle);
if (f->vlan == IXL_VLAN_ANY && (f->flags & IXL_FILTER_VLAN) != 0)
vsi->num_macs--;
if (vlan == IXL_VLAN_ANY || vsi->num_vlans > 0) {
ixl_del_hw_filters(vsi, &ftl_head, to_del_cnt);
return;
}
tmp = ixl_find_filter(&vsi->ftl, macaddr, 0);
if (tmp != NULL) {
LIST_REMOVE(tmp, ftle);
LIST_INSERT_AFTER(f, tmp, ftle);
to_del_cnt++;
}
ixl_del_hw_filters(vsi, &ftl_head, to_del_cnt);
ixl_add_filter(vsi, macaddr, IXL_VLAN_ANY);
}
void
ixl_del_all_vlan_filters(struct ixl_vsi *vsi, const u8 *macaddr)
{
struct ixl_mac_filter *f, *tmp;
struct ixl_ftl_head to_del;
int to_del_cnt = 0;
LIST_INIT(&to_del);
LIST_FOREACH_SAFE(f, &vsi->ftl, ftle, tmp) {
if ((f->flags & IXL_FILTER_MC) != 0 ||
!ixl_ether_is_equal(f->macaddr, macaddr))
continue;
LIST_REMOVE(f, ftle);
LIST_INSERT_HEAD(&to_del, f, ftle);
to_del_cnt++;
}
ixl_dbg_filter((struct ixl_pf *)vsi->back,
"%s: " MAC_FORMAT ", to_del_cnt: %d\n",
__func__, MAC_FORMAT_ARGS(macaddr), to_del_cnt);
if (to_del_cnt > 0)
ixl_del_hw_filters(vsi, &to_del, to_del_cnt);
}
struct ixl_mac_filter *
ixl_find_filter(struct ixl_ftl_head *headp, const u8 *macaddr, s16 vlan)
{
struct ixl_mac_filter *f;
LIST_FOREACH(f, headp, ftle) {
if (ixl_ether_is_equal(f->macaddr, macaddr) &&
(f->vlan == vlan)) {
return (f);
}
}
return (NULL);
}
void
ixl_add_hw_filters(struct ixl_vsi *vsi, struct ixl_ftl_head *to_add, int cnt)
{
struct i40e_aqc_add_macvlan_element_data *a, *b;
struct ixl_mac_filter *f, *fn;
struct ixl_pf *pf;
struct i40e_hw *hw;
device_t dev;
enum i40e_status_code status;
int j = 0;
pf = vsi->back;
dev = vsi->dev;
hw = &pf->hw;
ixl_dbg_filter(pf, "ixl_add_hw_filters: cnt: %d\n", cnt);
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,
M_IXL, M_NOWAIT | M_ZERO);
if (a == NULL) {
device_printf(dev, "add_hw_filters failed to get memory\n");
return;
}
LIST_FOREACH(f, to_add, ftle) {
b = &a[j];
bcopy(f->macaddr, b->mac_addr, ETHER_ADDR_LEN);
if (f->vlan == IXL_VLAN_ANY) {
b->vlan_tag = 0;
b->flags = I40E_AQC_MACVLAN_ADD_IGNORE_VLAN;
} else {
b->vlan_tag = f->vlan;
b->flags = 0;
}
b->flags |= I40E_AQC_MACVLAN_ADD_PERFECT_MATCH;
b->match_method = I40E_AQC_MM_ERR_NO_RES;
ixl_dbg_filter(pf, "ADD: " MAC_FORMAT "\n",
MAC_FORMAT_ARGS(f->macaddr));
if (++j == cnt)
break;
}
if (j != cnt) {
device_printf(dev,
"%s ERROR: list of filters to short expected: %d, found: %d\n",
__func__, cnt, j);
ixl_free_filters(to_add);
goto out_free;
}
status = i40e_aq_add_macvlan(hw, vsi->seid, a, j, NULL);
if (status == I40E_SUCCESS) {
LIST_CONCAT(&vsi->ftl, to_add, ixl_mac_filter, ftle);
vsi->num_hw_filters += j;
goto out_free;
}
device_printf(dev,
"i40e_aq_add_macvlan status %s, error %s\n",
i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
j = 0;
LIST_FOREACH_SAFE(f, to_add, ftle, fn) {
LIST_REMOVE(f, ftle);
if (a[j].match_method == I40E_AQC_MM_ERR_NO_RES) {
ixl_dbg_filter(pf,
"%s filter " MAC_FORMAT " VTAG: %d not added\n",
__func__,
MAC_FORMAT_ARGS(f->macaddr),
f->vlan);
free(f, M_IXL);
} else {
LIST_INSERT_HEAD(&vsi->ftl, f, ftle);
vsi->num_hw_filters++;
}
j++;
}
out_free:
free(a, M_IXL);
}
void
ixl_del_hw_filters(struct ixl_vsi *vsi, struct ixl_ftl_head *to_del, int cnt)
{
struct i40e_aqc_remove_macvlan_element_data *d, *e;
struct ixl_pf *pf;
struct i40e_hw *hw;
device_t dev;
struct ixl_mac_filter *f, *f_temp;
enum i40e_status_code status;
int j = 0;
pf = vsi->back;
hw = &pf->hw;
dev = vsi->dev;
ixl_dbg_filter(pf, "%s: start, cnt: %d\n", __func__, cnt);
d = malloc(sizeof(struct i40e_aqc_remove_macvlan_element_data) * cnt,
M_IXL, M_NOWAIT | M_ZERO);
if (d == NULL) {
device_printf(dev, "%s: failed to get memory\n", __func__);
return;
}
LIST_FOREACH_SAFE(f, to_del, ftle, f_temp) {
e = &d[j];
bcopy(f->macaddr, e->mac_addr, ETHER_ADDR_LEN);
e->flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH;
if (f->vlan == IXL_VLAN_ANY) {
e->vlan_tag = 0;
e->flags |= I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
} else {
e->vlan_tag = f->vlan;
}
ixl_dbg_filter(pf, "DEL: " MAC_FORMAT "\n",
MAC_FORMAT_ARGS(f->macaddr));
LIST_REMOVE(f, ftle);
free(f, M_IXL);
if (++j == cnt)
break;
}
if (j != cnt || !LIST_EMPTY(to_del)) {
device_printf(dev,
"%s ERROR: wrong size of list of filters, expected: %d, found: %d\n",
__func__, cnt, j);
ixl_free_filters(to_del);
goto out_free;
}
status = i40e_aq_remove_macvlan(hw, vsi->seid, d, j, NULL);
if (status) {
device_printf(dev,
"%s: i40e_aq_remove_macvlan status %s, error %s\n",
__func__, i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
for (int i = 0; i < j; i++) {
if (d[i].error_code == 0)
continue;
device_printf(dev,
"%s Filter does not exist " MAC_FORMAT " VTAG: %d\n",
__func__, MAC_FORMAT_ARGS(d[i].mac_addr),
d[i].vlan_tag);
}
}
vsi->num_hw_filters -= j;
out_free:
free(d, M_IXL);
ixl_dbg_filter(pf, "%s: end\n", __func__);
}
int
ixl_enable_tx_ring(struct ixl_pf *pf, struct ixl_pf_qtag *qtag, u16 vsi_qidx)
{
struct i40e_hw *hw = &pf->hw;
int error = 0;
u32 reg;
u16 pf_qidx;
pf_qidx = ixl_pf_qidx_from_vsi_qidx(qtag, vsi_qidx);
ixl_dbg(pf, IXL_DBG_EN_DIS,
"Enabling PF TX ring %4d / VSI TX ring %4d...\n",
pf_qidx, vsi_qidx);
i40e_pre_tx_queue_cfg(hw, pf_qidx, TRUE);
reg = rd32(hw, I40E_QTX_ENA(pf_qidx));
reg |= I40E_QTX_ENA_QENA_REQ_MASK |
I40E_QTX_ENA_QENA_STAT_MASK;
wr32(hw, I40E_QTX_ENA(pf_qidx), reg);
for (int j = 0; j < 10; j++) {
reg = rd32(hw, I40E_QTX_ENA(pf_qidx));
if (reg & I40E_QTX_ENA_QENA_STAT_MASK)
break;
i40e_usec_delay(10);
}
if ((reg & I40E_QTX_ENA_QENA_STAT_MASK) == 0) {
device_printf(pf->dev, "TX queue %d still disabled!\n",
pf_qidx);
error = ETIMEDOUT;
}
return (error);
}
int
ixl_enable_rx_ring(struct ixl_pf *pf, struct ixl_pf_qtag *qtag, u16 vsi_qidx)
{
struct i40e_hw *hw = &pf->hw;
int error = 0;
u32 reg;
u16 pf_qidx;
pf_qidx = ixl_pf_qidx_from_vsi_qidx(qtag, vsi_qidx);
ixl_dbg(pf, IXL_DBG_EN_DIS,
"Enabling PF RX ring %4d / VSI RX ring %4d...\n",
pf_qidx, vsi_qidx);
reg = rd32(hw, I40E_QRX_ENA(pf_qidx));
reg |= I40E_QRX_ENA_QENA_REQ_MASK |
I40E_QRX_ENA_QENA_STAT_MASK;
wr32(hw, I40E_QRX_ENA(pf_qidx), reg);
for (int j = 0; j < 10; j++) {
reg = rd32(hw, I40E_QRX_ENA(pf_qidx));
if (reg & I40E_QRX_ENA_QENA_STAT_MASK)
break;
i40e_usec_delay(10);
}
if ((reg & I40E_QRX_ENA_QENA_STAT_MASK) == 0) {
device_printf(pf->dev, "RX queue %d still disabled!\n",
pf_qidx);
error = ETIMEDOUT;
}
return (error);
}
int
ixl_enable_ring(struct ixl_pf *pf, struct ixl_pf_qtag *qtag, u16 vsi_qidx)
{
int error = 0;
error = ixl_enable_tx_ring(pf, qtag, vsi_qidx);
if (error)
return (error);
error = ixl_enable_rx_ring(pf, qtag, vsi_qidx);
return (error);
}
int
ixl_disable_tx_ring(struct ixl_pf *pf, struct ixl_pf_qtag *qtag, u16 vsi_qidx)
{
struct i40e_hw *hw = &pf->hw;
int error = 0;
u32 reg;
u16 pf_qidx;
pf_qidx = ixl_pf_qidx_from_vsi_qidx(qtag, vsi_qidx);
ixl_dbg(pf, IXL_DBG_EN_DIS,
"Disabling PF TX ring %4d / VSI TX ring %4d...\n",
pf_qidx, vsi_qidx);
i40e_pre_tx_queue_cfg(hw, pf_qidx, FALSE);
i40e_usec_delay(500);
reg = rd32(hw, I40E_QTX_ENA(pf_qidx));
reg &= ~I40E_QTX_ENA_QENA_REQ_MASK;
wr32(hw, I40E_QTX_ENA(pf_qidx), reg);
for (int j = 0; j < 10; j++) {
reg = rd32(hw, I40E_QTX_ENA(pf_qidx));
if (!(reg & I40E_QTX_ENA_QENA_STAT_MASK))
break;
i40e_msec_delay(10);
}
if (reg & I40E_QTX_ENA_QENA_STAT_MASK) {
device_printf(pf->dev, "TX queue %d still enabled!\n",
pf_qidx);
error = ETIMEDOUT;
}
return (error);
}
int
ixl_disable_rx_ring(struct ixl_pf *pf, struct ixl_pf_qtag *qtag, u16 vsi_qidx)
{
struct i40e_hw *hw = &pf->hw;
int error = 0;
u32 reg;
u16 pf_qidx;
pf_qidx = ixl_pf_qidx_from_vsi_qidx(qtag, vsi_qidx);
ixl_dbg(pf, IXL_DBG_EN_DIS,
"Disabling PF RX ring %4d / VSI RX ring %4d...\n",
pf_qidx, vsi_qidx);
reg = rd32(hw, I40E_QRX_ENA(pf_qidx));
reg &= ~I40E_QRX_ENA_QENA_REQ_MASK;
wr32(hw, I40E_QRX_ENA(pf_qidx), reg);
for (int j = 0; j < 10; j++) {
reg = rd32(hw, I40E_QRX_ENA(pf_qidx));
if (!(reg & I40E_QRX_ENA_QENA_STAT_MASK))
break;
i40e_msec_delay(10);
}
if (reg & I40E_QRX_ENA_QENA_STAT_MASK) {
device_printf(pf->dev, "RX queue %d still enabled!\n",
pf_qidx);
error = ETIMEDOUT;
}
return (error);
}
int
ixl_disable_ring(struct ixl_pf *pf, struct ixl_pf_qtag *qtag, u16 vsi_qidx)
{
int error = 0;
error = ixl_disable_tx_ring(pf, qtag, vsi_qidx);
if (error)
return (error);
error = ixl_disable_rx_ring(pf, qtag, vsi_qidx);
return (error);
}
static void
ixl_handle_tx_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 vf_num, queue;
u8 pf_num, event;
u8 pf_mdet_num, vp_mdet_num;
u32 reg;
reg = rd32(hw, I40E_GL_MDET_TX);
if (reg & I40E_GL_MDET_TX_VALID_MASK) {
pf_num = (reg & I40E_GL_MDET_TX_PF_NUM_MASK) >>
I40E_GL_MDET_TX_PF_NUM_SHIFT;
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;
queue = (reg & I40E_GL_MDET_TX_QUEUE_MASK) >>
I40E_GL_MDET_TX_QUEUE_SHIFT;
wr32(hw, I40E_GL_MDET_TX, 0xffffffff);
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;
}
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;
}
}
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);
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;
reg = rd32(hw, I40E_GL_MDET_RX);
if (reg & I40E_GL_MDET_RX_VALID_MASK) {
pf_num = (reg & I40E_GL_MDET_RX_FUNCTION_MASK) >>
I40E_GL_MDET_RX_FUNCTION_SHIFT;
event = (reg & I40E_GL_MDET_RX_EVENT_MASK) >>
I40E_GL_MDET_RX_EVENT_SHIFT;
queue = (reg & I40E_GL_MDET_RX_QUEUE_MASK) >>
I40E_GL_MDET_RX_QUEUE_SHIFT;
wr32(hw, I40E_GL_MDET_RX, 0xffffffff);
mdd_detected = true;
}
if (!mdd_detected)
return;
reg = rd32(hw, I40E_PF_MDET_RX);
if (reg & I40E_PF_MDET_RX_VALID_MASK) {
wr32(hw, I40E_PF_MDET_RX, 0xFFFF);
pf_mdet_num = hw->pf_id;
pf_mdd_detected = true;
}
for (int i = 0; i < pf->num_vfs; i++) {
vf = &(pf->vfs[i]);
reg = rd32(hw, I40E_VP_MDET_RX(i));
if (reg & I40E_VP_MDET_RX_VALID_MASK) {
wr32(hw, I40E_VP_MDET_RX(i), 0xFFFF);
vp_mdet_num = i;
vf->num_mdd_events++;
vf_mdd_detected = true;
}
}
if (vf_mdd_detected && pf_mdd_detected)
device_printf(dev,
"Malicious Driver Detection event %d"
" 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);
else
device_printf(dev,
"RX Malicious Driver Detection event (unknown)\n");
}
void
ixl_handle_mdd_event(struct ixl_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
u32 reg;
ixl_handle_tx_mdd_event(pf);
ixl_handle_rx_mdd_event(pf);
ixl_clear_state(&pf->state, IXL_STATE_MDD_PENDING);
reg = rd32(hw, I40E_PFINT_ICR0_ENA);
reg |= I40E_PFINT_ICR0_ENA_MAL_DETECT_MASK;
wr32(hw, I40E_PFINT_ICR0_ENA, reg);
ixl_flush(hw);
}
void
ixl_enable_intr0(struct i40e_hw *hw)
{
u32 reg;
reg = I40E_PFINT_DYN_CTL0_INTENA_MASK |
I40E_PFINT_DYN_CTL0_CLEARPBA_MASK |
(IXL_ITR_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT);
wr32(hw, I40E_PFINT_DYN_CTL0, reg);
}
void
ixl_disable_intr0(struct i40e_hw *hw)
{
u32 reg;
reg = IXL_ITR_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT;
wr32(hw, I40E_PFINT_DYN_CTL0, reg);
ixl_flush(hw);
}
void
ixl_enable_queue(struct i40e_hw *hw, int id)
{
u32 reg;
reg = I40E_PFINT_DYN_CTLN_INTENA_MASK |
I40E_PFINT_DYN_CTLN_CLEARPBA_MASK |
(IXL_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT);
wr32(hw, I40E_PFINT_DYN_CTLN(id), reg);
}
void
ixl_disable_queue(struct i40e_hw *hw, int id)
{
u32 reg;
reg = IXL_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT;
wr32(hw, I40E_PFINT_DYN_CTLN(id), reg);
}
void
ixl_handle_empr_reset(struct ixl_pf *pf)
{
struct ixl_vsi *vsi = &pf->vsi;
bool is_up = !!(if_getdrvflags(vsi->ifp) & IFF_DRV_RUNNING);
ixl_prepare_for_reset(pf, is_up);
ixl_pf_reset(pf);
if (!IXL_PF_IN_RECOVERY_MODE(pf) &&
ixl_get_fw_mode(pf) == IXL_FW_MODE_RECOVERY) {
ixl_set_state(&pf->state, IXL_STATE_RECOVERY_MODE);
device_printf(pf->dev,
"Firmware recovery mode detected. Limiting functionality. Refer to Intel(R) Ethernet Adapters and Devices User Guide for details on firmware recovery mode.\n");
pf->link_up = FALSE;
ixl_update_link_status(pf);
}
ixl_rebuild_hw_structs_after_reset(pf, is_up);
ixl_clear_state(&pf->state, IXL_STATE_RESETTING);
}
void
ixl_update_stats_counters(struct ixl_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
struct ixl_vsi *vsi = &pf->vsi;
struct ixl_vf *vf;
u64 prev_link_xoff_rx = pf->stats.link_xoff_rx;
struct i40e_hw_port_stats *nsd = &pf->stats;
struct i40e_hw_port_stats *osd = &pf->stats_offsets;
ixl_stat_update32(hw, I40E_GLPRT_CRCERRS(hw->port),
pf->stat_offsets_loaded,
&osd->crc_errors, &nsd->crc_errors);
ixl_stat_update32(hw, I40E_GLPRT_ILLERRC(hw->port),
pf->stat_offsets_loaded,
&osd->illegal_bytes, &nsd->illegal_bytes);
ixl_stat_update48(hw, I40E_GLPRT_GORCL(hw->port),
pf->stat_offsets_loaded,
&osd->eth.rx_bytes, &nsd->eth.rx_bytes);
ixl_stat_update48(hw, I40E_GLPRT_GOTCL(hw->port),
pf->stat_offsets_loaded,
&osd->eth.tx_bytes, &nsd->eth.tx_bytes);
ixl_stat_update32(hw, I40E_GLPRT_RDPC(hw->port),
pf->stat_offsets_loaded,
&osd->eth.rx_discards,
&nsd->eth.rx_discards);
ixl_stat_update48(hw, I40E_GLPRT_UPRCL(hw->port),
pf->stat_offsets_loaded,
&osd->eth.rx_unicast,
&nsd->eth.rx_unicast);
ixl_stat_update48(hw, I40E_GLPRT_UPTCL(hw->port),
pf->stat_offsets_loaded,
&osd->eth.tx_unicast,
&nsd->eth.tx_unicast);
ixl_stat_update48(hw, I40E_GLPRT_MPRCL(hw->port),
pf->stat_offsets_loaded,
&osd->eth.rx_multicast,
&nsd->eth.rx_multicast);
ixl_stat_update48(hw, I40E_GLPRT_MPTCL(hw->port),
pf->stat_offsets_loaded,
&osd->eth.tx_multicast,
&nsd->eth.tx_multicast);
ixl_stat_update48(hw, I40E_GLPRT_BPRCL(hw->port),
pf->stat_offsets_loaded,
&osd->eth.rx_broadcast,
&nsd->eth.rx_broadcast);
ixl_stat_update48(hw, I40E_GLPRT_BPTCL(hw->port),
pf->stat_offsets_loaded,
&osd->eth.tx_broadcast,
&nsd->eth.tx_broadcast);
ixl_stat_update32(hw, I40E_GLPRT_TDOLD(hw->port),
pf->stat_offsets_loaded,
&osd->tx_dropped_link_down,
&nsd->tx_dropped_link_down);
ixl_stat_update32(hw, I40E_GLPRT_MLFC(hw->port),
pf->stat_offsets_loaded,
&osd->mac_local_faults,
&nsd->mac_local_faults);
ixl_stat_update32(hw, I40E_GLPRT_MRFC(hw->port),
pf->stat_offsets_loaded,
&osd->mac_remote_faults,
&nsd->mac_remote_faults);
ixl_stat_update32(hw, I40E_GLPRT_RLEC(hw->port),
pf->stat_offsets_loaded,
&osd->rx_length_errors,
&nsd->rx_length_errors);
ixl_stat_update32(hw, I40E_GLPRT_LXONRXC(hw->port),
pf->stat_offsets_loaded,
&osd->link_xon_rx, &nsd->link_xon_rx);
ixl_stat_update32(hw, I40E_GLPRT_LXONTXC(hw->port),
pf->stat_offsets_loaded,
&osd->link_xon_tx, &nsd->link_xon_tx);
ixl_stat_update32(hw, I40E_GLPRT_LXOFFRXC(hw->port),
pf->stat_offsets_loaded,
&osd->link_xoff_rx, &nsd->link_xoff_rx);
ixl_stat_update32(hw, I40E_GLPRT_LXOFFTXC(hw->port),
pf->stat_offsets_loaded,
&osd->link_xoff_tx, &nsd->link_xoff_tx);
if (pf->stats.link_xoff_rx != prev_link_xoff_rx)
vsi->shared->isc_pause_frames = 1;
ixl_stat_update48(hw, I40E_GLPRT_PRC64L(hw->port),
pf->stat_offsets_loaded,
&osd->rx_size_64, &nsd->rx_size_64);
ixl_stat_update48(hw, I40E_GLPRT_PRC127L(hw->port),
pf->stat_offsets_loaded,
&osd->rx_size_127, &nsd->rx_size_127);
ixl_stat_update48(hw, I40E_GLPRT_PRC255L(hw->port),
pf->stat_offsets_loaded,
&osd->rx_size_255, &nsd->rx_size_255);
ixl_stat_update48(hw, I40E_GLPRT_PRC511L(hw->port),
pf->stat_offsets_loaded,
&osd->rx_size_511, &nsd->rx_size_511);
ixl_stat_update48(hw, I40E_GLPRT_PRC1023L(hw->port),
pf->stat_offsets_loaded,
&osd->rx_size_1023, &nsd->rx_size_1023);
ixl_stat_update48(hw, I40E_GLPRT_PRC1522L(hw->port),
pf->stat_offsets_loaded,
&osd->rx_size_1522, &nsd->rx_size_1522);
ixl_stat_update48(hw, I40E_GLPRT_PRC9522L(hw->port),
pf->stat_offsets_loaded,
&osd->rx_size_big, &nsd->rx_size_big);
ixl_stat_update48(hw, I40E_GLPRT_PTC64L(hw->port),
pf->stat_offsets_loaded,
&osd->tx_size_64, &nsd->tx_size_64);
ixl_stat_update48(hw, I40E_GLPRT_PTC127L(hw->port),
pf->stat_offsets_loaded,
&osd->tx_size_127, &nsd->tx_size_127);
ixl_stat_update48(hw, I40E_GLPRT_PTC255L(hw->port),
pf->stat_offsets_loaded,
&osd->tx_size_255, &nsd->tx_size_255);
ixl_stat_update48(hw, I40E_GLPRT_PTC511L(hw->port),
pf->stat_offsets_loaded,
&osd->tx_size_511, &nsd->tx_size_511);
ixl_stat_update48(hw, I40E_GLPRT_PTC1023L(hw->port),
pf->stat_offsets_loaded,
&osd->tx_size_1023, &nsd->tx_size_1023);
ixl_stat_update48(hw, I40E_GLPRT_PTC1522L(hw->port),
pf->stat_offsets_loaded,
&osd->tx_size_1522, &nsd->tx_size_1522);
ixl_stat_update48(hw, I40E_GLPRT_PTC9522L(hw->port),
pf->stat_offsets_loaded,
&osd->tx_size_big, &nsd->tx_size_big);
ixl_stat_update32(hw, I40E_GLPRT_RUC(hw->port),
pf->stat_offsets_loaded,
&osd->rx_undersize, &nsd->rx_undersize);
ixl_stat_update32(hw, I40E_GLPRT_RFC(hw->port),
pf->stat_offsets_loaded,
&osd->rx_fragments, &nsd->rx_fragments);
u64 rx_roc;
ixl_stat_update32(hw, I40E_GLPRT_ROC(hw->port),
pf->stat_offsets_loaded,
&osd->rx_oversize, &rx_roc);
u64 rx_err1;
ixl_stat_update64(hw,
I40E_GL_RXERR1L(hw->pf_id + BIT(7)),
pf->stat_offsets_loaded,
&osd->rx_err1,
&rx_err1);
nsd->rx_oversize = rx_roc + rx_err1;
ixl_stat_update32(hw, I40E_GLPRT_RJC(hw->port),
pf->stat_offsets_loaded,
&osd->rx_jabber, &nsd->rx_jabber);
i40e_get_phy_lpi_status(hw, nsd);
i40e_lpi_stat_update(hw, pf->stat_offsets_loaded,
&osd->tx_lpi_count, &nsd->tx_lpi_count,
&osd->rx_lpi_count, &nsd->rx_lpi_count);
pf->stat_offsets_loaded = true;
ixl_update_vsi_stats(vsi);
for (int i = 0; i < pf->num_vfs; i++) {
vf = &pf->vfs[i];
if (vf->vf_flags & VF_FLAG_ENABLED)
ixl_update_eth_stats(&pf->vfs[i].vsi);
}
}
void
ixl_update_eth_stats(struct ixl_vsi *vsi)
{
struct ixl_pf *pf = (struct ixl_pf *)vsi->back;
struct i40e_hw *hw = &pf->hw;
struct i40e_eth_stats *es;
struct i40e_eth_stats *oes;
u16 stat_idx = vsi->info.stat_counter_idx;
es = &vsi->eth_stats;
oes = &vsi->eth_stats_offsets;
ixl_stat_update32(hw, I40E_GLV_TEPC(stat_idx),
vsi->stat_offsets_loaded,
&oes->tx_errors, &es->tx_errors);
ixl_stat_update32(hw, I40E_GLV_RDPC(stat_idx),
vsi->stat_offsets_loaded,
&oes->rx_discards, &es->rx_discards);
ixl_stat_update48(hw, I40E_GLV_GORCL(stat_idx),
vsi->stat_offsets_loaded,
&oes->rx_bytes, &es->rx_bytes);
ixl_stat_update48(hw, I40E_GLV_UPRCL(stat_idx),
vsi->stat_offsets_loaded,
&oes->rx_unicast, &es->rx_unicast);
ixl_stat_update48(hw, I40E_GLV_MPRCL(stat_idx),
vsi->stat_offsets_loaded,
&oes->rx_multicast, &es->rx_multicast);
ixl_stat_update48(hw, I40E_GLV_BPRCL(stat_idx),
vsi->stat_offsets_loaded,
&oes->rx_broadcast, &es->rx_broadcast);
ixl_stat_update48(hw, I40E_GLV_GOTCL(stat_idx),
vsi->stat_offsets_loaded,
&oes->tx_bytes, &es->tx_bytes);
ixl_stat_update48(hw, I40E_GLV_UPTCL(stat_idx),
vsi->stat_offsets_loaded,
&oes->tx_unicast, &es->tx_unicast);
ixl_stat_update48(hw, I40E_GLV_MPTCL(stat_idx),
vsi->stat_offsets_loaded,
&oes->tx_multicast, &es->tx_multicast);
ixl_stat_update48(hw, I40E_GLV_BPTCL(stat_idx),
vsi->stat_offsets_loaded,
&oes->tx_broadcast, &es->tx_broadcast);
vsi->stat_offsets_loaded = true;
}
void
ixl_update_vsi_stats(struct ixl_vsi *vsi)
{
struct ixl_pf *pf;
struct i40e_eth_stats *es;
u64 tx_discards, csum_errs;
struct i40e_hw_port_stats *nsd;
pf = vsi->back;
es = &vsi->eth_stats;
nsd = &pf->stats;
ixl_update_eth_stats(vsi);
tx_discards = es->tx_discards + nsd->tx_dropped_link_down;
csum_errs = 0;
for (int i = 0; i < vsi->num_rx_queues; i++)
csum_errs += vsi->rx_queues[i].rxr.csum_errs;
nsd->checksum_error = csum_errs;
IXL_SET_IPACKETS(vsi, es->rx_unicast +
es->rx_multicast +
es->rx_broadcast);
IXL_SET_OPACKETS(vsi, es->tx_unicast +
es->tx_multicast +
es->tx_broadcast);
IXL_SET_IBYTES(vsi, es->rx_bytes);
IXL_SET_OBYTES(vsi, es->tx_bytes);
IXL_SET_IMCASTS(vsi, es->rx_multicast);
IXL_SET_OMCASTS(vsi, es->tx_multicast);
IXL_SET_IERRORS(vsi, nsd->crc_errors + nsd->illegal_bytes +
nsd->checksum_error + nsd->rx_length_errors +
nsd->rx_undersize + nsd->rx_fragments + nsd->rx_oversize +
nsd->rx_jabber);
IXL_SET_OERRORS(vsi, es->tx_errors);
IXL_SET_IQDROPS(vsi, es->rx_discards + nsd->eth.rx_discards);
IXL_SET_OQDROPS(vsi, tx_discards);
IXL_SET_NOPROTO(vsi, es->rx_unknown_protocol);
IXL_SET_COLLISIONS(vsi, 0);
}
void
ixl_pf_reset_stats(struct ixl_pf *pf)
{
bzero(&pf->stats, sizeof(struct i40e_hw_port_stats));
bzero(&pf->stats_offsets, sizeof(struct i40e_hw_port_stats));
pf->stat_offsets_loaded = false;
}
void
ixl_vsi_reset_stats(struct ixl_vsi *vsi)
{
bzero(&vsi->eth_stats, sizeof(struct i40e_eth_stats));
bzero(&vsi->eth_stats_offsets, sizeof(struct i40e_eth_stats));
vsi->stat_offsets_loaded = false;
}
static void
_ixl_stat_update_helper(struct i40e_hw *hw, u32 reg,
bool offset_loaded, u64 mask, u64 *offset, u64 *stat)
{
u64 new_data = rd64(hw, reg);
if (!offset_loaded)
*offset = new_data;
if (new_data >= *offset)
*stat = new_data - *offset;
else
*stat = (new_data + mask) - *offset + 1;
*stat &= mask;
}
void
ixl_stat_update48(struct i40e_hw *hw, u32 reg,
bool offset_loaded, u64 *offset, u64 *stat)
{
_ixl_stat_update_helper(hw,
reg,
offset_loaded,
0xFFFFFFFFFFFFULL,
offset,
stat);
}
void
ixl_stat_update64(struct i40e_hw *hw, u32 reg,
bool offset_loaded, u64 *offset, u64 *stat)
{
_ixl_stat_update_helper(hw,
reg,
offset_loaded,
0xFFFFFFFFFFFFFFFFULL,
offset,
stat);
}
void
ixl_stat_update32(struct i40e_hw *hw, u32 reg,
bool offset_loaded, u64 *offset, u64 *stat)
{
u32 new_data;
new_data = rd32(hw, reg);
if (!offset_loaded)
*offset = new_data;
if (new_data >= *offset)
*stat = (u32)(new_data - *offset);
else
*stat = (u32)((new_data + ((u64)1 << 32)) - *offset);
}
void
ixl_add_sysctls_recovery_mode(struct ixl_pf *pf)
{
device_t dev = pf->dev;
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
struct sysctl_oid_list *ctx_list =
SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
struct sysctl_oid *debug_node;
struct sysctl_oid_list *debug_list;
SYSCTL_ADD_PROC(ctx, ctx_list,
OID_AUTO, "fw_version",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, pf, 0,
ixl_sysctl_show_fw, "A", "Firmware version");
debug_node = SYSCTL_ADD_NODE(ctx, ctx_list,
OID_AUTO, "debug", CTLFLAG_RD | CTLFLAG_SKIP | CTLFLAG_MPSAFE, NULL,
"Debug Sysctls");
debug_list = SYSCTL_CHILDREN(debug_node);
SYSCTL_ADD_UINT(ctx, debug_list,
OID_AUTO, "shared_debug_mask", CTLFLAG_RW,
&pf->hw.debug_mask, 0, "Shared code debug message level");
SYSCTL_ADD_UINT(ctx, debug_list,
OID_AUTO, "core_debug_mask", CTLFLAG_RW,
&pf->dbg_mask, 0, "Non-shared code debug message level");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "dump_debug_data",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_dump_debug_data, "A", "Dump Debug Data from FW");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "do_pf_reset",
CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_do_pf_reset, "I", "Tell HW to initiate a PF reset");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "do_core_reset",
CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_do_core_reset, "I", "Tell HW to initiate a CORE reset");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "do_global_reset",
CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_do_global_reset, "I", "Tell HW to initiate a GLOBAL reset");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "queue_interrupt_table",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_queue_interrupt_table, "A", "View MSI-X indices for TX/RX queues");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "queue_int_ctln",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_debug_queue_int_ctln, "A",
"View MSI-X control registers for RX queues");
}
void
ixl_add_device_sysctls(struct ixl_pf *pf)
{
device_t dev = pf->dev;
struct i40e_hw *hw = &pf->hw;
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
struct sysctl_oid_list *ctx_list =
SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
struct sysctl_oid *debug_node;
struct sysctl_oid_list *debug_list;
struct sysctl_oid *fec_node;
struct sysctl_oid_list *fec_list;
struct sysctl_oid *eee_node;
struct sysctl_oid_list *eee_list;
SYSCTL_ADD_PROC(ctx, ctx_list,
OID_AUTO, "fc", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_set_flowcntl, "I", IXL_SYSCTL_HELP_FC);
SYSCTL_ADD_PROC(ctx, ctx_list,
OID_AUTO, "advertise_speed",
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, pf, 0,
ixl_sysctl_set_advertise, "I", IXL_SYSCTL_HELP_SET_ADVERTISE);
SYSCTL_ADD_PROC(ctx, ctx_list,
OID_AUTO, "supported_speeds",
CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, pf, 0,
ixl_sysctl_supported_speeds, "I", IXL_SYSCTL_HELP_SUPPORTED_SPEED);
SYSCTL_ADD_PROC(ctx, ctx_list,
OID_AUTO, "current_speed",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, pf, 0,
ixl_sysctl_current_speed, "A", "Current Port Speed");
SYSCTL_ADD_PROC(ctx, ctx_list,
OID_AUTO, "fw_version",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, pf, 0,
ixl_sysctl_show_fw, "A", "Firmware version");
SYSCTL_ADD_PROC(ctx, ctx_list,
OID_AUTO, "unallocated_queues",
CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, pf, 0,
ixl_sysctl_unallocated_queues, "I",
"Queues not allocated to a PF or VF");
SYSCTL_ADD_PROC(ctx, ctx_list,
OID_AUTO, "tx_itr",
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, pf, 0,
ixl_sysctl_pf_tx_itr, "I",
"Immediately set TX ITR value for all queues");
SYSCTL_ADD_PROC(ctx, ctx_list,
OID_AUTO, "rx_itr",
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, pf, 0,
ixl_sysctl_pf_rx_itr, "I",
"Immediately set RX ITR value for all queues");
SYSCTL_ADD_INT(ctx, ctx_list,
OID_AUTO, "dynamic_rx_itr", CTLFLAG_RW,
&pf->dynamic_rx_itr, 0, "Enable dynamic RX ITR");
SYSCTL_ADD_INT(ctx, ctx_list,
OID_AUTO, "dynamic_tx_itr", CTLFLAG_RW,
&pf->dynamic_tx_itr, 0, "Enable dynamic TX ITR");
if (i40e_is_25G_device(hw->device_id)) {
fec_node = SYSCTL_ADD_NODE(ctx, ctx_list,
OID_AUTO, "fec", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
"FEC Sysctls");
fec_list = SYSCTL_CHILDREN(fec_node);
SYSCTL_ADD_PROC(ctx, fec_list,
OID_AUTO, "fc_ability",
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, pf, 0,
ixl_sysctl_fec_fc_ability, "I", "FC FEC ability enabled");
SYSCTL_ADD_PROC(ctx, fec_list,
OID_AUTO, "rs_ability",
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, pf, 0,
ixl_sysctl_fec_rs_ability, "I", "RS FEC ability enabled");
SYSCTL_ADD_PROC(ctx, fec_list,
OID_AUTO, "fc_requested",
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, pf, 0,
ixl_sysctl_fec_fc_request, "I",
"FC FEC mode requested on link");
SYSCTL_ADD_PROC(ctx, fec_list,
OID_AUTO, "rs_requested",
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, pf, 0,
ixl_sysctl_fec_rs_request, "I",
"RS FEC mode requested on link");
SYSCTL_ADD_PROC(ctx, fec_list,
OID_AUTO, "auto_fec_enabled",
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, pf, 0,
ixl_sysctl_fec_auto_enable, "I",
"Let FW decide FEC ability/request modes");
}
SYSCTL_ADD_PROC(ctx, ctx_list,
OID_AUTO, "fw_lldp", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_fw_lldp, "I", IXL_SYSCTL_HELP_FW_LLDP);
eee_node = SYSCTL_ADD_NODE(ctx, ctx_list,
OID_AUTO, "eee", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
"Energy Efficient Ethernet (EEE) Sysctls");
eee_list = SYSCTL_CHILDREN(eee_node);
SYSCTL_ADD_PROC(ctx, eee_list,
OID_AUTO, "enable", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
pf, 0, ixl_sysctl_eee_enable, "I",
"Enable Energy Efficient Ethernet (EEE)");
SYSCTL_ADD_UINT(ctx, eee_list, OID_AUTO, "tx_lpi_status",
CTLFLAG_RD | CTLFLAG_MPSAFE, &pf->stats.tx_lpi_status, 0,
"TX LPI status");
SYSCTL_ADD_UINT(ctx, eee_list, OID_AUTO, "rx_lpi_status",
CTLFLAG_RD | CTLFLAG_MPSAFE, &pf->stats.rx_lpi_status, 0,
"RX LPI status");
SYSCTL_ADD_UQUAD(ctx, eee_list, OID_AUTO, "tx_lpi_count",
CTLFLAG_RD | CTLFLAG_MPSAFE, &pf->stats.tx_lpi_count,
"TX LPI count");
SYSCTL_ADD_UQUAD(ctx, eee_list, OID_AUTO, "rx_lpi_count",
CTLFLAG_RD | CTLFLAG_MPSAFE, &pf->stats.rx_lpi_count,
"RX LPI count");
SYSCTL_ADD_PROC(ctx, ctx_list, OID_AUTO,
"link_active_on_if_down",
CTLTYPE_INT | CTLFLAG_RWTUN,
pf, 0, ixl_sysctl_set_link_active, "I",
IXL_SYSCTL_HELP_SET_LINK_ACTIVE);
debug_node = SYSCTL_ADD_NODE(ctx, ctx_list,
OID_AUTO, "debug", CTLFLAG_RD | CTLFLAG_SKIP | CTLFLAG_MPSAFE, NULL,
"Debug Sysctls");
debug_list = SYSCTL_CHILDREN(debug_node);
SYSCTL_ADD_UINT(ctx, debug_list,
OID_AUTO, "shared_debug_mask", CTLFLAG_RW,
&pf->hw.debug_mask, 0, "Shared code debug message level");
SYSCTL_ADD_UINT(ctx, debug_list,
OID_AUTO, "core_debug_mask", CTLFLAG_RW,
&pf->dbg_mask, 0, "Non-shared code debug message level");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "link_status",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_link_status, "A", IXL_SYSCTL_HELP_LINK_STATUS);
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "phy_abilities_init",
CTLTYPE_STRING | CTLFLAG_RD,
pf, 1, ixl_sysctl_phy_abilities, "A", "Initial PHY Abilities");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "phy_abilities",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_phy_abilities, "A", "PHY Abilities");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "filter_list",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_sw_filter_list, "A", "SW Filter List");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "hw_res_alloc",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_hw_res_alloc, "A", "HW Resource Allocation");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "switch_config",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_switch_config, "A", "HW Switch Configuration");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "switch_vlans",
CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_switch_vlans, "I", "HW Switch VLAN Configuration");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "rss_key",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_hkey, "A", "View RSS key");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "rss_lut",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_hlut, "A", "View RSS lookup table");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "rss_hena",
CTLTYPE_ULONG | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_hena, "LU", "View enabled packet types for RSS");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "disable_fw_link_management",
CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_fw_link_management, "I", "Disable FW Link Management");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "dump_debug_data",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_dump_debug_data, "A", "Dump Debug Data from FW");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "do_pf_reset",
CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_do_pf_reset, "I", "Tell HW to initiate a PF reset");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "do_core_reset",
CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_do_core_reset, "I", "Tell HW to initiate a CORE reset");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "do_global_reset",
CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_do_global_reset, "I", "Tell HW to initiate a GLOBAL reset");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "queue_interrupt_table",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_queue_interrupt_table, "A", "View MSI-X indices for TX/RX queues");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "phy_statistics", CTLTYPE_STRING | CTLFLAG_RD,
pf, 0, ixl_sysctl_phy_statistics, "A", "PHY Statistics");
if (pf->has_i2c) {
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "read_i2c_byte",
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_read_i2c_byte, "I", IXL_SYSCTL_HELP_READ_I2C);
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "write_i2c_byte",
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_write_i2c_byte, "I", IXL_SYSCTL_HELP_WRITE_I2C);
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "read_i2c_diag_data",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
pf, 0, ixl_sysctl_read_i2c_diag_data, "A", "Dump selected diagnostic data from FW");
}
}
static int
ixl_sysctl_unallocated_queues(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
int queues;
queues = (int)ixl_pf_qmgr_get_num_free(&pf->qmgr);
return sysctl_handle_int(oidp, NULL, queues, req);
}
static const char *
ixl_link_speed_string(enum i40e_aq_link_speed link_speed)
{
const char * link_speed_str[] = {
"Unknown",
"100 Mbps",
"1 Gbps",
"10 Gbps",
"40 Gbps",
"20 Gbps",
"25 Gbps",
"2.5 Gbps",
"5 Gbps"
};
int index;
switch (link_speed) {
case I40E_LINK_SPEED_100MB:
index = 1;
break;
case I40E_LINK_SPEED_1GB:
index = 2;
break;
case I40E_LINK_SPEED_10GB:
index = 3;
break;
case I40E_LINK_SPEED_40GB:
index = 4;
break;
case I40E_LINK_SPEED_20GB:
index = 5;
break;
case I40E_LINK_SPEED_25GB:
index = 6;
break;
case I40E_LINK_SPEED_2_5GB:
index = 7;
break;
case I40E_LINK_SPEED_5GB:
index = 8;
break;
case I40E_LINK_SPEED_UNKNOWN:
default:
index = 0;
break;
}
return (link_speed_str[index]);
}
int
ixl_sysctl_current_speed(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct i40e_hw *hw = &pf->hw;
int error = 0;
ixl_update_link_status(pf);
error = sysctl_handle_string(oidp,
__DECONST(void *,
ixl_link_speed_string(hw->phy.link_info.link_speed)),
8, req);
return (error);
}
static u8
ixl_convert_sysctl_aq_link_speed(u8 speeds, bool to_aq)
{
#define SPEED_MAP_SIZE 8
static u16 speedmap[SPEED_MAP_SIZE] = {
(I40E_LINK_SPEED_100MB | (0x1 << 8)),
(I40E_LINK_SPEED_1GB | (0x2 << 8)),
(I40E_LINK_SPEED_10GB | (0x4 << 8)),
(I40E_LINK_SPEED_20GB | (0x8 << 8)),
(I40E_LINK_SPEED_25GB | (0x10 << 8)),
(I40E_LINK_SPEED_40GB | (0x20 << 8)),
(I40E_LINK_SPEED_2_5GB | (0x40 << 8)),
(I40E_LINK_SPEED_5GB | (0x80 << 8)),
};
u8 retval = 0;
for (int i = 0; i < SPEED_MAP_SIZE; i++) {
if (to_aq)
retval |= (speeds & (speedmap[i] >> 8)) ? (speedmap[i] & 0xff) : 0;
else
retval |= (speeds & speedmap[i]) ? (speedmap[i] >> 8) : 0;
}
return (retval);
}
int
ixl_set_advertised_speeds(struct ixl_pf *pf, int speeds, bool from_aq)
{
struct i40e_hw *hw = &pf->hw;
device_t dev = pf->dev;
struct i40e_aq_get_phy_abilities_resp abilities;
struct i40e_aq_set_phy_config config;
enum i40e_status_code aq_error = 0;
aq_error = i40e_aq_get_phy_capabilities(hw,
FALSE, FALSE, &abilities, NULL);
if (aq_error) {
device_printf(dev,
"%s: Error getting phy capabilities %d,"
" aq error: %d\n", __func__, aq_error,
hw->aq.asq_last_status);
return (EIO);
}
bzero(&config, sizeof(config));
if (from_aq)
config.link_speed = speeds;
else
config.link_speed = ixl_convert_sysctl_aq_link_speed(speeds, true);
config.phy_type = abilities.phy_type;
config.phy_type_ext = abilities.phy_type_ext;
config.abilities = abilities.abilities
| I40E_AQ_PHY_ENABLE_ATOMIC_LINK;
config.eee_capability = abilities.eee_capability;
config.eeer = abilities.eeer_val;
config.low_power_ctrl = abilities.d3_lpan;
config.fec_config = abilities.fec_cfg_curr_mod_ext_info
& I40E_AQ_PHY_FEC_CONFIG_MASK;
aq_error = i40e_aq_set_phy_config(hw, &config, NULL);
if (aq_error) {
device_printf(dev,
"%s: Error setting new phy config %d,"
" aq error: %d\n", __func__, aq_error,
hw->aq.asq_last_status);
return (EIO);
}
return (0);
}
static int
ixl_sysctl_supported_speeds(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
int supported = ixl_convert_sysctl_aq_link_speed(pf->supported_speeds, false);
return sysctl_handle_int(oidp, NULL, supported, req);
}
int
ixl_sysctl_set_advertise(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
device_t dev = pf->dev;
u8 converted_speeds;
int requested_ls = 0;
int error = 0;
requested_ls = pf->advertised_speed;
error = sysctl_handle_int(oidp, &requested_ls, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
if (IXL_PF_IN_RECOVERY_MODE(pf)) {
device_printf(dev, "Interface is currently in FW recovery mode. "
"Setting advertise speed not supported\n");
return (EINVAL);
}
if ((requested_ls & ~((u8)0xFF)) != 0) {
device_printf(dev, "Input advertised speed out of range; "
"valid flags are: 0x%02x\n",
ixl_convert_sysctl_aq_link_speed(pf->supported_speeds, false));
return (EINVAL);
}
converted_speeds = ixl_convert_sysctl_aq_link_speed((u8)requested_ls, true);
if ((converted_speeds | pf->supported_speeds) != pf->supported_speeds) {
device_printf(dev, "Invalid advertised speed; "
"valid flags are: 0x%02x\n",
ixl_convert_sysctl_aq_link_speed(pf->supported_speeds, false));
return (EINVAL);
}
error = ixl_set_advertised_speeds(pf, requested_ls, false);
if (error)
return (error);
pf->advertised_speed = requested_ls;
ixl_update_link_status(pf);
return (0);
}
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_5GB)
return IF_Gbps(5);
if (link_speeds & I40E_LINK_SPEED_2_5GB)
return IF_Mbps(2500);
if (link_speeds & I40E_LINK_SPEED_1GB)
return IF_Gbps(1);
if (link_speeds & I40E_LINK_SPEED_100MB)
return IF_Mbps(100);
else
return IF_Mbps(100);
}
void
ixl_get_bus_info(struct ixl_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
device_t dev = pf->dev;
u16 link;
u32 offset, num_ports;
u64 max_speed;
if (hw->mac.type == I40E_MAC_X722)
return;
pci_find_cap(dev, PCIY_EXPRESS, &offset);
link = pci_read_config(dev, offset + PCIER_LINK_STA, 2);
i40e_set_pci_config_data(hw, link);
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_5000) ? "5.0GT/s":
(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_x4) ? "Width x4" :
(hw->bus.width == i40e_bus_width_pcie_x2) ? "Width x2" :
(hw->bus.width == i40e_bus_width_pcie_x1) ? "Width x1" :
("Unknown"));
if (hw->bus.speed >= i40e_bus_speed_8000
&& hw->bus.width >= i40e_bus_width_pcie_x8)
return;
num_ports = bitcount32(hw->func_caps.valid_functions);
max_speed = ixl_max_aq_speed_to_value(pf->supported_speeds) / 1000000;
if ((num_ports * max_speed) > hw->bus.speed * hw->bus.width) {
device_printf(dev, "PCI-Express bandwidth available"
" for this device may be insufficient for"
" optimal performance.\n");
device_printf(dev, "Please move the device to a different"
" PCI-e link with more lanes and/or higher"
" transfer rate.\n");
}
}
static int
ixl_sysctl_show_fw(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct i40e_hw *hw = &pf->hw;
struct sbuf *sbuf;
sbuf = sbuf_new_for_sysctl(NULL, NULL, 128, req);
ixl_nvm_version_str(hw, sbuf);
sbuf_finish(sbuf);
sbuf_delete(sbuf);
return (0);
}
void
ixl_print_nvm_cmd(device_t dev, struct i40e_nvm_access *nvma)
{
u8 nvma_ptr = nvma->config & 0xFF;
u8 nvma_flags = (nvma->config & 0xF00) >> 8;
const char * cmd_str;
switch (nvma->command) {
case I40E_NVM_READ:
if (nvma_ptr == 0xF && nvma_flags == 0xF &&
nvma->offset == 0 && nvma->data_size == 1) {
device_printf(dev, "NVMUPD: Get Driver Status Command\n");
return;
}
cmd_str = "READ ";
break;
case I40E_NVM_WRITE:
cmd_str = "WRITE";
break;
default:
device_printf(dev, "NVMUPD: unknown command: 0x%08x\n", nvma->command);
return;
}
device_printf(dev,
"NVMUPD: cmd: %s ptr: 0x%02x flags: 0x%01x offset: 0x%08x data_s: 0x%08x\n",
cmd_str, nvma_ptr, nvma_flags, nvma->offset, nvma->data_size);
}
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;
size_t nvma_size, ifd_len, exp_len;
int err, perrno;
DEBUGFUNC("ixl_handle_nvmupd_cmd");
nvma_size = sizeof(struct i40e_nvm_access);
ifd_len = ifd->ifd_len;
if (ifd_len < nvma_size ||
ifd->ifd_data == NULL) {
device_printf(dev, "%s: incorrect ifdrv length or data pointer\n",
__func__);
device_printf(dev, "%s: ifdrv length: %zu, sizeof(struct i40e_nvm_access): %zu\n",
__func__, ifd_len, nvma_size);
device_printf(dev, "%s: data pointer: %p\n", __func__,
ifd->ifd_data);
return (EINVAL);
}
nvma = malloc(ifd_len, M_IXL, M_WAITOK);
err = copyin(ifd->ifd_data, nvma, ifd_len);
if (err) {
device_printf(dev, "%s: Cannot get request from user space\n",
__func__);
free(nvma, M_IXL);
return (err);
}
if (pf->dbg_mask & IXL_DBG_NVMUPD)
ixl_print_nvm_cmd(dev, nvma);
if (IXL_PF_IS_RESETTING(pf)) {
int count = 0;
while (count++ < 100) {
i40e_msec_delay(100);
if (!(IXL_PF_IS_RESETTING(pf)))
break;
}
}
if (IXL_PF_IS_RESETTING(pf)) {
device_printf(dev,
"%s: timeout waiting for EMP reset to finish\n",
__func__);
free(nvma, M_IXL);
return (-EBUSY);
}
if (nvma->data_size < 1 || nvma->data_size > 4096) {
device_printf(dev,
"%s: invalid request, data size not in supported range\n",
__func__);
free(nvma, M_IXL);
return (EINVAL);
}
exp_len = nvma_size + nvma->data_size - 1;
if (ifd_len < exp_len) {
ifd_len = exp_len;
nvma = realloc(nvma, ifd_len, M_IXL, M_WAITOK);
err = copyin(ifd->ifd_data, nvma, ifd_len);
if (err) {
device_printf(dev, "%s: Cannot get request from user space\n",
__func__);
free(nvma, M_IXL);
return (err);
}
}
status = i40e_nvmupd_command(hw, nvma, nvma->data, &perrno);
err = copyout(nvma, ifd->ifd_data, ifd_len);
free(nvma, M_IXL);
if (err) {
device_printf(dev, "%s: Cannot return data to user space\n",
__func__);
return (err);
}
if (status != 0 && (pf->dbg_mask & IXL_DBG_NVMUPD) != 0)
device_printf(dev, "i40e_nvmupd_command status %s, perrno %d\n",
i40e_stat_str(hw, status), perrno);
if (perrno == -EPERM)
return (-EACCES);
else
return (perrno);
}
int
ixl_find_i2c_interface(struct ixl_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
bool i2c_en, port_matched;
u32 reg;
for (int i = 0; i < 4; i++) {
reg = rd32(hw, I40E_GLGEN_MDIO_I2C_SEL(i));
i2c_en = (reg & I40E_GLGEN_MDIO_I2C_SEL_MDIO_I2C_SEL_MASK);
port_matched = ((reg & I40E_GLGEN_MDIO_I2C_SEL_PHY_PORT_NUM_MASK)
>> I40E_GLGEN_MDIO_I2C_SEL_PHY_PORT_NUM_SHIFT)
& BIT(hw->port);
if (i2c_en && port_matched)
return (i);
}
return (-1);
}
void
ixl_set_link(struct ixl_pf *pf, bool enable)
{
struct i40e_hw *hw = &pf->hw;
device_t dev = pf->dev;
struct i40e_aq_get_phy_abilities_resp abilities;
struct i40e_aq_set_phy_config config;
enum i40e_status_code aq_error = 0;
u32 phy_type, phy_type_ext;
aq_error = i40e_aq_get_phy_capabilities(hw,
FALSE, TRUE, &abilities, NULL);
if (aq_error) {
device_printf(dev,
"%s: Error getting phy capabilities %d,"
" aq error: %d\n", __func__, aq_error,
hw->aq.asq_last_status);
return;
}
phy_type = abilities.phy_type;
phy_type_ext = abilities.phy_type_ext;
aq_error = i40e_aq_get_phy_capabilities(hw,
FALSE, FALSE, &abilities, NULL);
if (aq_error) {
device_printf(dev,
"%s: Error getting phy capabilities %d,"
" aq error: %d\n", __func__, aq_error,
hw->aq.asq_last_status);
return;
}
memset(&config, 0, sizeof(config));
config.link_speed = abilities.link_speed;
config.abilities = abilities.abilities;
config.eee_capability = abilities.eee_capability;
config.eeer = abilities.eeer_val;
config.low_power_ctrl = abilities.d3_lpan;
config.fec_config = abilities.fec_cfg_curr_mod_ext_info
& I40E_AQ_PHY_FEC_CONFIG_MASK;
config.phy_type = 0;
config.phy_type_ext = 0;
config.abilities &= ~(I40E_AQ_PHY_FLAG_PAUSE_TX |
I40E_AQ_PHY_FLAG_PAUSE_RX);
switch (pf->fc) {
case I40E_FC_FULL:
config.abilities |= I40E_AQ_PHY_FLAG_PAUSE_TX |
I40E_AQ_PHY_FLAG_PAUSE_RX;
break;
case I40E_FC_RX_PAUSE:
config.abilities |= I40E_AQ_PHY_FLAG_PAUSE_RX;
break;
case I40E_FC_TX_PAUSE:
config.abilities |= I40E_AQ_PHY_FLAG_PAUSE_TX;
break;
default:
break;
}
if (enable) {
config.phy_type = phy_type;
config.phy_type_ext = phy_type_ext;
}
aq_error = i40e_aq_set_phy_config(hw, &config, NULL);
if (aq_error) {
device_printf(dev,
"%s: Error setting new phy config %d,"
" aq error: %d\n", __func__, aq_error,
hw->aq.asq_last_status);
return;
}
aq_error = i40e_aq_set_link_restart_an(hw, enable, NULL);
if (aq_error) {
device_printf(dev,
"%s: Error set link config %d,"
" aq error: %d\n", __func__, aq_error,
hw->aq.asq_last_status);
return;
}
}
static char *
ixl_phy_type_string(u32 bit_pos, bool ext)
{
static char * phy_types_str[32] = {
"SGMII",
"1000BASE-KX",
"10GBASE-KX4",
"10GBASE-KR",
"40GBASE-KR4",
"XAUI",
"XFI",
"SFI",
"XLAUI",
"XLPPI",
"40GBASE-CR4",
"10GBASE-CR1",
"SFP+ Active DA",
"QSFP+ Active DA",
"Reserved (14)",
"Reserved (15)",
"Reserved (16)",
"100BASE-TX",
"1000BASE-T",
"10GBASE-T",
"10GBASE-SR",
"10GBASE-LR",
"10GBASE-SFP+Cu",
"10GBASE-CR1",
"40GBASE-CR4",
"40GBASE-SR4",
"40GBASE-LR4",
"1000BASE-SX",
"1000BASE-LX",
"1000BASE-T Optical",
"20GBASE-KR2",
"Reserved (31)"
};
static char * ext_phy_types_str[8] = {
"25GBASE-KR",
"25GBASE-CR",
"25GBASE-SR",
"25GBASE-LR",
"25GBASE-AOC",
"25GBASE-ACC",
"2.5GBASE-T",
"5GBASE-T"
};
if (ext && bit_pos > 7) return "Invalid_Ext";
if (bit_pos > 31) return "Invalid";
return (ext) ? ext_phy_types_str[bit_pos] : phy_types_str[bit_pos];
}
int
ixl_aq_get_link_status(struct ixl_pf *pf, struct i40e_aqc_get_link_status *link_status)
{
device_t dev = pf->dev;
struct i40e_hw *hw = &pf->hw;
struct i40e_aq_desc desc;
enum i40e_status_code status;
struct i40e_aqc_get_link_status *aq_link_status =
(struct i40e_aqc_get_link_status *)&desc.params.raw;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_get_link_status);
link_status->command_flags = CPU_TO_LE16(I40E_AQ_LSE_ENABLE);
status = i40e_asq_send_command(hw, &desc, NULL, 0, NULL);
if (status) {
device_printf(dev,
"%s: i40e_aqc_opc_get_link_status status %s, aq error %s\n",
__func__, i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
return (EIO);
}
bcopy(aq_link_status, link_status, sizeof(struct i40e_aqc_get_link_status));
return (0);
}
static char *
ixl_phy_type_string_ls(u8 val)
{
if (val >= 0x1F)
return ixl_phy_type_string(val - 0x1F, true);
else
return ixl_phy_type_string(val, false);
}
static int
ixl_sysctl_link_status(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
device_t dev = pf->dev;
struct sbuf *buf;
int error = 0;
buf = sbuf_new_for_sysctl(NULL, NULL, 128, req);
if (!buf) {
device_printf(dev, "Could not allocate sbuf for sysctl output.\n");
return (ENOMEM);
}
struct i40e_aqc_get_link_status link_status;
error = ixl_aq_get_link_status(pf, &link_status);
if (error) {
sbuf_delete(buf);
return (error);
}
sbuf_printf(buf, "\n"
"PHY Type : 0x%02x<%s>\n"
"Speed : 0x%02x\n"
"Link info: 0x%02x\n"
"AN info : 0x%02x\n"
"Ext info : 0x%02x\n"
"Loopback : 0x%02x\n"
"Max Frame: %d\n"
"Config : 0x%02x\n"
"Power : 0x%02x",
link_status.phy_type,
ixl_phy_type_string_ls(link_status.phy_type),
link_status.link_speed,
link_status.link_info,
link_status.an_info,
link_status.ext_info,
link_status.loopback,
link_status.max_frame_size,
link_status.config,
link_status.power_desc);
error = sbuf_finish(buf);
if (error)
device_printf(dev, "Error finishing sbuf: %d\n", error);
sbuf_delete(buf);
return (error);
}
static int
ixl_sysctl_phy_abilities(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct i40e_hw *hw = &pf->hw;
device_t dev = pf->dev;
enum i40e_status_code status;
struct i40e_aq_get_phy_abilities_resp abilities;
struct sbuf *buf;
int error = 0;
buf = sbuf_new_for_sysctl(NULL, NULL, 128, req);
if (!buf) {
device_printf(dev, "Could not allocate sbuf for sysctl output.\n");
return (ENOMEM);
}
status = i40e_aq_get_phy_capabilities(hw,
FALSE, arg2 != 0, &abilities, NULL);
if (status) {
device_printf(dev,
"%s: i40e_aq_get_phy_capabilities() status %s, aq error %s\n",
__func__, i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
sbuf_delete(buf);
return (EIO);
}
sbuf_printf(buf, "\n"
"PHY Type : %08x",
abilities.phy_type);
if (abilities.phy_type != 0) {
sbuf_printf(buf, "<");
for (int i = 0; i < 32; i++)
if ((1 << i) & abilities.phy_type)
sbuf_printf(buf, "%s,", ixl_phy_type_string(i, false));
sbuf_printf(buf, ">");
}
sbuf_printf(buf, "\nPHY Ext : %02x",
abilities.phy_type_ext);
if (abilities.phy_type_ext != 0) {
sbuf_printf(buf, "<");
for (int i = 0; i < 4; i++)
if ((1 << i) & abilities.phy_type_ext)
sbuf_printf(buf, "%s,",
ixl_phy_type_string(i, true));
sbuf_printf(buf, ">");
}
sbuf_printf(buf, "\nSpeed : %02x", abilities.link_speed);
if (abilities.link_speed != 0) {
u8 link_speed;
sbuf_printf(buf, " <");
for (int i = 0; i < 8; i++) {
link_speed = (1 << i) & abilities.link_speed;
if (link_speed)
sbuf_printf(buf, "%s, ",
ixl_link_speed_string(link_speed));
}
sbuf_printf(buf, ">");
}
sbuf_printf(buf, "\n"
"Abilities: %02x\n"
"EEE cap : %04x\n"
"EEER reg : %08x\n"
"D3 Lpan : %02x\n"
"ID : %02x %02x %02x %02x\n"
"ModType : %02x %02x %02x\n"
"ModType E: %01x\n"
"FEC Cfg : %02x\n"
"Ext CC : %02x",
abilities.abilities, abilities.eee_capability,
abilities.eeer_val, abilities.d3_lpan,
abilities.phy_id[0], abilities.phy_id[1],
abilities.phy_id[2], abilities.phy_id[3],
abilities.module_type[0], abilities.module_type[1],
abilities.module_type[2], (abilities.fec_cfg_curr_mod_ext_info & 0xe0) >> 5,
abilities.fec_cfg_curr_mod_ext_info & 0x1F,
abilities.ext_comp_code);
error = sbuf_finish(buf);
if (error)
device_printf(dev, "Error finishing sbuf: %d\n", error);
sbuf_delete(buf);
return (error);
}
static int
ixl_sysctl_phy_statistics(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct i40e_hw *hw = &pf->hw;
device_t dev = pf->dev;
struct sbuf *buf;
int error = 0;
buf = sbuf_new_for_sysctl(NULL, NULL, 128, req);
if (buf == NULL) {
device_printf(dev, "Could not allocate sbuf for sysctl output.\n");
return (ENOMEM);
}
if (hw->mac.type == I40E_MAC_X722) {
sbuf_printf(buf, "\n"
"PCS Link Control Register: unavailable\n"
"PCS Link Status 1: unavailable\n"
"PCS Link Status 2: unavailable\n"
"XGMII FIFO Status: unavailable\n"
"Auto-Negotiation (AN) Status: unavailable\n"
"KR PCS Status: unavailable\n"
"KR FEC Status 1 – FEC Correctable Blocks Counter: unavailable\n"
"KR FEC Status 2 – FEC Uncorrectable Blocks Counter: unavailable"
);
} else {
sbuf_printf(buf, "\n"
"PCS Link Control Register: %#010X\n"
"PCS Link Status 1: %#010X\n"
"PCS Link Status 2: %#010X\n"
"XGMII FIFO Status: %#010X\n"
"Auto-Negotiation (AN) Status: %#010X\n"
"KR PCS Status: %#010X\n"
"KR FEC Status 1 – FEC Correctable Blocks Counter: %#010X\n"
"KR FEC Status 2 – FEC Uncorrectable Blocks Counter: %#010X",
rd32(hw, I40E_PRTMAC_PCS_LINK_CTRL),
rd32(hw, I40E_PRTMAC_PCS_LINK_STATUS1(0)),
rd32(hw, I40E_PRTMAC_PCS_LINK_STATUS2),
rd32(hw, I40E_PRTMAC_PCS_XGMII_FIFO_STATUS),
rd32(hw, I40E_PRTMAC_PCS_AN_LP_STATUS),
rd32(hw, I40E_PRTMAC_PCS_KR_STATUS),
rd32(hw, I40E_PRTMAC_PCS_FEC_KR_STATUS1),
rd32(hw, I40E_PRTMAC_PCS_FEC_KR_STATUS2)
);
}
error = sbuf_finish(buf);
if (error)
device_printf(dev, "Error finishing sbuf: %d\n", error);
sbuf_delete(buf);
return (error);
}
static int
ixl_sysctl_sw_filter_list(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct ixl_vsi *vsi = &pf->vsi;
struct ixl_mac_filter *f;
device_t dev = pf->dev;
int error = 0, ftl_len = 0, ftl_counter = 0;
struct sbuf *buf;
buf = sbuf_new_for_sysctl(NULL, NULL, 128, req);
if (!buf) {
device_printf(dev, "Could not allocate sbuf for sysctl output.\n");
return (ENOMEM);
}
sbuf_printf(buf, "\n");
sbuf_printf(buf, "PF Filters:\n");
LIST_FOREACH(f, &vsi->ftl, ftle)
ftl_len++;
if (ftl_len < 1)
sbuf_printf(buf, "(none)\n");
else {
LIST_FOREACH(f, &vsi->ftl, ftle) {
sbuf_printf(buf,
MAC_FORMAT ", vlan %4d, flags %#06x",
MAC_FORMAT_ARGS(f->macaddr), f->vlan, f->flags);
if (++ftl_counter != ftl_len)
sbuf_printf(buf, "\n");
}
}
#ifdef PCI_IOV
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);
LIST_FOREACH(f, &vsi->ftl, ftle)
ftl_len++;
if (ftl_len < 1)
sbuf_printf(buf, "(none)\n");
else {
LIST_FOREACH(f, &vsi->ftl, ftle) {
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)
device_printf(dev, "Error finishing sbuf: %d\n", error);
sbuf_delete(buf);
return (error);
}
#define IXL_SW_RES_SIZE 0x14
int
ixl_res_alloc_cmp(const void *a, const void *b)
{
const struct i40e_aqc_switch_resource_alloc_element_resp *one, *two;
one = (const struct i40e_aqc_switch_resource_alloc_element_resp *)a;
two = (const struct i40e_aqc_switch_resource_alloc_element_resp *)b;
return ((int)one->resource_type - (int)two->resource_type);
}
const char *
ixl_switch_res_type_string(u8 type)
{
static const char * ixl_switch_res_type_strings[IXL_SW_RES_SIZE] = {
"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 < IXL_SW_RES_SIZE)
return ixl_switch_res_type_strings[type];
else
return "(Reserved)";
}
static int
ixl_sysctl_hw_res_alloc(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct i40e_hw *hw = &pf->hw;
device_t dev = pf->dev;
struct sbuf *buf;
enum i40e_status_code status;
int error = 0;
u8 num_entries;
struct i40e_aqc_switch_resource_alloc_element_resp resp[IXL_SW_RES_SIZE];
buf = sbuf_new_for_sysctl(NULL, NULL, 128, req);
if (!buf) {
device_printf(dev, "Could not allocate sbuf for output.\n");
return (ENOMEM);
}
bzero(resp, sizeof(resp));
status = i40e_aq_get_switch_resource_alloc(hw, &num_entries,
resp,
IXL_SW_RES_SIZE,
NULL);
if (status) {
device_printf(dev,
"%s: get_switch_resource_alloc() error %s, aq error %s\n",
__func__, i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
sbuf_delete(buf);
return (error);
}
qsort(resp, num_entries,
sizeof(struct i40e_aqc_switch_resource_alloc_element_resp),
&ixl_res_alloc_cmp);
sbuf_cat(buf, "\n");
sbuf_printf(buf, "# of entries: %d\n", num_entries);
sbuf_printf(buf,
" Type | Guaranteed | Total | Used | Un-allocated\n"
" | (this) | (all) | (this) | (all) \n");
for (int i = 0; i < num_entries; i++) {
sbuf_printf(buf,
"%25s | %10d %5d %6d %12d",
ixl_switch_res_type_string(resp[i].resource_type),
resp[i].guaranteed,
resp[i].total,
resp[i].used,
resp[i].total_unalloced);
if (i < num_entries - 1)
sbuf_cat(buf, "\n");
}
error = sbuf_finish(buf);
if (error)
device_printf(dev, "Error finishing sbuf: %d\n", error);
sbuf_delete(buf);
return (error);
}
enum ixl_sw_seid_offset {
IXL_SW_SEID_EMP = 1,
IXL_SW_SEID_MAC_START = 2,
IXL_SW_SEID_MAC_END = 5,
IXL_SW_SEID_PF_START = 16,
IXL_SW_SEID_PF_END = 31,
IXL_SW_SEID_VF_START = 32,
IXL_SW_SEID_VF_END = 159,
};
static char *
ixl_switch_element_string(struct sbuf *s, u8 element_type, u16 seid)
{
sbuf_clear(s);
if (seid == IXL_SW_SEID_EMP) {
sbuf_cat(s, "EMP");
goto out;
} else if (seid >= IXL_SW_SEID_MAC_START &&
seid <= IXL_SW_SEID_MAC_END) {
sbuf_printf(s, "MAC %2d",
seid - IXL_SW_SEID_MAC_START);
goto out;
} else if (seid >= IXL_SW_SEID_PF_START &&
seid <= IXL_SW_SEID_PF_END) {
sbuf_printf(s, "PF %3d",
seid - IXL_SW_SEID_PF_START);
goto out;
} else if (seid >= IXL_SW_SEID_VF_START &&
seid <= IXL_SW_SEID_VF_END) {
sbuf_printf(s, "VF %3d",
seid - IXL_SW_SEID_VF_START);
goto out;
}
switch (element_type) {
case I40E_AQ_SW_ELEM_TYPE_BMC:
sbuf_cat(s, "BMC");
break;
case I40E_AQ_SW_ELEM_TYPE_PV:
sbuf_cat(s, "PV");
break;
case I40E_AQ_SW_ELEM_TYPE_VEB:
sbuf_cat(s, "VEB");
break;
case I40E_AQ_SW_ELEM_TYPE_PA:
sbuf_cat(s, "PA");
break;
case I40E_AQ_SW_ELEM_TYPE_VSI:
sbuf_printf(s, "VSI");
break;
default:
sbuf_cat(s, "?");
break;
}
out:
sbuf_finish(s);
return sbuf_data(s);
}
static int
ixl_sw_cfg_elem_seid_cmp(const void *a, const void *b)
{
const struct i40e_aqc_switch_config_element_resp *one, *two;
one = (const struct i40e_aqc_switch_config_element_resp *)a;
two = (const struct i40e_aqc_switch_config_element_resp *)b;
return ((int)one->seid - (int)two->seid);
}
static int
ixl_sysctl_switch_config(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct i40e_hw *hw = &pf->hw;
device_t dev = pf->dev;
struct sbuf *buf;
struct sbuf *nmbuf;
enum i40e_status_code status;
int error = 0;
u16 next = 0;
u8 aq_buf[I40E_AQ_LARGE_BUF];
struct i40e_aqc_switch_config_element_resp *elem;
struct i40e_aqc_get_switch_config_resp *sw_config;
sw_config = (struct i40e_aqc_get_switch_config_resp *)aq_buf;
buf = sbuf_new_for_sysctl(NULL, NULL, 128, req);
if (!buf) {
device_printf(dev, "Could not allocate sbuf for sysctl output.\n");
return (ENOMEM);
}
status = i40e_aq_get_switch_config(hw, sw_config,
sizeof(aq_buf), &next, NULL);
if (status) {
device_printf(dev,
"%s: aq_get_switch_config() error %s, aq error %s\n",
__func__, i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
sbuf_delete(buf);
return error;
}
if (next)
device_printf(dev, "%s: TODO: get more config with SEID %d\n",
__func__, next);
nmbuf = sbuf_new_auto();
if (!nmbuf) {
device_printf(dev, "Could not allocate sbuf for name output.\n");
sbuf_delete(buf);
return (ENOMEM);
}
qsort(sw_config->element, sw_config->header.num_reported,
sizeof(struct i40e_aqc_switch_config_element_resp),
&ixl_sw_cfg_elem_seid_cmp);
sbuf_cat(buf, "\n");
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);
sbuf_printf(buf,
"SEID ( Name ) | Up ( Name ) | Down ( Name ) | Conn Type\n"
" | | | (uplink)\n");
for (int i = 0; i < sw_config->header.num_reported; i++) {
elem = &sw_config->element[i];
sbuf_printf(buf, "%4d", elem->seid);
sbuf_cat(buf, " ");
sbuf_printf(buf, "(%8s)", ixl_switch_element_string(nmbuf,
elem->element_type, elem->seid));
sbuf_cat(buf, " | ");
sbuf_printf(buf, "%4d", elem->uplink_seid);
sbuf_cat(buf, " ");
sbuf_printf(buf, "(%8s)", ixl_switch_element_string(nmbuf,
0, elem->uplink_seid));
sbuf_cat(buf, " | ");
sbuf_printf(buf, "%4d", elem->downlink_seid);
sbuf_cat(buf, " ");
sbuf_printf(buf, "(%8s)", ixl_switch_element_string(nmbuf,
0, elem->downlink_seid));
sbuf_cat(buf, " | ");
sbuf_printf(buf, "%8d", elem->connection_type);
if (i < sw_config->header.num_reported - 1)
sbuf_cat(buf, "\n");
}
sbuf_delete(nmbuf);
error = sbuf_finish(buf);
if (error)
device_printf(dev, "Error finishing sbuf: %d\n", error);
sbuf_delete(buf);
return (error);
}
static int
ixl_sysctl_switch_vlans(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct i40e_hw *hw = &pf->hw;
device_t dev = pf->dev;
int requested_vlan = -1;
enum i40e_status_code status = 0;
int error = 0;
error = sysctl_handle_int(oidp, &requested_vlan, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
if ((hw->flags & I40E_HW_FLAG_802_1AD_CAPABLE) == 0) {
device_printf(dev, "Flags disallow setting of vlans\n");
return (ENODEV);
}
hw->switch_tag = requested_vlan;
device_printf(dev,
"Setting switch config to switch_tag=%04x, first_tag=%04x, second_tag=%04x\n",
hw->switch_tag, hw->first_tag, hw->second_tag);
status = i40e_aq_set_switch_config(hw, 0, 0, 0, NULL);
if (status) {
device_printf(dev,
"%s: aq_set_switch_config() error %s, aq error %s\n",
__func__, i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
return (status);
}
return (0);
}
static int
ixl_sysctl_hkey(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct i40e_hw *hw = &pf->hw;
device_t dev = pf->dev;
struct sbuf *buf;
int error = 0;
enum i40e_status_code status;
u32 reg;
struct i40e_aqc_get_set_rss_key_data key_data;
buf = sbuf_new_for_sysctl(NULL, NULL, 128, req);
if (!buf) {
device_printf(dev, "Could not allocate sbuf for output.\n");
return (ENOMEM);
}
bzero(&key_data, sizeof(key_data));
sbuf_cat(buf, "\n");
if (hw->mac.type == I40E_MAC_X722) {
status = i40e_aq_get_rss_key(hw, pf->vsi.vsi_num, &key_data);
if (status)
device_printf(dev, "i40e_aq_get_rss_key status %s, error %s\n",
i40e_stat_str(hw, status), i40e_aq_str(hw, hw->aq.asq_last_status));
} else {
for (int i = 0; i < IXL_RSS_KEY_SIZE_REG; i++) {
reg = i40e_read_rx_ctl(hw, I40E_PFQF_HKEY(i));
bcopy(®, ((caddr_t)&key_data) + (i << 2), 4);
}
}
ixl_sbuf_print_bytes(buf, (u8 *)&key_data, sizeof(key_data), 0, true);
error = sbuf_finish(buf);
if (error)
device_printf(dev, "Error finishing sbuf: %d\n", error);
sbuf_delete(buf);
return (error);
}
static void
ixl_sbuf_print_bytes(struct sbuf *sb, u8 *buf, int length, int label_offset, bool text)
{
int i, j, k, width;
char c;
if (length < 1 || buf == NULL) return;
int byte_stride = 16;
int lines = length / byte_stride;
int rem = length % byte_stride;
if (rem > 0)
lines++;
for (i = 0; i < lines; i++) {
width = (rem > 0 && i == lines - 1)
? rem : byte_stride;
sbuf_printf(sb, "%4d | ", label_offset + i * byte_stride);
for (j = 0; j < width; j++)
sbuf_printf(sb, "%02x ", buf[i * byte_stride + j]);
if (width < byte_stride) {
for (k = 0; k < (byte_stride - width); k++)
sbuf_printf(sb, " ");
}
if (!text) {
sbuf_printf(sb, "\n");
continue;
}
for (j = 0; j < width; j++) {
c = (char)buf[i * byte_stride + j];
if (c < 32 || c > 126)
sbuf_printf(sb, ".");
else
sbuf_printf(sb, "%c", c);
if (j == width - 1)
sbuf_printf(sb, "\n");
}
}
}
static int
ixl_sysctl_hlut(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct i40e_hw *hw = &pf->hw;
device_t dev = pf->dev;
struct sbuf *buf;
int error = 0;
enum i40e_status_code status;
u8 hlut[512];
u32 reg;
buf = sbuf_new_for_sysctl(NULL, NULL, 128, req);
if (!buf) {
device_printf(dev, "Could not allocate sbuf for output.\n");
return (ENOMEM);
}
bzero(hlut, sizeof(hlut));
sbuf_cat(buf, "\n");
if (hw->mac.type == I40E_MAC_X722) {
status = i40e_aq_get_rss_lut(hw, pf->vsi.vsi_num, TRUE, hlut, sizeof(hlut));
if (status)
device_printf(dev, "i40e_aq_get_rss_lut status %s, error %s\n",
i40e_stat_str(hw, status), i40e_aq_str(hw, hw->aq.asq_last_status));
} else {
for (int i = 0; i < hw->func_caps.rss_table_size >> 2; i++) {
reg = rd32(hw, I40E_PFQF_HLUT(i));
bcopy(®, &hlut[i << 2], 4);
}
}
ixl_sbuf_print_bytes(buf, hlut, 512, 0, false);
error = sbuf_finish(buf);
if (error)
device_printf(dev, "Error finishing sbuf: %d\n", error);
sbuf_delete(buf);
return (error);
}
static int
ixl_sysctl_hena(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct i40e_hw *hw = &pf->hw;
u64 hena;
hena = (u64)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(0)) |
((u64)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(1)) << 32);
return sysctl_handle_long(oidp, NULL, hena, req);
}
static int
ixl_sysctl_fw_link_management(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct i40e_hw *hw = &pf->hw;
device_t dev = pf->dev;
int requested_mode = -1;
enum i40e_status_code status = 0;
int error = 0;
error = sysctl_handle_int(oidp, &requested_mode, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
if (requested_mode < 0 || requested_mode > 1) {
device_printf(dev, "Valid modes are 0 or 1\n");
return (EINVAL);
}
status = i40e_aq_set_phy_debug(hw, !!(requested_mode) << 4, NULL);
if (status) {
device_printf(dev,
"%s: Error setting new phy debug mode %s,"
" aq error: %s\n", __func__, i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
return (EIO);
}
return (0);
}
static int
ixl_sysctl_read_i2c_diag_data(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
device_t dev = pf->dev;
struct sbuf *sbuf;
int error = 0;
u8 output;
if (req->oldptr == NULL) {
error = SYSCTL_OUT(req, 0, 128);
return (0);
}
error = pf->read_i2c_byte(pf, 0, 0xA0, &output);
if (error) {
device_printf(dev, "Error reading from i2c\n");
return (error);
}
if (output == 0x3) {
pf->read_i2c_byte(pf, 92, 0xA0, &output);
if (!(output & 0x60)) {
device_printf(dev, "Module doesn't support diagnostics: %02X\n", output);
return (0);
}
sbuf = sbuf_new_for_sysctl(NULL, NULL, 128, req);
for (u8 offset = 96; offset < 100; offset++) {
pf->read_i2c_byte(pf, offset, 0xA2, &output);
sbuf_printf(sbuf, "%02X ", output);
}
for (u8 offset = 102; offset < 106; offset++) {
pf->read_i2c_byte(pf, offset, 0xA2, &output);
sbuf_printf(sbuf, "%02X ", output);
}
} else if (output == 0xD || output == 0x11) {
sbuf = sbuf_new_for_sysctl(NULL, NULL, 128, req);
for (u8 offset = 22; offset < 24; offset++) {
pf->read_i2c_byte(pf, offset, 0xA0, &output);
sbuf_printf(sbuf, "%02X ", output);
}
for (u8 offset = 26; offset < 28; offset++) {
pf->read_i2c_byte(pf, offset, 0xA0, &output);
sbuf_printf(sbuf, "%02X ", output);
}
for (u8 offset = 34; offset < 36; offset++) {
pf->read_i2c_byte(pf, offset, 0xA0, &output);
sbuf_printf(sbuf, "%02X ", output);
}
for (u8 offset = 50; offset < 52; offset++) {
pf->read_i2c_byte(pf, offset, 0xA0, &output);
sbuf_printf(sbuf, "%02X ", output);
}
} else {
device_printf(dev, "Module is not SFP/SFP+/SFP28/QSFP+ (%02X)\n", output);
return (0);
}
sbuf_finish(sbuf);
sbuf_delete(sbuf);
return (0);
}
static int
ixl_sysctl_read_i2c_byte(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
device_t dev = pf->dev;
int input = -1, error = 0;
u8 dev_addr, offset, output;
error = sysctl_handle_int(oidp, &input, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
dev_addr = input & 0xFF;
if (dev_addr != 0xA0 && dev_addr != 0xA2) {
return (EINVAL);
}
offset = (input >> 8) & 0xFF;
error = pf->read_i2c_byte(pf, offset, dev_addr, &output);
if (error)
return (error);
device_printf(dev, "%02X\n", output);
return (0);
}
static int
ixl_sysctl_write_i2c_byte(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
device_t dev = pf->dev;
int input = -1, error = 0;
u8 dev_addr, offset, value;
error = sysctl_handle_int(oidp, &input, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
dev_addr = input & 0xFF;
if (dev_addr != 0xA0 && dev_addr != 0xA2) {
return (EINVAL);
}
offset = (input >> 8) & 0xFF;
value = (input >> 16) & 0xFF;
error = pf->write_i2c_byte(pf, offset, dev_addr, value);
if (error)
return (error);
device_printf(dev, "%02X written\n", value);
return (0);
}
static int
ixl_get_fec_config(struct ixl_pf *pf, struct i40e_aq_get_phy_abilities_resp *abilities,
u8 bit_pos, int *is_set)
{
device_t dev = pf->dev;
struct i40e_hw *hw = &pf->hw;
enum i40e_status_code status;
if (IXL_PF_IN_RECOVERY_MODE(pf))
return (EIO);
status = i40e_aq_get_phy_capabilities(hw,
FALSE, FALSE, abilities, NULL);
if (status) {
device_printf(dev,
"%s: i40e_aq_get_phy_capabilities() status %s, aq error %s\n",
__func__, i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
return (EIO);
}
*is_set = !!(abilities->fec_cfg_curr_mod_ext_info & bit_pos);
return (0);
}
static int
ixl_set_fec_config(struct ixl_pf *pf, struct i40e_aq_get_phy_abilities_resp *abilities,
u8 bit_pos, int set)
{
device_t dev = pf->dev;
struct i40e_hw *hw = &pf->hw;
struct i40e_aq_set_phy_config config;
enum i40e_status_code status;
memset(&config, 0, sizeof(config));
config.fec_config = abilities->fec_cfg_curr_mod_ext_info & ~(bit_pos);
if (set)
config.fec_config |= bit_pos;
if (config.fec_config != abilities->fec_cfg_curr_mod_ext_info) {
config.abilities |= I40E_AQ_PHY_ENABLE_ATOMIC_LINK;
config.phy_type = abilities->phy_type;
config.phy_type_ext = abilities->phy_type_ext;
config.link_speed = abilities->link_speed;
config.eee_capability = abilities->eee_capability;
config.eeer = abilities->eeer_val;
config.low_power_ctrl = abilities->d3_lpan;
status = i40e_aq_set_phy_config(hw, &config, NULL);
if (status) {
device_printf(dev,
"%s: i40e_aq_set_phy_config() status %s, aq error %s\n",
__func__, i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
return (EIO);
}
}
return (0);
}
static int
ixl_sysctl_fec_fc_ability(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
int mode, error = 0;
struct i40e_aq_get_phy_abilities_resp abilities;
error = ixl_get_fec_config(pf, &abilities, I40E_AQ_ENABLE_FEC_KR, &mode);
if (error)
return (error);
error = sysctl_handle_int(oidp, &mode, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
return ixl_set_fec_config(pf, &abilities, I40E_AQ_SET_FEC_ABILITY_KR, !!(mode));
}
static int
ixl_sysctl_fec_rs_ability(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
int mode, error = 0;
struct i40e_aq_get_phy_abilities_resp abilities;
error = ixl_get_fec_config(pf, &abilities, I40E_AQ_ENABLE_FEC_RS, &mode);
if (error)
return (error);
error = sysctl_handle_int(oidp, &mode, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
return ixl_set_fec_config(pf, &abilities, I40E_AQ_SET_FEC_ABILITY_RS, !!(mode));
}
static int
ixl_sysctl_fec_fc_request(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
int mode, error = 0;
struct i40e_aq_get_phy_abilities_resp abilities;
error = ixl_get_fec_config(pf, &abilities, I40E_AQ_REQUEST_FEC_KR, &mode);
if (error)
return (error);
error = sysctl_handle_int(oidp, &mode, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
return ixl_set_fec_config(pf, &abilities, I40E_AQ_SET_FEC_REQUEST_KR, !!(mode));
}
static int
ixl_sysctl_fec_rs_request(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
int mode, error = 0;
struct i40e_aq_get_phy_abilities_resp abilities;
error = ixl_get_fec_config(pf, &abilities, I40E_AQ_REQUEST_FEC_RS, &mode);
if (error)
return (error);
error = sysctl_handle_int(oidp, &mode, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
return ixl_set_fec_config(pf, &abilities, I40E_AQ_SET_FEC_REQUEST_RS, !!(mode));
}
static int
ixl_sysctl_fec_auto_enable(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
int mode, error = 0;
struct i40e_aq_get_phy_abilities_resp abilities;
error = ixl_get_fec_config(pf, &abilities, I40E_AQ_ENABLE_FEC_AUTO, &mode);
if (error)
return (error);
error = sysctl_handle_int(oidp, &mode, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
return ixl_set_fec_config(pf, &abilities, I40E_AQ_SET_FEC_AUTO, !!(mode));
}
static int
ixl_sysctl_dump_debug_data(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct i40e_hw *hw = &pf->hw;
device_t dev = pf->dev;
struct sbuf *buf;
int error = 0;
enum i40e_status_code status;
buf = sbuf_new_for_sysctl(NULL, NULL, 128, req);
if (!buf) {
device_printf(dev, "Could not allocate sbuf for output.\n");
return (ENOMEM);
}
u8 *final_buff;
#define IXL_FINAL_BUFF_SIZE (1280 * 1024)
final_buff = malloc(IXL_FINAL_BUFF_SIZE, M_IXL, M_NOWAIT);
if (final_buff == NULL) {
device_printf(dev, "Could not allocate memory for output.\n");
goto out;
}
int final_buff_len = 0;
u8 cluster_id = 1;
bool more = true;
u8 dump_buf[4096];
u16 curr_buff_size = 4096;
u8 curr_next_table = 0;
u32 curr_next_index = 0;
u16 ret_buff_size;
u8 ret_next_table;
u32 ret_next_index;
sbuf_cat(buf, "\n");
while (more) {
status = i40e_aq_debug_dump(hw, cluster_id, curr_next_table, curr_next_index, curr_buff_size,
dump_buf, &ret_buff_size, &ret_next_table, &ret_next_index, NULL);
if (status) {
device_printf(dev, "i40e_aq_debug_dump status %s, error %s\n",
i40e_stat_str(hw, status), i40e_aq_str(hw, hw->aq.asq_last_status));
goto free_out;
}
bcopy(dump_buf, (caddr_t)final_buff + final_buff_len, ret_buff_size);
final_buff_len += ret_buff_size;
if (ret_next_table != curr_next_table) {
sbuf_printf(buf, "%d:", curr_next_table);
int bytes_printed = 0;
while (bytes_printed <= final_buff_len) {
sbuf_printf(buf, "%16D", ((caddr_t)final_buff + bytes_printed), "");
bytes_printed += 16;
}
sbuf_cat(buf, "\n");
if (ret_next_table == 0xFF)
break;
bzero(final_buff, IXL_FINAL_BUFF_SIZE);
final_buff_len = 0;
}
if (ret_next_index == 0xFFFFFFFF)
ret_next_index = 0;
bzero(dump_buf, sizeof(dump_buf));
curr_next_table = ret_next_table;
curr_next_index = ret_next_index;
}
free_out:
free(final_buff, M_IXL);
out:
error = sbuf_finish(buf);
if (error)
device_printf(dev, "Error finishing sbuf: %d\n", error);
sbuf_delete(buf);
return (error);
}
static int
ixl_start_fw_lldp(struct ixl_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
enum i40e_status_code status;
status = i40e_aq_start_lldp(hw, false, NULL);
if (status != I40E_SUCCESS) {
switch (hw->aq.asq_last_status) {
case I40E_AQ_RC_EEXIST:
device_printf(pf->dev,
"FW LLDP agent is already running\n");
break;
case I40E_AQ_RC_EPERM:
device_printf(pf->dev,
"Device configuration forbids SW from starting "
"the LLDP agent. Set the \"LLDP Agent\" UEFI HII "
"attribute to \"Enabled\" to use this sysctl\n");
return (EINVAL);
default:
device_printf(pf->dev,
"Starting FW LLDP agent failed: error: %s, %s\n",
i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
return (EINVAL);
}
}
ixl_clear_state(&pf->state, IXL_STATE_FW_LLDP_DISABLED);
return (0);
}
static int
ixl_stop_fw_lldp(struct ixl_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
device_t dev = pf->dev;
enum i40e_status_code status;
if (hw->func_caps.npar_enable != 0) {
device_printf(dev,
"Disabling FW LLDP agent is not supported on this device\n");
return (EINVAL);
}
if ((hw->flags & I40E_HW_FLAG_FW_LLDP_STOPPABLE) == 0) {
device_printf(dev,
"Disabling FW LLDP agent is not supported in this FW version. Please update FW to enable this feature.\n");
return (EINVAL);
}
status = i40e_aq_stop_lldp(hw, true, false, NULL);
if (status != I40E_SUCCESS) {
if (hw->aq.asq_last_status != I40E_AQ_RC_EPERM) {
device_printf(dev,
"Disabling FW LLDP agent failed: error: %s, %s\n",
i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
return (EINVAL);
}
device_printf(dev, "FW LLDP agent is already stopped\n");
}
i40e_aq_set_dcb_parameters(hw, true, NULL);
ixl_set_state(&pf->state, IXL_STATE_FW_LLDP_DISABLED);
return (0);
}
static int
ixl_sysctl_fw_lldp(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
int state, new_state, error = 0;
state = new_state = !ixl_test_state(&pf->state, IXL_STATE_FW_LLDP_DISABLED);
error = sysctl_handle_int(oidp, &new_state, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
if (new_state == state)
return (error);
if (new_state == 0)
return ixl_stop_fw_lldp(pf);
return ixl_start_fw_lldp(pf);
}
static int
ixl_sysctl_eee_enable(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
int state, new_state;
int sysctl_handle_status = 0;
enum i40e_status_code cmd_status;
state = new_state = ixl_test_state(&pf->state, IXL_STATE_EEE_ENABLED);
sysctl_handle_status = sysctl_handle_int(oidp, &new_state, 0, req);
if ((sysctl_handle_status) || (req->newptr == NULL))
return (sysctl_handle_status);
if (new_state == state)
return (0);
cmd_status = i40e_enable_eee(&pf->hw, (bool)(!!new_state));
if (!cmd_status) {
if (new_state == 0)
ixl_clear_state(&pf->state, IXL_STATE_EEE_ENABLED);
else
ixl_set_state(&pf->state, IXL_STATE_EEE_ENABLED);
} else if (cmd_status == I40E_ERR_CONFIG)
return (EPERM);
else
return (EIO);
return (0);
}
static int
ixl_sysctl_set_link_active(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
int error, state;
state = ixl_test_state(&pf->state, IXL_STATE_LINK_ACTIVE_ON_DOWN);
error = sysctl_handle_int(oidp, &state, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
if (state == 0)
ixl_clear_state(&pf->state, IXL_STATE_LINK_ACTIVE_ON_DOWN);
else
ixl_set_state(&pf->state, IXL_STATE_LINK_ACTIVE_ON_DOWN);
return (0);
}
int
ixl_attach_get_link_status(struct ixl_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
device_t dev = pf->dev;
enum i40e_status_code status;
if (((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver < 33)) ||
(hw->aq.fw_maj_ver < 4)) {
i40e_msec_delay(75);
status = i40e_aq_set_link_restart_an(hw, TRUE, NULL);
if (status != I40E_SUCCESS) {
device_printf(dev,
"%s link restart failed status: %s, aq_err=%s\n",
__func__, i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
return (EINVAL);
}
}
hw->phy.get_link_info = TRUE;
status = i40e_get_link_status(hw, &pf->link_up);
if (status != I40E_SUCCESS) {
device_printf(dev,
"%s get link status, status: %s aq_err=%s\n",
__func__, i40e_stat_str(hw, status),
i40e_aq_str(hw, hw->aq.asq_last_status));
ixl_set_state(&pf->state, IXL_STATE_LINK_POLLING);
pf->link_poll_start = getsbinuptime();
return (EAGAIN);
}
ixl_dbg_link(pf, "%s link_up: %d\n", __func__, pf->link_up);
if (pf->fc == -1)
pf->fc = hw->fc.current_mode;
return (0);
}
static int
ixl_sysctl_do_pf_reset(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
int requested = 0, error = 0;
error = sysctl_handle_int(oidp, &requested, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
ixl_set_state(&pf->state, IXL_STATE_PF_RESET_REQ);
return (error);
}
static int
ixl_sysctl_do_core_reset(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct i40e_hw *hw = &pf->hw;
int requested = 0, error = 0;
error = sysctl_handle_int(oidp, &requested, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
wr32(hw, I40E_GLGEN_RTRIG, I40E_GLGEN_RTRIG_CORER_MASK);
return (error);
}
static int
ixl_sysctl_do_global_reset(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct i40e_hw *hw = &pf->hw;
int requested = 0, error = 0;
error = sysctl_handle_int(oidp, &requested, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
wr32(hw, I40E_GLGEN_RTRIG, I40E_GLGEN_RTRIG_GLOBR_MASK);
return (error);
}
static int
ixl_sysctl_queue_interrupt_table(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct ixl_vsi *vsi = &pf->vsi;
struct i40e_hw *hw = vsi->hw;
device_t dev = pf->dev;
struct sbuf *buf;
int error = 0;
struct ixl_rx_queue *rx_que = vsi->rx_queues;
struct ixl_tx_queue *tx_que = vsi->tx_queues;
buf = sbuf_new_for_sysctl(NULL, NULL, 128, req);
if (!buf) {
device_printf(dev, "Could not allocate sbuf for output.\n");
return (ENOMEM);
}
sbuf_cat(buf, "\n");
for (int i = 0; i < vsi->num_rx_queues; i++) {
rx_que = &vsi->rx_queues[i];
sbuf_printf(buf,
"(rxq %3d): %d LNKLSTN: %08x QINT_RQCTL: %08x\n",
i, rx_que->msix,
rd32(hw, I40E_PFINT_LNKLSTN(rx_que->msix - 1)),
rd32(hw, I40E_QINT_RQCTL(rx_que->msix - 1)));
}
for (int i = 0; i < vsi->num_tx_queues; i++) {
tx_que = &vsi->tx_queues[i];
sbuf_printf(buf, "(txq %3d): %d QINT_TQCTL: %08x\n",
i, tx_que->msix,
rd32(hw, I40E_QINT_TQCTL(tx_que->msix - 1)));
}
error = sbuf_finish(buf);
if (error)
device_printf(dev, "Error finishing sbuf: %d\n", error);
sbuf_delete(buf);
return (error);
}
static int
ixl_sysctl_debug_queue_int_ctln(SYSCTL_HANDLER_ARGS)
{
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct ixl_vsi *vsi = &pf->vsi;
struct i40e_hw *hw = vsi->hw;
device_t dev = pf->dev;
struct sbuf *buf;
int error = 0;
struct ixl_rx_queue *rx_que = vsi->rx_queues;
buf = sbuf_new_for_sysctl(NULL, NULL, 128, req);
if (!buf) {
device_printf(dev, "Could not allocate sbuf for output.\n");
return (ENOMEM);
}
sbuf_cat(buf, "\n");
for (int i = 0; i < vsi->num_rx_queues; i++) {
rx_que = &vsi->rx_queues[i];
sbuf_printf(buf,
"(rxq %3d): %d PFINT_DYN_CTLN: %08x\n",
i, rx_que->msix,
rd32(hw, I40E_PFINT_DYN_CTLN(rx_que->msix - 1)));
}
error = sbuf_finish(buf);
if (error)
device_printf(dev, "Error finishing sbuf: %d\n", error);
sbuf_delete(buf);
return (error);
}