/* Helper macro to define an i40e_stat structure with proper size and type. *Usethiswhendefiningconstantstatisticsarrays.Notethat@_typeexpects *onlyatypenameandisusedmultipletimes.
*/ #define I40E_STAT(_type, _name, _stat) { \
.stat_string = _name, \
.sizeof_stat = sizeof_field(_type, _stat), \
.stat_offset = offsetof(_type, _stat) \
}
/* Helper macro for defining some statistics directly copied from the netdev *statsstructure.
*/ #define I40E_NETDEV_STAT(_net_stat) \
I40E_STAT(struct rtnl_link_stats64, #_net_stat, _net_stat)
/* Helper macro for defining some statistics related to queues */ #define I40E_QUEUE_STAT(_name, _stat) \
I40E_STAT(struct i40e_ring, _name, _stat)
/* Stats associated with a Tx or Rx ring */ staticconststruct i40e_stats i40e_gstrings_queue_stats[] = {
I40E_QUEUE_STAT("%s-%u.packets", stats.packets),
I40E_QUEUE_STAT("%s-%u.bytes", stats.bytes),
};
staticconstchar i40e_gstrings_test[][ETH_GSTRING_LEN] = { "Register test (offline)", "Eeprom test (offline)", "Interrupt test (offline)", "Link test (on/offline)"
};
/** *i40e_partition_setting_complaint-genericcomplaintforMFPrestriction *@pf:thePFstruct
**/ staticvoid i40e_partition_setting_complaint(struct i40e_pf *pf)
{
dev_info(&pf->pdev->dev, "The link settings are allowed to be changed only from the first partition of a given port. Please switch to the first partition in order to change the setting.\n");
}
/* Initialize supported and advertised settings based on phy settings */ switch (hw_link_info->phy_type) { case I40E_PHY_TYPE_40GBASE_CR4: case I40E_PHY_TYPE_40GBASE_CR4_CU:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseCR4_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, advertising, 40000baseCR4_Full); break; case I40E_PHY_TYPE_XLAUI: case I40E_PHY_TYPE_XLPPI: case I40E_PHY_TYPE_40GBASE_AOC:
ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseCR4_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, 40000baseCR4_Full); break; case I40E_PHY_TYPE_40GBASE_SR4:
ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseSR4_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, 40000baseSR4_Full); break; case I40E_PHY_TYPE_40GBASE_LR4:
ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseLR4_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, 40000baseLR4_Full); break; case I40E_PHY_TYPE_25GBASE_SR: case I40E_PHY_TYPE_25GBASE_LR: case I40E_PHY_TYPE_10GBASE_SR: case I40E_PHY_TYPE_10GBASE_LR: case I40E_PHY_TYPE_1000BASE_SX: case I40E_PHY_TYPE_1000BASE_LX:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported, 25000baseSR_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, 25000baseSR_Full);
i40e_get_settings_link_up_fec(hw_link_info->req_fec_info, ks);
ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseSR_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseSR_Full);
ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseLR_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseLR_Full);
ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseX_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseX_Full);
ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseT_Full); if (hw_link_info->module_type[2] &
I40E_MODULE_TYPE_1000BASE_SX ||
hw_link_info->module_type[2] &
I40E_MODULE_TYPE_1000BASE_LX) {
ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseT_Full); if (hw_link_info->requested_speeds &
I40E_LINK_SPEED_1GB)
ethtool_link_ksettings_add_link_mode(
ks, advertising, 1000baseT_Full);
} if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB)
ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseT_Full); break; case I40E_PHY_TYPE_10GBASE_T: case I40E_PHY_TYPE_5GBASE_T_LINK_STATUS: case I40E_PHY_TYPE_2_5GBASE_T_LINK_STATUS: case I40E_PHY_TYPE_1000BASE_T: case I40E_PHY_TYPE_100BASE_TX:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseT_Full);
ethtool_link_ksettings_add_link_mode(ks, supported, 5000baseT_Full);
ethtool_link_ksettings_add_link_mode(ks, supported, 2500baseT_Full);
ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseT_Full);
ethtool_link_ksettings_add_link_mode(ks, supported, 100baseT_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB)
ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseT_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_5GB)
ethtool_link_ksettings_add_link_mode(ks, advertising, 5000baseT_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_2_5GB)
ethtool_link_ksettings_add_link_mode(ks, advertising, 2500baseT_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB)
ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseT_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB)
ethtool_link_ksettings_add_link_mode(ks, advertising, 100baseT_Full); break; case I40E_PHY_TYPE_1000BASE_T_OPTICAL:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseT_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseT_Full); break; case I40E_PHY_TYPE_10GBASE_CR1_CU: case I40E_PHY_TYPE_10GBASE_CR1:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseT_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseT_Full); break; case I40E_PHY_TYPE_XAUI: case I40E_PHY_TYPE_XFI: case I40E_PHY_TYPE_SFI: case I40E_PHY_TYPE_10GBASE_SFPP_CU: case I40E_PHY_TYPE_10GBASE_AOC:
ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseT_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB)
ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseT_Full);
i40e_get_settings_link_up_fec(hw_link_info->req_fec_info, ks); break; case I40E_PHY_TYPE_SGMII:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseT_Full); if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB)
ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseT_Full); if (test_bit(I40E_HW_CAP_100M_SGMII, pf->hw.caps)) {
ethtool_link_ksettings_add_link_mode(ks, supported, 100baseT_Full); if (hw_link_info->requested_speeds &
I40E_LINK_SPEED_100MB)
ethtool_link_ksettings_add_link_mode(
ks, advertising, 100baseT_Full);
} break; case I40E_PHY_TYPE_40GBASE_KR4: case I40E_PHY_TYPE_25GBASE_KR: case I40E_PHY_TYPE_20GBASE_KR2: case I40E_PHY_TYPE_10GBASE_KR: case I40E_PHY_TYPE_10GBASE_KX4: case I40E_PHY_TYPE_1000BASE_KX:
ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseKR4_Full);
ethtool_link_ksettings_add_link_mode(ks, supported, 25000baseKR_Full);
ethtool_link_ksettings_add_link_mode(ks, supported, 20000baseKR2_Full);
ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseKR_Full);
ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseKX4_Full);
ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseKX_Full);
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, advertising, 40000baseKR4_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, 25000baseKR_Full);
i40e_get_settings_link_up_fec(hw_link_info->req_fec_info, ks);
ethtool_link_ksettings_add_link_mode(ks, advertising, 20000baseKR2_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseKR_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseKX4_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, 1000baseKX_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); break; case I40E_PHY_TYPE_25GBASE_CR:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported, 25000baseCR_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, 25000baseCR_Full);
i40e_get_settings_link_up_fec(hw_link_info->req_fec_info, ks);
break; case I40E_PHY_TYPE_25GBASE_AOC: case I40E_PHY_TYPE_25GBASE_ACC:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported, 25000baseCR_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, 25000baseCR_Full);
i40e_get_settings_link_up_fec(hw_link_info->req_fec_info, ks);
ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseCR_Full);
ethtool_link_ksettings_add_link_mode(ks, advertising, 10000baseCR_Full); break; default: /* if we got here and link is up something bad is afoot */
netdev_info(netdev, "WARNING: Link is up but PHY type 0x%x is not recognized, or incorrect cable is in use\n",
hw_link_info->phy_type);
}
/* Now that we've worked out everything that could be supported by the *currentPHYtype,getwhatissupportedbytheNVMandintersect *themtogetwhatistrulysupported
*/
memset(&cap_ksettings, 0, sizeof(struct ethtool_link_ksettings));
i40e_phy_type_to_ethtool(pf, &cap_ksettings);
ethtool_intersect_link_masks(ks, &cap_ksettings);
/* Set speed and duplex */ switch (link_speed) { case I40E_LINK_SPEED_40GB:
ks->base.speed = SPEED_40000; break; case I40E_LINK_SPEED_25GB:
ks->base.speed = SPEED_25000; break; case I40E_LINK_SPEED_20GB:
ks->base.speed = SPEED_20000; break; case I40E_LINK_SPEED_10GB:
ks->base.speed = SPEED_10000; break; case I40E_LINK_SPEED_5GB:
ks->base.speed = SPEED_5000; break; case I40E_LINK_SPEED_2_5GB:
ks->base.speed = SPEED_2500; break; case I40E_LINK_SPEED_1GB:
ks->base.speed = SPEED_1000; break; case I40E_LINK_SPEED_100MB:
ks->base.speed = SPEED_100; break; default:
ks->base.speed = SPEED_UNKNOWN; break;
}
ks->base.duplex = DUPLEX_FULL;
}
/** *i40e_get_settings_link_down-GettheLinksettingsforwhenlinkisdown *@hw:hwstructure *@ks:ethtoolksettingstofillin *@pf:pointertophysicalfunctionstruct * *Reportslinksettingsthatcanbedeterminedwhenlinkisdown
**/ staticvoid i40e_get_settings_link_down(struct i40e_hw *hw, struct ethtool_link_ksettings *ks, struct i40e_pf *pf)
{ /* link is down and the driver needs to fall back on *supportedphytypestofigureoutwhatinfotodisplay
*/
i40e_phy_type_to_ethtool(pf, ks);
/* With no link speed and duplex are unknown */
ks->base.speed = SPEED_UNKNOWN;
ks->base.duplex = DUPLEX_UNKNOWN;
}
if (link_up)
i40e_get_settings_link_up(hw, ks, netdev, pf); else
i40e_get_settings_link_down(hw, ks, pf);
/* Now set the settings that don't rely on link being up/down */ /* Set autoneg settings */
ks->base.autoneg = ((hw_link_info->an_info & I40E_AQ_AN_COMPLETED) ?
AUTONEG_ENABLE : AUTONEG_DISABLE);
/* Set media type settings */ switch (hw->phy.media_type) { case I40E_MEDIA_TYPE_BACKPLANE:
ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, supported, Backplane);
ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
ethtool_link_ksettings_add_link_mode(ks, advertising,
Backplane);
ks->base.port = PORT_NONE; break; case I40E_MEDIA_TYPE_BASET:
ethtool_link_ksettings_add_link_mode(ks, supported, TP);
ethtool_link_ksettings_add_link_mode(ks, advertising, TP);
ks->base.port = PORT_TP; break; case I40E_MEDIA_TYPE_DA: case I40E_MEDIA_TYPE_CX4:
ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE);
ethtool_link_ksettings_add_link_mode(ks, advertising, FIBRE);
ks->base.port = PORT_DA; break; case I40E_MEDIA_TYPE_FIBER:
ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE);
ethtool_link_ksettings_add_link_mode(ks, advertising, FIBRE);
ks->base.port = PORT_FIBRE; break; case I40E_MEDIA_TYPE_UNKNOWN: default:
ks->base.port = PORT_OTHER; break;
}
/* Set flow control settings */
ethtool_link_ksettings_add_link_mode(ks, supported, Pause);
ethtool_link_ksettings_add_link_mode(ks, supported, Asym_Pause);
/* save autoneg out of ksettings */
autoneg = copy_ks.base.autoneg;
speed = copy_ks.base.speed;
/* get our own copy of the bits to check against */
memset(&safe_ks, 0, sizeof(struct ethtool_link_ksettings));
safe_ks.base.cmd = copy_ks.base.cmd;
safe_ks.base.link_mode_masks_nwords =
copy_ks.base.link_mode_masks_nwords;
i40e_get_link_ksettings(netdev, &safe_ks);
/* Get link modes supported by hardware and check against modes *requestedbytheuser.Returnanerrorifunsupportedmodewasset.
*/ if (!bitmap_subset(copy_ks.link_modes.advertising,
safe_ks.link_modes.supported,
__ETHTOOL_LINK_MODE_MASK_NBITS)) return -EINVAL;
/* set autoneg back to what it currently is */
copy_ks.base.autoneg = safe_ks.base.autoneg;
copy_ks.base.speed = safe_ks.base.speed;
while (test_and_set_bit(__I40E_CONFIG_BUSY, pf->state)) {
timeout--; if (!timeout) return -EBUSY;
usleep_range(1000, 2000);
}
/* Get the current phy config */
status = i40e_aq_get_phy_capabilities(hw, false, false, &abilities,
NULL); if (status) {
err = -EAGAIN; goto done;
}
/* Copy abilities to config in case autoneg is not *setbelow
*/
memset(&config, 0, sizeof(struct i40e_aq_set_phy_config));
config.abilities = abilities.abilities;
/* Check autoneg */ if (autoneg == AUTONEG_ENABLE) { /* If autoneg was not already enabled */ if (!(hw->phy.link_info.an_info & I40E_AQ_AN_COMPLETED)) { /* If autoneg is not supported, return error */ if (!ethtool_link_ksettings_test_link_mode(&safe_ks,
supported,
Autoneg)) {
netdev_info(netdev, "Autoneg not supported on this phy\n");
err = -EINVAL; goto done;
} /* Autoneg is allowed to change */
config.abilities = abilities.abilities |
I40E_AQ_PHY_ENABLE_AN;
autoneg_changed = true;
}
} else { /* If autoneg is currently enabled */ if (hw->phy.link_info.an_info & I40E_AQ_AN_COMPLETED) { /* If autoneg is supported 10GBASE_T is the only PHY *thatcandisableit,sootherwisereturnerror
*/ if (ethtool_link_ksettings_test_link_mode(&safe_ks,
supported,
Autoneg) &&
hw->phy.media_type != I40E_MEDIA_TYPE_BASET) {
netdev_info(netdev, "Autoneg cannot be disabled on this phy\n");
err = -EINVAL; goto done;
} /* Autoneg is allowed to change */
config.abilities = abilities.abilities &
~I40E_AQ_PHY_ENABLE_AN;
autoneg_changed = true;
}
}
/* Autonegotiation must be disabled to change speed */ if ((speed != SPEED_UNKNOWN && safe_ks.base.speed != speed) &&
(autoneg == AUTONEG_DISABLE ||
(safe_ks.base.autoneg == AUTONEG_DISABLE && !autoneg_changed))) {
link_speed = i40e_speed_to_link_speed(speed, ks); if (link_speed == I40E_LINK_SPEED_UNKNOWN) {
netdev_info(netdev, "Given speed is not supported\n");
err = -EOPNOTSUPP; goto done;
} else {
config.link_speed = link_speed;
}
} else { if (safe_ks.base.speed != speed) {
netdev_info(netdev, "Unable to set speed, disable autoneg\n");
err = -EOPNOTSUPP; goto done;
}
}
/* If speed didn't get set, set it to what it currently is. *Thisisneededbecauseifadvertiseis0(asitiswhenautoneg *isdisabled)thenspeedwon'tgetset.
*/ if (!config.link_speed)
config.link_speed = abilities.link_speed; if (autoneg_changed || abilities.link_speed != config.link_speed) { /* copy over the rest of the abilities */
config.phy_type = abilities.phy_type;
config.phy_type_ext = abilities.phy_type_ext;
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;
/* save the requested speeds */
hw->phy.link_info.requested_speeds = config.link_speed; /* set link and auto negotiation so changes take effect */
config.abilities |= I40E_AQ_PHY_ENABLE_ATOMIC_LINK; /* If link is up put link down */ if (hw->phy.link_info.link_info & I40E_AQ_LINK_UP) { /* Tell the OS link is going down, the link will go *backupwhenfwsaysitisreadyasynchronously
*/
i40e_print_link_message(vsi, false);
netif_carrier_off(netdev);
netif_tx_stop_all_queues(netdev);
}
/* make the aq call */
status = i40e_aq_set_phy_config(hw, &config, NULL); if (status) {
netdev_info(netdev, "Set phy config failed, err %pe aq_err %s\n",
ERR_PTR(status),
libie_aq_str(hw->aq.asq_last_status));
err = -EAGAIN; goto done;
}
status = i40e_update_link_info(hw); if (status)
netdev_dbg(netdev, "Updating link info failed with err %pe aq_err %s\n",
ERR_PTR(status),
libie_aq_str(hw->aq.asq_last_status));
/* Changing the port's flow control is not supported if this isn't the *port'scontrollingPF
*/ if (hw->partition_id != 1) {
i40e_partition_setting_complaint(pf); return -EOPNOTSUPP;
}
if (vsi->type != I40E_VSI_MAIN) return -EOPNOTSUPP;
/* If we have link and don't have autoneg */ if (!test_bit(__I40E_DOWN, pf->state) && !is_an) { /* Send message that it might not necessarily work*/
netdev_info(netdev, "Autoneg did not complete so changing settings may not result in an actual change.\n");
}
if (dcbx_cfg->pfc.pfcenable) {
netdev_info(netdev, "Priority flow control enabled. Cannot set link flow control.\n"); return -EOPNOTSUPP;
}
/* Tell the OS link is going down, the link will go back up when fw *saysitisreadyasynchronously
*/
i40e_print_link_message(vsi, false);
netif_carrier_off(netdev);
netif_tx_stop_all_queues(netdev);
/* Set the fc mode and only restart an if link is up*/
status = i40e_set_fc(hw, &aq_failures, link_up);
if (aq_failures & I40E_SET_FC_AQ_FAIL_GET) {
netdev_info(netdev, "Set fc failed on the get_phy_capabilities call with err %pe aq_err %s\n",
ERR_PTR(status),
libie_aq_str(hw->aq.asq_last_status));
err = -EAGAIN;
} if (aq_failures & I40E_SET_FC_AQ_FAIL_SET) {
netdev_info(netdev, "Set fc failed on the set_phy_config call with err %pe aq_err %s\n",
ERR_PTR(status),
libie_aq_str(hw->aq.asq_last_status));
err = -EAGAIN;
} if (aq_failures & I40E_SET_FC_AQ_FAIL_UPDATE) {
netdev_info(netdev, "Set fc failed on the get_link_info call with err %pe aq_err %s\n",
ERR_PTR(status),
libie_aq_str(hw->aq.asq_last_status));
err = -EAGAIN;
}
if (!test_bit(__I40E_DOWN, pf->state) && is_an) { /* Give it a little more time to try to come back */
msleep(75); if (!test_bit(__I40E_DOWN, pf->state)) return i40e_nway_reset(netdev);
}
/* if nothing to do return success */ if ((new_tx_count == vsi->tx_rings[0]->count) &&
(new_rx_count == vsi->rx_rings[0]->count)) return0;
/* If there is a AF_XDP page pool attached to any of Rx rings, *disallowchangingthenumberofdescriptors--regardless *ifthenetdevisrunningornot.
*/ if (i40e_xsk_any_rx_ring_enabled(vsi)) return -EBUSY;
while (test_and_set_bit(__I40E_CONFIG_BUSY, pf->state)) {
timeout--; if (!timeout) return -EBUSY;
usleep_range(1000, 2000);
}
if (!netif_running(vsi->netdev)) { /* simple case - set for the next time the netdev is started */ for (i = 0; i < vsi->num_queue_pairs; i++) {
vsi->tx_rings[i]->count = new_tx_count;
vsi->rx_rings[i]->count = new_rx_count; if (i40e_enabled_xdp_vsi(vsi))
vsi->xdp_rings[i]->count = new_tx_count;
}
vsi->num_tx_desc = new_tx_count;
vsi->num_rx_desc = new_rx_count; goto done;
}
/* We can't just free everything and then setup again, *becausetheISRsinMSI-Xmodegetpassedpointers *totheTxandRxringstructs.
*/
/* alloc updated Tx and XDP Tx resources */
tx_alloc_queue_pairs = vsi->alloc_queue_pairs *
(i40e_enabled_xdp_vsi(vsi) ? 2 : 1); if (new_tx_count != vsi->tx_rings[0]->count) {
netdev_info(netdev, "Changing Tx descriptor count from %d to %d.\n",
vsi->tx_rings[0]->count, new_tx_count);
tx_rings = kcalloc(tx_alloc_queue_pairs, sizeof(struct i40e_ring), GFP_KERNEL); if (!tx_rings) {
err = -ENOMEM; goto done;
}
for (i = 0; i < tx_alloc_queue_pairs; i++) { if (!i40e_active_tx_ring_index(vsi, i)) continue;
tx_rings[i] = *vsi->tx_rings[i];
tx_rings[i].count = new_tx_count; /* the desc and bi pointers will be reallocated in the *setupcall
*/
tx_rings[i].desc = NULL;
tx_rings[i].rx_bi = NULL;
err = i40e_setup_tx_descriptors(&tx_rings[i]); if (err) { while (i) {
i--; if (!i40e_active_tx_ring_index(vsi, i)) continue;
i40e_free_tx_resources(&tx_rings[i]);
}
kfree(tx_rings);
tx_rings = NULL;
goto done;
}
}
}
/* alloc updated Rx resources */ if (new_rx_count != vsi->rx_rings[0]->count) {
netdev_info(netdev, "Changing Rx descriptor count from %d to %d\n",
vsi->rx_rings[0]->count, new_rx_count);
rx_rings = kcalloc(vsi->alloc_queue_pairs, sizeof(struct i40e_ring), GFP_KERNEL); if (!rx_rings) {
err = -ENOMEM; goto free_tx;
}
for (i = 0; i < vsi->num_queue_pairs; i++) {
u16 unused;
/* clone ring and setup updated count */
rx_rings[i] = *vsi->rx_rings[i];
rx_rings[i].count = new_rx_count; /* the desc and bi pointers will be reallocated in the *setupcall
*/
rx_rings[i].desc = NULL;
rx_rings[i].rx_bi = NULL; /* Clear cloned XDP RX-queue info before setup call */
memset(&rx_rings[i].xdp_rxq, 0, sizeof(rx_rings[i].xdp_rxq)); /* this is to allow wr32 to have something to write to *duringearlyallocationofRxbuffers
*/
rx_rings[i].tail = hw->hw_addr + I40E_PRTGEN_STATUS;
err = i40e_setup_rx_descriptors(&rx_rings[i]); if (err) goto rx_unwind;
/* now allocate the Rx buffers to make sure the OS *hasenoughmemory,anyfailureheremeansabort
*/
unused = I40E_DESC_UNUSED(&rx_rings[i]);
err = i40e_alloc_rx_buffers(&rx_rings[i], unused);
rx_unwind: if (err) { do {
i40e_free_rx_resources(&rx_rings[i]);
} while (i--);
kfree(rx_rings);
rx_rings = NULL;
goto free_tx;
}
}
}
/* Bring interface down, copy in the new ring info, *thenrestoretheinterface
*/
i40e_down(vsi);
if (tx_rings) { for (i = 0; i < tx_alloc_queue_pairs; i++) { if (i40e_active_tx_ring_index(vsi, i)) {
i40e_free_tx_resources(vsi->tx_rings[i]);
*vsi->tx_rings[i] = tx_rings[i];
}
}
kfree(tx_rings);
tx_rings = NULL;
}
if (rx_rings) { for (i = 0; i < vsi->num_queue_pairs; i++) {
i40e_free_rx_resources(vsi->rx_rings[i]); /* get the real tail offset */
rx_rings[i].tail = vsi->rx_rings[i]->tail; /* this is to fake out the allocation routine *intothinkingithastorealloceverything *buttherecyclinglogicwillletusre-use *thebuffersallocatedabove
*/
rx_rings[i].next_to_use = 0;
rx_rings[i].next_to_clean = 0;
rx_rings[i].next_to_alloc = 0; /* do a struct copy */
*vsi->rx_rings[i] = rx_rings[i];
}
kfree(rx_rings);
rx_rings = NULL;
}
free_tx: /* error cleanup if the Rx allocations failed after getting Tx */ if (tx_rings) { for (i = 0; i < tx_alloc_queue_pairs; i++) { if (i40e_active_tx_ring_index(vsi, i))
i40e_free_tx_resources(vsi->tx_rings[i]);
}
kfree(tx_rings);
tx_rings = NULL;
}
/* The number of stats reported for a given net_device must remain *constantthroughoutthelifeofthatdevice. * *ThisisbecausetheAPIforobtainingthesize,strings,andstats *isspreadoutoverthreeseparateethtoolioctls.Thereisnosafe *waytolockthenumberofstatsacrossthesecalls,sowemust *assumethattheywillneverchange. * *Duetothis,wereportthemaximumnumberofqueues,evenifnot *everyqueueiscurrentlyconfigured.Sincewealwaysallocate *queuesinpairs,we'lljustusenetdev->num_tx_queues*2.This *worksbecausethenum_tx_queuesissetatdevicecreationandnever *changes.
*/
stats_len += I40E_QUEUE_STATS_LEN * 2 * netdev->num_tx_queues;
rcu_read_lock(); for (i = 0; i < netdev->num_tx_queues; i++) {
i40e_add_queue_stats(&data, READ_ONCE(vsi->tx_rings[i]));
i40e_add_queue_stats(&data, READ_ONCE(vsi->rx_rings[i]));
}
rcu_read_unlock();
if (vsi->type != I40E_VSI_MAIN || pf->hw.partition_id != 1) goto check_data_pointer;
veb = i40e_pf_get_main_veb(pf);
veb_stats = veb && test_bit(I40E_FLAG_VEB_STATS_ENA, pf->flags);
if (veb_stats)
i40e_update_veb_stats(veb);
/* If veb stats aren't enabled, pass NULL instead of the veb so that *weinitializestatstozeroandupdatethedatapointer *intelligently
*/
i40e_add_ethtool_stats(&data, veb_stats ? veb : NULL,
i40e_gstrings_veb_stats);
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) if (veb_stats) { struct i40e_cp_veb_tc_stats veb_tc =
i40e_get_veb_tc_stats(&veb->tc_stats, i);
for (i = 0; i < I40E_PRIV_FLAGS_STR_LEN; i++)
ethtool_puts(&p, i40e_gstrings_priv_flags[i].flag_string); if (pf->hw.pf_id != 0) return; for (i = 0; i < I40E_GL_PRIV_FLAGS_STR_LEN; i++)
ethtool_puts(&p, i40e_gl_gstrings_priv_flags[i].flag_string);
}
if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) ||
test_bit(__I40E_RESET_INTR_RECEIVED, pf->state)) {
dev_warn(&pf->pdev->dev, "Cannot start offline testing when PF is in reset state.\n"); goto skip_ol_tests;
}
if (i40e_active_vfs(pf) || i40e_active_vmdqs(pf)) {
dev_warn(&pf->pdev->dev, "Please take active VFs and Netqueues offline and restart the adapter before running NIC diagnostics\n"); goto skip_ol_tests;
}
/* If the device is online then take it offline */ if (if_running) /* indicate we're in test mode */
i40e_close(netdev); else /* This reset does not affect link - if it is *changedtoatypeofresetthatdoesaffect *linkthenthefollowinglinktestwouldhave *tobemovedtobeforethereset
*/
i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED), true);
if (i40e_link_test(netdev, &data[I40E_ETH_TEST_LINK]))
eth_test->flags |= ETH_TEST_FL_FAILED;
if (i40e_eeprom_test(netdev, &data[I40E_ETH_TEST_EEPROM]))
eth_test->flags |= ETH_TEST_FL_FAILED;
if (i40e_intr_test(netdev, &data[I40E_ETH_TEST_INTR]))
eth_test->flags |= ETH_TEST_FL_FAILED;
/* run reg test last, a reset is required after it */ if (i40e_reg_test(netdev, &data[I40E_ETH_TEST_REG]))
eth_test->flags |= ETH_TEST_FL_FAILED;
if (i40e_link_test(netdev, &data[I40E_ETH_TEST_LINK]))
eth_test->flags |= ETH_TEST_FL_FAILED;
/* Offline only tests, not run in online; pass by default */
data[I40E_ETH_TEST_REG] = 0;
data[I40E_ETH_TEST_EEPROM] = 0;
data[I40E_ETH_TEST_INTR] = 0;
}
/* WoL not supported if this isn't the controlling PF on the port */ if (hw->partition_id != 1) {
i40e_partition_setting_complaint(pf); return -EOPNOTSUPP;
}
if (vsi->type != I40E_VSI_MAIN) return -EOPNOTSUPP;
/* NVM bit on means WoL disabled for the port */
i40e_read_nvm_word(hw, I40E_SR_NVM_WAKE_ON_LAN, &wol_nvm_bits); if (BIT(hw->port) & wol_nvm_bits) return -EOPNOTSUPP;
/* only magic packet is supported */ if (wol->wolopts & ~WAKE_MAGIC) return -EOPNOTSUPP;
/* is this a new value? */ if (pf->wol_en != !!wol->wolopts) {
pf->wol_en = !!wol->wolopts;
device_set_wakeup_enable(&pf->pdev->dev, pf->wol_en);
}
switch (state) { case ETHTOOL_ID_ACTIVE: if (!test_bit(I40E_HW_CAP_PHY_CONTROLS_LEDS, pf->hw.caps)) {
pf->led_status = i40e_led_get(hw);
} else { if (!test_bit(I40E_HW_CAP_AQ_PHY_ACCESS, hw->caps))
i40e_aq_set_phy_debug(hw, I40E_PHY_DEBUG_ALL,
NULL);
ret = i40e_led_get_phy(hw, &temp_status,
&pf->phy_led_val);
pf->led_status = temp_status;
} return blink_freq; case ETHTOOL_ID_ON: if (!test_bit(I40E_HW_CAP_PHY_CONTROLS_LEDS, pf->hw.caps))
i40e_led_set(hw, 0xf, false); else
ret = i40e_led_set_phy(hw, true, pf->led_status, 0); break; case ETHTOOL_ID_OFF: if (!test_bit(I40E_HW_CAP_PHY_CONTROLS_LEDS, pf->hw.caps))
i40e_led_set(hw, 0x0, false); else
ret = i40e_led_set_phy(hw, false, pf->led_status, 0); break; case ETHTOOL_ID_INACTIVE: if (!test_bit(I40E_HW_CAP_PHY_CONTROLS_LEDS, pf->hw.caps)) {
i40e_led_set(hw, pf->led_status, false);
} else {
ret = i40e_led_set_phy(hw, false, pf->led_status,
(pf->phy_led_val |
I40E_PHY_LED_MODE_ORIG)); if (!test_bit(I40E_HW_CAP_AQ_PHY_ACCESS, hw->caps))
i40e_aq_set_phy_debug(hw, 0, NULL);
} break; default: break;
} if (ret) return -ENOENT; else return0;
}
/* NOTE: i40e hardware uses a conversion factor of 2 for Interrupt *ThrottleRate(ITR)ie.ITR(1)=2usITR(10)=20us,andalso *125us(8000interruptspersecond)==ITR(62)
*/
/* rx and tx usecs has per queue value. If user doesn't specify the *queue,returnqueue0'svaluetorepresent.
*/ if (queue < 0)
queue = 0; elseif (queue >= vsi->num_queue_pairs) return -EINVAL;
/* we use the _usecs_high to store/set the interrupt rate limit *thatthehardwaresupports,thatalmostbutnotquite *fitstheoriginalintentoftheethtoolvariable, *therx_coalesce_usecs_highlimitstotalinterrupts *persecondfrombothtx/rxsources.
*/
ec->rx_coalesce_usecs_high = vsi->int_rate_limit;
ec->tx_coalesce_usecs_high = vsi->int_rate_limit;
/* The interrupt handler itself will take care of programming *theTxandRxITRvaluesbasedonthevalueswehaveentered *intotheq_vector,noneedtowritethevaluesnow.
*/
/* tx_coalesce_usecs_high is ignored, use rx-usecs-high instead */ if (ec->tx_coalesce_usecs_high != vsi->int_rate_limit) {
netif_info(pf, drv, netdev, "tx-usecs-high is not used, please program rx-usecs-high\n"); return -EINVAL;
}
if (ec->rx_coalesce_usecs_high > INTRL_REG_TO_USEC(I40E_MAX_INTRL)) {
netif_info(pf, drv, netdev, "Invalid value, rx-usecs-high range is 0-%lu\n",
INTRL_REG_TO_USEC(I40E_MAX_INTRL)); return -EINVAL;
}
if (ec->rx_coalesce_usecs != cur_rx_itr &&
ec->use_adaptive_rx_coalesce) {
netif_info(pf, drv, netdev, "RX interrupt moderation cannot be changed if adaptive-rx is enabled.\n"); return -EINVAL;
}
if (ec->rx_coalesce_usecs > I40E_MAX_ITR) {
netif_info(pf, drv, netdev, "Invalid value, rx-usecs range is 0-8160\n"); return -EINVAL;
}
if (ec->tx_coalesce_usecs != cur_tx_itr &&
ec->use_adaptive_tx_coalesce) {
netif_info(pf, drv, netdev, "TX interrupt moderation cannot be changed if adaptive-tx is enabled.\n"); return -EINVAL;
}
if (ec->tx_coalesce_usecs > I40E_MAX_ITR) {
netif_info(pf, drv, netdev, "Invalid value, tx-usecs range is 0-8160\n"); return -EINVAL;
}
if (ec->use_adaptive_rx_coalesce && !cur_rx_itr)
ec->rx_coalesce_usecs = I40E_MIN_ITR;
if (ec->use_adaptive_tx_coalesce && !cur_tx_itr)
ec->tx_coalesce_usecs = I40E_MIN_ITR;
intrl_reg = i40e_intrl_usec_to_reg(ec->rx_coalesce_usecs_high);
vsi->int_rate_limit = INTRL_REG_TO_USEC(intrl_reg); if (vsi->int_rate_limit != ec->rx_coalesce_usecs_high) {
netif_info(pf, drv, netdev, "Interrupt rate limit rounded down to %d\n",
vsi->int_rate_limit);
}
/* rx and tx usecs has per queue value. If user doesn't specify the *queue,applytoallqueues.
*/ if (queue < 0) { for (i = 0; i < vsi->num_queue_pairs; i++)
i40e_set_itr_per_queue(vsi, ec, i);
} else {
i40e_set_itr_per_queue(vsi, ec, queue);
}
switch (cmd->flow_type) { case TCP_V4_FLOW:
flow_pctype = LIBIE_FILTER_PCTYPE_NONF_IPV4_TCP; break; case UDP_V4_FLOW:
flow_pctype = LIBIE_FILTER_PCTYPE_NONF_IPV4_UDP; break; case TCP_V6_FLOW:
flow_pctype = LIBIE_FILTER_PCTYPE_NONF_IPV6_TCP; break; case UDP_V6_FLOW:
flow_pctype = LIBIE_FILTER_PCTYPE_NONF_IPV6_UDP; break; case SCTP_V4_FLOW: case AH_ESP_V4_FLOW: case AH_V4_FLOW: case ESP_V4_FLOW: case IPV4_FLOW: case SCTP_V6_FLOW: case AH_ESP_V6_FLOW: case AH_V6_FLOW: case ESP_V6_FLOW: case IPV6_FLOW: /* Default is src/dest for IP, no matter the L4 hashing */
cmd->data |= RXH_IP_SRC | RXH_IP_DST; break; default: return -EINVAL;
}
/* Read flow based hash input set register */ if (flow_pctype) {
i_set = (u64)i40e_read_rx_ctl(hw, I40E_GLQF_HASH_INSET(0,
flow_pctype)) |
((u64)i40e_read_rx_ctl(hw, I40E_GLQF_HASH_INSET(1,
flow_pctype)) << 32);
}
/* Process bits of hash input set */ if (i_set) { if (i_set & I40E_L4_SRC_MASK)
cmd->data |= RXH_L4_B_0_1; if (i_set & I40E_L4_DST_MASK)
cmd->data |= RXH_L4_B_2_3;
if (cmd->flow_type == TCP_V4_FLOW ||
cmd->flow_type == UDP_V4_FLOW) { if (hw->mac.type == I40E_MAC_X722) { if (i_set & I40E_X722_L3_SRC_MASK)
cmd->data |= RXH_IP_SRC; if (i_set & I40E_X722_L3_DST_MASK)
cmd->data |= RXH_IP_DST;
} else { if (i_set & I40E_L3_SRC_MASK)
cmd->data |= RXH_IP_SRC; if (i_set & I40E_L3_DST_MASK)
cmd->data |= RXH_IP_DST;
}
} elseif (cmd->flow_type == TCP_V6_FLOW ||
cmd->flow_type == UDP_V6_FLOW) { if (i_set & I40E_L3_V6_SRC_MASK)
cmd->data |= RXH_IP_SRC; if (i_set & I40E_L3_V6_DST_MASK)
cmd->data |= RXH_IP_DST;
}
}
if (fsp->flow_type == IPV6_USER_FLOW ||
fsp->flow_type == UDP_V6_FLOW ||
fsp->flow_type == TCP_V6_FLOW ||
fsp->flow_type == SCTP_V6_FLOW) { /* Reverse the src and dest notion, since the HW views them *fromTxperspectivewhereastheuserexpectsitfrom *Rxfilterview.
*/
fsp->h_u.tcp_ip6_spec.psrc = rule->dst_port;
fsp->h_u.tcp_ip6_spec.pdst = rule->src_port;
memcpy(fsp->h_u.tcp_ip6_spec.ip6dst, rule->src_ip6, sizeof(__be32) * 4);
memcpy(fsp->h_u.tcp_ip6_spec.ip6src, rule->dst_ip6, sizeof(__be32) * 4);
} else { /* Reverse the src and dest notion, since the HW views them *fromTxperspectivewhereastheuserexpectsitfrom *Rxfilterview.
*/
fsp->h_u.tcp_ip4_spec.psrc = rule->dst_port;
fsp->h_u.tcp_ip4_spec.pdst = rule->src_port;
fsp->h_u.tcp_ip4_spec.ip4src = rule->dst_ip;
fsp->h_u.tcp_ip4_spec.ip4dst = rule->src_ip;
}
switch (rule->flow_type) { case SCTP_V4_FLOW:
index = LIBIE_FILTER_PCTYPE_NONF_IPV4_SCTP; break; case TCP_V4_FLOW:
index = LIBIE_FILTER_PCTYPE_NONF_IPV4_TCP; break; case UDP_V4_FLOW:
index = LIBIE_FILTER_PCTYPE_NONF_IPV4_UDP; break; case SCTP_V6_FLOW:
index = LIBIE_FILTER_PCTYPE_NONF_IPV6_SCTP; break; case TCP_V6_FLOW:
index = LIBIE_FILTER_PCTYPE_NONF_IPV6_TCP; break; case UDP_V6_FLOW:
index = LIBIE_FILTER_PCTYPE_NONF_IPV6_UDP; break; case IP_USER_FLOW:
index = LIBIE_FILTER_PCTYPE_NONF_IPV4_OTHER; break; case IPV6_USER_FLOW:
index = LIBIE_FILTER_PCTYPE_NONF_IPV6_OTHER; break; default: /* If we have stored a filter with a flow type not listed here *itisalmostcertainlyadriverbug.WARN(),andthen *assigntheinput_setasifallfieldsareenabledtoavoid *readingunassignedmemory.
*/
WARN(1, "Missing input set index for flow_type %d\n",
rule->flow_type);
input_set = 0xFFFFFFFFFFFFFFFFULL; goto no_input_set;
}
vsi = i40e_pf_get_main_vsi(pf); if (rule->dest_vsi != vsi->id) {
vsi = i40e_find_vsi_from_id(pf, rule->dest_vsi); if (vsi && vsi->type == I40E_VSI_SRIOV) { /* VFs are zero-indexed by the driver, but ethtool *expectsthemtobeone-indexed,soaddonehere
*/
u64 ring_vf = vsi->vf_id + 1;
if (test_bit(I40E_FLAG_MFP_ENA, pf->flags)) {
dev_err(&pf->pdev->dev, "Change of RSS hash input set is not supported when MFP mode is enabled\n"); return -EOPNOTSUPP;
}
/* RSS does not support anything other than hashing *toqueuesonsrcanddstIPsandports
*/ if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST |
RXH_L4_B_0_1 | RXH_L4_B_2_3)) return -EINVAL;
switch (nfc->flow_type) { case TCP_V4_FLOW:
set_bit(LIBIE_FILTER_PCTYPE_NONF_IPV4_TCP, flow_pctypes); if (test_bit(I40E_HW_CAP_MULTI_TCP_UDP_RSS_PCTYPE,
pf->hw.caps))
set_bit(LIBIE_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK,
flow_pctypes); break; case TCP_V6_FLOW:
set_bit(LIBIE_FILTER_PCTYPE_NONF_IPV6_TCP, flow_pctypes); if (test_bit(I40E_HW_CAP_MULTI_TCP_UDP_RSS_PCTYPE,
pf->hw.caps))
set_bit(LIBIE_FILTER_PCTYPE_NONF_IPV6_TCP_SYN_NO_ACK,
flow_pctypes); break; case UDP_V4_FLOW:
set_bit(LIBIE_FILTER_PCTYPE_NONF_IPV4_UDP, flow_pctypes); if (test_bit(I40E_HW_CAP_MULTI_TCP_UDP_RSS_PCTYPE,
pf->hw.caps)) {
set_bit(LIBIE_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP,
flow_pctypes);
set_bit(LIBIE_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP,
flow_pctypes);
}
hena |= BIT_ULL(LIBIE_FILTER_PCTYPE_FRAG_IPV4); break; case UDP_V6_FLOW:
set_bit(LIBIE_FILTER_PCTYPE_NONF_IPV6_UDP, flow_pctypes); if (test_bit(I40E_HW_CAP_MULTI_TCP_UDP_RSS_PCTYPE,
pf->hw.caps)) {
set_bit(LIBIE_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP,
flow_pctypes);
set_bit(LIBIE_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP,
flow_pctypes);
}
hena |= BIT_ULL(LIBIE_FILTER_PCTYPE_FRAG_IPV6); break; case AH_ESP_V4_FLOW: case AH_V4_FLOW: case ESP_V4_FLOW: case SCTP_V4_FLOW: if ((nfc->data & RXH_L4_B_0_1) ||
(nfc->data & RXH_L4_B_2_3)) return -EINVAL;
hena |= BIT_ULL(LIBIE_FILTER_PCTYPE_NONF_IPV4_OTHER); break; case AH_ESP_V6_FLOW: case AH_V6_FLOW: case ESP_V6_FLOW: case SCTP_V6_FLOW: if ((nfc->data & RXH_L4_B_0_1) ||
(nfc->data & RXH_L4_B_2_3)) return -EINVAL;
hena |= BIT_ULL(LIBIE_FILTER_PCTYPE_NONF_IPV6_OTHER); break; case IPV4_FLOW:
hena |= BIT_ULL(LIBIE_FILTER_PCTYPE_NONF_IPV4_OTHER) |
BIT_ULL(LIBIE_FILTER_PCTYPE_FRAG_IPV4); break; case IPV6_FLOW:
hena |= BIT_ULL(LIBIE_FILTER_PCTYPE_NONF_IPV6_OTHER) |
BIT_ULL(LIBIE_FILTER_PCTYPE_FRAG_IPV6); break; default: return -EINVAL;
}
if (bitmap_weight(flow_pctypes, FLOW_PCTYPES_SIZE)) {
u8 flow_id;
hlist_for_each_entry_safe(rule, node2,
&pf->fdir_filter_list, fdir_node) { /* hash found, or no matching entry */ if (rule->fd_id >= sw_idx) break;
parent = rule;
}
/* if there is an old rule occupying our place remove it */ if (rule && (rule->fd_id == sw_idx)) { /* Remove this rule, since we're either deleting it, or *replacingit.
*/
err = i40e_add_del_fdir(vsi, rule, false);
hlist_del(&rule->fdir_node);
kfree(rule);
pf->fdir_pf_active_filters--;
}
/* If we weren't given an input, this is a delete, so just return the *errorcodeindicatingiftherewasanentryattherequestedslot
*/ if (!input) return err;
/* Otherwise, install the new rule as requested */
INIT_HLIST_NODE(&input->fdir_node);
/* add filter to the list */ if (parent)
hlist_add_behind(&input->fdir_node, &parent->fdir_node); else
hlist_add_head(&input->fdir_node,
&pf->fdir_filter_list);
/* First, we'll check the l3 table */
list_for_each_entry_safe(entry, tmp, &pf->l3_flex_pit_list, list) { bool found = false;
hlist_for_each_entry(rule, &pf->fdir_filter_list, fdir_node) { if (rule->flow_type != IP_USER_FLOW) continue; if (rule->flex_filter &&
rule->flex_offset == entry->src_offset) {
found = true; break;
}
}
/* If we didn't find the filter, then we can prune this entry *fromthelist.
*/ if (!found) {
list_del(&entry->list);
kfree(entry);
}
}
/* Followed by the L4 table */
list_for_each_entry_safe(entry, tmp, &pf->l4_flex_pit_list, list) { bool found = false;
hlist_for_each_entry(rule, &pf->fdir_filter_list, fdir_node) { /* Skip this filter if it's L3, since we already *checkedthoseintheaboveloop
*/ if (rule->flow_type == IP_USER_FLOW) continue; if (rule->flex_filter &&
rule->flex_offset == entry->src_offset) {
found = true; break;
}
}
/* If we didn't find the filter, then we can prune this entry *fromthelist.
*/ if (!found) {
list_del(&entry->list);
kfree(entry);
}
}
}
/* Search for the src_offset first. If we find a matching entry *alreadyprogrammed,wecansimplyre-useit.
*/
list_for_each_entry(entry, flex_pit_list, list) {
size++; if (entry->src_offset == src_offset) return entry;
}
/* If we haven't found an entry yet, then the provided src offset has *notyetbeenprogrammed.Wewillprogramthesrcoffsetlateron, *butweneedtoindicatewhetherthereisenoughspacetodoso *here.We'llmakeuseofERR_PTRforthispurpose.
*/ if (size >= I40E_FLEX_PIT_TABLE_SIZE) return ERR_PTR(-ENOSPC);
/* We need to insert this item such that the list is sorted by *src_offsetinascendingorder.
*/
list_for_each_entry(entry, flex_pit_list, list) { if (new_pit->src_offset < entry->src_offset) {
list_add_tail(&new_pit->list, &entry->list); return0;
}
/* If we found an entry with our offset already programmed we *cansimplyreturnhere,afterfreeingthememory.However, *ifthepit_indexdoesnotmatchweneedtoreportanerror.
*/ if (new_pit->src_offset == entry->src_offset) { int err = 0;
/* If the PIT index is not the same we can't re-use *theentry,sowemustreportanerror.
*/ if (new_pit->pit_index != entry->pit_index)
err = -EINVAL;
kfree(new_pit); return err;
}
}
/* If we reached here, then we haven't yet added the item. This means *thatweshouldaddtheitemattheendofthelist.
*/
list_add_tail(&new_pit->list, flex_pit_list); return0;
}
/* First, loop over the list of flex PIT entries, and reprogram the *registers.
*/
list_for_each_entry(entry, flex_pit_list, list) { /* We have to be careful when programming values for the *largestSRC_OFFSETvalue.Itispossiblethatadding *additionalemptyvaluesattheendwouldoverflowthespace *fortheSRC_OFFSETintheFLX_PITregister.Toavoidthis, *wecheckhereandaddtheemptyvaluespriortoaddingthe *largestvalue. * *Todeterminethis,wewillusealoopfromi+1to3,which *willdeterminewhethertheunusedentrieswouldhavevalid *SRC_OFFSET.Notethattherecannotbeextraentriespast *thisvalue,becausetheonlyvalidvalueswouldhavebeen *largerthanI40E_MAX_FLEX_SRC_OFFSET,andthuswouldnot *havebeenaddedtothelistinthefirstplace.
*/ for (j = i + 1; j < 3; j++) {
u16 offset = entry->src_offset + j; int index = flex_pit_start + i;
u32 value = I40E_FLEX_PREP_VAL(I40E_FLEX_DEST_UNUSED, 1,
offset - 3);
/* Now, we can program the actual value into the table */
i40e_write_rx_ctl(&pf->hw,
I40E_PRTQF_FLX_PIT(flex_pit_start + i),
I40E_FLEX_PREP_VAL(entry->pit_index + 50, 1,
entry->src_offset));
i++;
}
/* In order to program the last entries in the table, we need to *determinethevalidoffset.Ifthelistisempty,we'lljuststart *with0.Otherwise,we'llstartwiththelastitemoffsetandadd1. *Thisensuresthatallentrieshavevalidsizes.Ifwedon'tdothis *correctly,thehardwarewilldisableflexiblefieldparsing.
*/ if (!list_empty(flex_pit_list))
last_offset = list_prev_entry(entry, list)->src_offset + 1;
for (; i < 3; i++, last_offset++) {
i40e_write_rx_ctl(&pf->hw,
I40E_PRTQF_FLX_PIT(flex_pit_start + i),
I40E_FLEX_PREP_VAL(I40E_FLEX_DEST_UNUSED, 1,
last_offset));
}
}
/* We also need to program the L3 and L4 GLQF ORT register */
i40e_write_rx_ctl(&pf->hw,
I40E_GLQF_ORT(I40E_L3_GLQF_ORT_IDX),
I40E_ORT_PREP_VAL(I40E_FLEX_PIT_IDX_START_L3, 3, 1));
switch (fsp->flow_type & ~FLOW_EXT) { case SCTP_V4_FLOW:
index = LIBIE_FILTER_PCTYPE_NONF_IPV4_SCTP;
fdir_filter_count = &pf->fd_sctp4_filter_cnt; break; case TCP_V4_FLOW:
index = LIBIE_FILTER_PCTYPE_NONF_IPV4_TCP;
fdir_filter_count = &pf->fd_tcp4_filter_cnt; break; case UDP_V4_FLOW:
index = LIBIE_FILTER_PCTYPE_NONF_IPV4_UDP;
fdir_filter_count = &pf->fd_udp4_filter_cnt; break; case SCTP_V6_FLOW:
index = LIBIE_FILTER_PCTYPE_NONF_IPV6_SCTP;
fdir_filter_count = &pf->fd_sctp6_filter_cnt; break; case TCP_V6_FLOW:
index = LIBIE_FILTER_PCTYPE_NONF_IPV6_TCP;
fdir_filter_count = &pf->fd_tcp6_filter_cnt; break; case UDP_V6_FLOW:
index = LIBIE_FILTER_PCTYPE_NONF_IPV6_UDP;
fdir_filter_count = &pf->fd_udp6_filter_cnt; break; case IP_USER_FLOW:
index = LIBIE_FILTER_PCTYPE_NONF_IPV4_OTHER;
fdir_filter_count = &pf->fd_ip4_filter_cnt;
flex_l3 = true; break; case IPV6_USER_FLOW:
index = LIBIE_FILTER_PCTYPE_NONF_IPV6_OTHER;
fdir_filter_count = &pf->fd_ip6_filter_cnt;
flex_l3 = true; break; default: return -EOPNOTSUPP;
}
/* Read the current input set from register memory. */
current_mask = i40e_read_fd_input_set(pf, index);
new_mask = current_mask;
/* Determine, if any, the required changes to the input set in order *tosupporttheprovidedmask. * *Hardwareonlysupportsmaskingatword(2byte)granularityanddoes *notsupportfullbitwisemasking.Thisimplementationsimplifies *evenfurtherandonlysupportsfullyenabledorfullydisabled *masksforeachfield,eventhoughwecouldsplittheip4srcand *ip4dstfields.
*/ switch (fsp->flow_type & ~FLOW_EXT) { case SCTP_V4_FLOW:
new_mask &= ~I40E_VERIFY_TAG_MASK;
fallthrough; case TCP_V4_FLOW: case UDP_V4_FLOW:
tcp_ip4_spec = &fsp->m_u.tcp_ip4_spec;
/* L4 source port */ if (tcp_ip4_spec->psrc == htons(0xFFFF))
new_mask |= I40E_L4_SRC_MASK; elseif (!tcp_ip4_spec->psrc)
new_mask &= ~I40E_L4_SRC_MASK; else return -EOPNOTSUPP;
/* L4 destination port */ if (tcp_ip4_spec->pdst == htons(0xFFFF))
new_mask |= I40E_L4_DST_MASK; elseif (!tcp_ip4_spec->pdst)
new_mask &= ~I40E_L4_DST_MASK; else return -EOPNOTSUPP;
/* Filtering on Type of Service is not supported. */ if (tcp_ip4_spec->tos) return -EOPNOTSUPP;
break; case SCTP_V6_FLOW:
new_mask &= ~I40E_VERIFY_TAG_MASK;
fallthrough; case TCP_V6_FLOW: case UDP_V6_FLOW:
tcp_ip6_spec = &fsp->m_u.tcp_ip6_spec;
/* Check if user provided IPv6 source address. */ if (ipv6_addr_equal((struct in6_addr *)&tcp_ip6_spec->ip6src,
(struct in6_addr *)&ipv6_full_mask))
new_mask |= I40E_L3_V6_SRC_MASK; elseif (ipv6_addr_any((struct in6_addr *)
&tcp_ip6_spec->ip6src))
new_mask &= ~I40E_L3_V6_SRC_MASK; else return -EOPNOTSUPP;
/* Check if user provided destination address. */ if (ipv6_addr_equal((struct in6_addr *)&tcp_ip6_spec->ip6dst,
(struct in6_addr *)&ipv6_full_mask))
new_mask |= I40E_L3_V6_DST_MASK; elseif (ipv6_addr_any((struct in6_addr *)
&tcp_ip6_spec->ip6dst))
new_mask &= ~I40E_L3_V6_DST_MASK; else return -EOPNOTSUPP;
/* L4 source port */ if (tcp_ip6_spec->psrc == htons(0xFFFF))
new_mask |= I40E_L4_SRC_MASK; elseif (!tcp_ip6_spec->psrc)
new_mask &= ~I40E_L4_SRC_MASK; else return -EOPNOTSUPP;
/* L4 destination port */ if (tcp_ip6_spec->pdst == htons(0xFFFF))
new_mask |= I40E_L4_DST_MASK; elseif (!tcp_ip6_spec->pdst)
new_mask &= ~I40E_L4_DST_MASK; else return -EOPNOTSUPP;
/* Filtering on Traffic Classes is not supported. */ if (tcp_ip6_spec->tclass) return -EOPNOTSUPP; break; case IP_USER_FLOW:
usr_ip4_spec = &fsp->m_u.usr_ip4_spec;
/* If we have a flexible filter, try to add this offset to the correct *flexiblefilterPITlist.Oncefinished,wecanupdatethemask. *Ifthesrc_offsetchanged,wewillgetanewmaskvaluewhichwill *triggeraninputsetchange.
*/ if (userdef->flex_filter) { struct i40e_flex_pit *l3_flex_pit = NULL, *flex_pit = NULL;
/* Flexible offset must be even, since the flexible payload *mustbealignedon2-byteboundary.
*/ if (userdef->flex_offset & 0x1) {
dev_warn(&pf->pdev->dev, "Flexible data offset must be 2-byte aligned\n"); return -EINVAL;
}
src_offset = userdef->flex_offset >> 1;
/* FLX_PIT source offset value is only so large */ if (src_offset > I40E_MAX_FLEX_SRC_OFFSET) {
dev_warn(&pf->pdev->dev, "Flexible data must reside within first 64 bytes of the packet payload\n"); return -EINVAL;
}
/* See if this offset has already been programmed. If we get *anERR_PTR,thenthefilterisnotsafetoadd.Otherwise, *ifwegetaNULLpointer,thismeanswewillneedtoadd *theoffset.
*/
flex_pit = i40e_find_flex_offset(&pf->l4_flex_pit_list,
src_offset); if (IS_ERR(flex_pit)) return PTR_ERR(flex_pit);
/* IP_USER_FLOW filters match both L4 (ICMP) and L3 (unknown) *packettypes,andthusweneedtoprogrambothL3andL4 *flexiblevalues.Thesemusthaveidenticalflexibleindex, *asotherwisewecan'tcorrectlyprogramtheinputset.So *we'llfindbothanL3andL4indexandmakesuretheyare *thesame.
*/ if (flex_l3) {
l3_flex_pit =
i40e_find_flex_offset(&pf->l3_flex_pit_list,
src_offset); if (IS_ERR(l3_flex_pit)) return PTR_ERR(l3_flex_pit);
if (flex_pit) { /* If we already had a matching L4 entry, we *needtomakesurethattheL3entrywe *obtainedusesthesameindex.
*/ if (l3_flex_pit) { if (l3_flex_pit->pit_index !=
flex_pit->pit_index) { return -EINVAL;
}
} else {
new_flex_offset = true;
}
} else {
flex_pit = l3_flex_pit;
}
}
/* If we didn't find an existing flex offset, we need to *programanewone.However,wedon'timmediatelyprogramit *herebecausewewillwaittoprogramuntilafterwecheck *thatitissafetochangetheinputset.
*/ if (!flex_pit) {
new_flex_offset = true;
pit_index = i40e_unused_pit_index(pf);
} else {
pit_index = flex_pit->pit_index;
}
/* Update the mask with the new offset */
new_mask |= i40e_pit_index_to_mask(pit_index);
}
/* If the mask and flexible filter offsets for this filter match the *currentlyprogrammedvalueswedon'tneedanyinputsetchange,so *thisfilterissafetoinstall.
*/ if (new_mask == current_mask && !new_flex_offset) return0;
netif_info(pf, drv, vsi->netdev, "Input set change requested for %s flows:\n",
i40e_flow_str(fsp));
i40e_print_input_set(vsi, current_mask, new_mask); if (new_flex_offset) {
netif_info(pf, drv, vsi->netdev, "FLEX index %d: Offset -> %d",
pit_index, src_offset);
}
/* Hardware input sets are global across multiple ports, so even the *mainportcannotchangethemwheninMFPmodeasthiswouldimpact *anyfiltersontheotherports.
*/ if (test_bit(I40E_FLAG_MFP_ENA, pf->flags)) {
netif_err(pf, drv, vsi->netdev, "Cannot change Flow Director input sets while MFP is enabled\n"); return -EOPNOTSUPP;
}
/* This filter requires us to update the input set. However, hardware *onlysupportsoneinputsetperflowtype,anddoesnotsupport *separatemasksforeachfilter.Thismeansthatwecanonlysupport *asinglemaskforallfiltersofaspecifictype. * *Ifwehavepreexistingfilters,theyobviouslydependonthe *currentprogrammedinputset.Displayadiagnosticmessageinthis *caseexplainingwhythefiltercouldnotbeaccepted.
*/ if (*fdir_filter_count) {
netif_err(pf, drv, vsi->netdev, "Cannot change input set for %s flows until %d preexisting filters are removed\n",
i40e_flow_str(fsp),
*fdir_filter_count); return -EOPNOTSUPP;
}
i40e_write_fd_input_set(pf, index, new_mask);
/* IP_USER_FLOW filters match both IPv4/Other and IPv4/Fragmented *frames.Ifwe'reprogrammingtheinputsetforIPv4/Other,wealso *needtoprogramtheIPv4/Fragmentedinputset.Sincewedon'thave *separatesupport,we'llalwaysassumeandenforcethatthetwoflow *typesmusthavematchinginputsets.
*/ if (index == LIBIE_FILTER_PCTYPE_NONF_IPV4_OTHER)
i40e_write_fd_input_set(pf, LIBIE_FILTER_PCTYPE_FRAG_IPV4,
new_mask);
/* Add the new offset and update table, if necessary */ if (new_flex_offset) {
err = i40e_add_flex_offset(&pf->l4_flex_pit_list, src_offset,
pit_index); if (err) return err;
if (flex_l3) {
err = i40e_add_flex_offset(&pf->l3_flex_pit_list,
src_offset,
pit_index); if (err) return err;
}
i40e_reprogram_flex_pit(pf);
}
return0;
}
/** *i40e_match_fdir_filter-Returntrueoftwofiltersmatch *@a:pointertofilterstruct *@b:pointertofilterstruct * *Returnstrueifthetwofiltersmatchexactlythesamecriteria.I.e.they *matchthesameflowtypeandhavethesameparameters.Wedon'tneedto *checkanyinput-setsinceallfiltersofthesameflowtypemustusethe *sameinputset.
**/ staticbool i40e_match_fdir_filter(struct i40e_fdir_filter *a, struct i40e_fdir_filter *b)
{ /* The filters do not much if any of these criteria differ. */ if (a->dst_ip != b->dst_ip ||
a->src_ip != b->src_ip ||
a->dst_port != b->dst_port ||
a->src_port != b->src_port ||
a->flow_type != b->flow_type ||
a->ipl4_proto != b->ipl4_proto ||
a->vlan_tag != b->vlan_tag ||
a->vlan_etype != b->vlan_etype) returnfalse;
/* Loop through every filter, and check that it doesn't match */
hlist_for_each_entry_safe(rule, node2,
&pf->fdir_filter_list, fdir_node) { /* Don't check the filters match if they share the same fd_id, *sincethenewfilterisactuallyjustupdatingthetarget *oftheoldfilter.
*/ if (rule->fd_id == input->fd_id) continue;
/* If any filters match, then print a warning message to the *kernelmessagebufferandbailout.
*/ if (i40e_match_fdir_filter(rule, input)) {
dev_warn(&pf->pdev->dev, "Existing user defined filter %d already matches this flow.\n",
rule->fd_id); return -EINVAL;
}
}
if (!test_bit(I40E_FLAG_FD_SB_ENA, pf->flags)) return -EOPNOTSUPP;
if (test_bit(__I40E_FD_SB_AUTO_DISABLED, pf->state)) return -ENOSPC;
if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) ||
test_bit(__I40E_RESET_INTR_RECEIVED, pf->state)) return -EBUSY;
if (test_bit(__I40E_FD_FLUSH_REQUESTED, pf->state)) return -EBUSY;
fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
/* Parse the user-defined field */ if (i40e_parse_rx_flow_user_data(fsp, &userdef)) return -EINVAL;
/* Extended MAC field is not supported */ if (fsp->flow_type & FLOW_MAC_EXT) return -EINVAL;
ret = i40e_check_fdir_input_set(vsi, fsp, &userdef); if (ret) return ret;
if (fsp->location >= (pf->hw.func_caps.fd_filters_best_effort +
pf->hw.func_caps.fd_filters_guaranteed)) { return -EINVAL;
}
/* ring_cookie is either the drop index, or is a mask of the queue *indexandVFidwewishtotarget.
*/ if (fsp->ring_cookie == RX_CLS_FLOW_DISC) {
dest_ctl = I40E_FILTER_PROGRAM_DESC_DEST_DROP_PACKET;
} else {
u32 ring = ethtool_get_flow_spec_ring(fsp->ring_cookie);
u8 vf = ethtool_get_flow_spec_ring_vf(fsp->ring_cookie);
if (!vf) { if (ring >= vsi->num_queue_pairs) return -EINVAL;
dest_vsi = vsi->id;
} else { /* VFs are zero-indexed, so we subtract one here */
vf--;
switch (cmd->cmd) { case ETHTOOL_SRXCLSRLINS:
ret = i40e_add_fdir_ethtool(vsi, cmd); break; case ETHTOOL_SRXCLSRLDEL:
ret = i40e_del_fdir_entry(vsi, cmd); break; default: break;
}
return ret;
}
/** *i40e_max_channels-getMaxnumberofcombinedchannelssupported *@vsi:vsipointer
**/ staticunsignedint i40e_max_channels(struct i40e_vsi *vsi)
{ /* TODO: This code assumes DCB and FD is disabled for now. */ return vsi->alloc_queue_pairs;
}
/* We do not support setting channels for any other VSI at present */ if (vsi->type != I40E_VSI_MAIN) return -EINVAL;
/* We do not support setting channels via ethtool when TCs are *configuredthroughmqprio
*/ if (i40e_is_tc_mqprio_enabled(pf)) return -EINVAL;
/* verify they are not requesting separate vectors */ if (!count || ch->rx_count || ch->tx_count) return -EINVAL;
/* verify other_count has not changed */ if (ch->other_count != (test_bit(I40E_FLAG_FD_SB_ENA, pf->flags) ? 1 : 0)) return -EINVAL;
/* verify the number of channels does not exceed hardware limits */ if (count > i40e_max_channels(vsi)) return -EINVAL;
/* verify that the number of channels does not invalidate any current *flowdirectorrules
*/
hlist_for_each_entry_safe(rule, node2,
&pf->fdir_filter_list, fdir_node) { if (rule->dest_ctl != drop && count <= rule->q_index) {
dev_warn(&pf->pdev->dev, "Existing user defined filter %d assigns flow to queue %d\n",
rule->fd_id, rule->q_index);
err = -EINVAL;
}
}
if (err) {
dev_err(&pf->pdev->dev, "Existing filter rules must be deleted to reduce combined channel count to %d\n",
count); return err;
}
/* update feature limits from largest to smallest supported values */ /* TODO: Flow director limit, DCB etc */
/* use rss_reconfig to rebuild with new queue count and update traffic *classqueuemapping
*/
new_count = i40e_reconfig_rss_queues(pf, count); if (new_count > 0) return0; else return -EINVAL;
}
if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE &&
rxfh->hfunc != ETH_RSS_HASH_TOP) return -EOPNOTSUPP;
if (rxfh->key) { if (!vsi->rss_hkey_user) {
vsi->rss_hkey_user = kzalloc(I40E_HKEY_ARRAY_SIZE,
GFP_KERNEL); if (!vsi->rss_hkey_user) return -ENOMEM;
}
memcpy(vsi->rss_hkey_user, rxfh->key, I40E_HKEY_ARRAY_SIZE);
seed = vsi->rss_hkey_user;
} if (!vsi->rss_lut_user) {
vsi->rss_lut_user = kzalloc(I40E_HLUT_ARRAY_SIZE, GFP_KERNEL); if (!vsi->rss_lut_user) return -ENOMEM;
}
/* Each 32 bits pointed by 'indir' is stored with a lut entry */ if (rxfh->indir) for (i = 0; i < I40E_HLUT_ARRAY_SIZE; i++)
vsi->rss_lut_user[i] = (u8)(rxfh->indir[i]); else
i40e_fill_rss_lut(pf, vsi->rss_lut_user, I40E_HLUT_ARRAY_SIZE,
vsi->rss_size);
/* If this is a read-only flag, it can't be changed */ if (priv_flag->read_only &&
test_bit(priv_flag->bitno, orig_flags) != new_val) return -EOPNOTSUPP;
if (new_val)
set_bit(priv_flag->bitno, new_flags); else
clear_bit(priv_flag->bitno, new_flags);
}
/* If this is a read-only flag, it can't be changed */ if (priv_flag->read_only &&
test_bit(priv_flag->bitno, orig_flags) != new_val) return -EOPNOTSUPP;
if (new_val)
set_bit(priv_flag->bitno, new_flags); else
clear_bit(priv_flag->bitno, new_flags);
}
/* Before we finalize any flag changes, we need to perform some *checkstoensurethatthechangesaresupportedandsafe.
*/
/* ATR eviction is not supported on all devices */ if (test_bit(I40E_FLAG_HW_ATR_EVICT_ENA, new_flags) &&
!test_bit(I40E_HW_CAP_ATR_EVICT, pf->hw.caps)) return -EOPNOTSUPP;
/* If the driver detected FW LLDP was disabled on init, this flag could *beset,howeverwedonotsupport_changing_theflag: *-onXL710ifNPARisenabledorFWAPIversion<1.7 *-onX722withFWAPIversion<1.6 *TherearesituationswhereolderFWversions/NPARenabledPFscould *disableLLDP,howeverwe_must_notallowtheusertoenable/disable *LLDPwiththisflagonunsupportedFWversions.
*/ if (test_bit(I40E_FLAG_FW_LLDP_DIS, changed_flags) &&
!test_bit(I40E_HW_CAP_FW_LLDP_STOPPABLE, pf->hw.caps)) {
dev_warn(&pf->pdev->dev, "Device does not support changing FW LLDP\n"); return -EOPNOTSUPP;
}
if (test_bit(I40E_FLAG_RS_FEC, changed_flags) &&
pf->hw.device_id != I40E_DEV_ID_25G_SFP28 &&
pf->hw.device_id != I40E_DEV_ID_25G_B) {
dev_warn(&pf->pdev->dev, "Device does not support changing FEC configuration\n"); return -EOPNOTSUPP;
}
if (test_bit(I40E_FLAG_BASE_R_FEC, changed_flags) &&
pf->hw.device_id != I40E_DEV_ID_25G_SFP28 &&
pf->hw.device_id != I40E_DEV_ID_25G_B &&
pf->hw.device_id != I40E_DEV_ID_KX_X722) {
dev_warn(&pf->pdev->dev, "Device does not support changing FEC configuration\n"); return -EOPNOTSUPP;
}
/* Process any additional changes needed as a result of flag changes. *Thechanged_flagsvaluereflectsthelistofbitsthatwere *changedinthecodeabove.
*/
/* Flush current ATR settings if ATR was disabled */ if (test_bit(I40E_FLAG_FD_ATR_ENA, changed_flags) &&
!test_bit(I40E_FLAG_FD_ATR_ENA, new_flags)) {
set_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state);
set_bit(__I40E_FD_FLUSH_REQUESTED, pf->state);
}
if (test_bit(I40E_FLAG_TRUE_PROMISC_ENA, changed_flags)) {
u16 sw_flags = 0, valid_flags = 0; int ret;
if (!test_bit(I40E_FLAG_TRUE_PROMISC_ENA, new_flags))
sw_flags = I40E_AQ_SET_SWITCH_CFG_PROMISC;
valid_flags = I40E_AQ_SET_SWITCH_CFG_PROMISC;
ret = i40e_aq_set_switch_config(&pf->hw, sw_flags, valid_flags, 0, NULL); if (ret && pf->hw.aq.asq_last_status != LIBIE_AQ_RC_ESRCH) {
dev_info(&pf->pdev->dev, "couldn't set switch config bits, err %pe aq_err %s\n",
ERR_PTR(ret),
libie_aq_str(pf->hw.aq.asq_last_status)); /* not a fatal problem, just keep going */
}
}
if (test_bit(I40E_FLAG_LINK_DOWN_ON_CLOSE_ENA, changed_flags) &&
test_bit(I40E_FLAG_TOTAL_PORT_SHUTDOWN_ENA, orig_flags)) {
dev_err(&pf->pdev->dev, "Setting link-down-on-close not supported on this port (because total-port-shutdown is enabled)\n"); return -EOPNOTSUPP;
}
if (test_bit(I40E_FLAG_VF_VLAN_PRUNING_ENA, changed_flags) &&
pf->num_alloc_vfs) {
dev_warn(&pf->pdev->dev, "Changing vf-vlan-pruning flag while VF(s) are active is not supported\n"); return -EOPNOTSUPP;
}
if (test_bit(I40E_FLAG_LEGACY_RX_ENA, changed_flags) &&
I40E_2K_TOO_SMALL_WITH_PADDING) {
dev_warn(&pf->pdev->dev, "2k Rx buffer is too small to fit standard MTU and skb_shared_info\n"); return -EOPNOTSUPP;
}
if (test_bit(I40E_FLAG_LINK_DOWN_ON_CLOSE_ENA, changed_flags) &&
test_bit(I40E_FLAG_LINK_DOWN_ON_CLOSE_ENA, new_flags) &&
test_bit(I40E_FLAG_MFP_ENA, new_flags))
dev_warn(&pf->pdev->dev, "Turning on link-down-on-close flag may affect other partitions\n");
if (test_bit(I40E_FLAG_FW_LLDP_DIS, changed_flags)) { if (test_bit(I40E_FLAG_FW_LLDP_DIS, new_flags)) { #ifdef CONFIG_I40E_DCB
i40e_dcb_sw_default_config(pf); #endif/* CONFIG_I40E_DCB */
i40e_aq_cfg_lldp_mib_change_event(&pf->hw, false, NULL);
i40e_aq_stop_lldp(&pf->hw, true, false, NULL);
} else {
status = i40e_aq_start_lldp(&pf->hw, false, NULL); if (status) {
adq_err = pf->hw.aq.asq_last_status; switch (adq_err) { case LIBIE_AQ_RC_EEXIST:
dev_warn(&pf->pdev->dev, "FW LLDP agent is already running\n");
reset_needed = 0; break; case LIBIE_AQ_RC_EPERM:
dev_warn(&pf->pdev->dev, "Device configuration forbids SW from starting the LLDP agent.\n"); return -EINVAL; case LIBIE_AQ_RC_EAGAIN:
dev_warn(&pf->pdev->dev, "Stop FW LLDP agent command is still being processed, please try again in a second.\n"); return -EBUSY; default:
dev_warn(&pf->pdev->dev, "Starting FW LLDP agent failed: error: %pe, %s\n",
ERR_PTR(status),
libie_aq_str(adq_err)); return -EINVAL;
}
}
}
}
/* Now that we've checked to ensure that the new flags are valid, load *themintoplace.Sinceweonlymodifyflagseither(a)during *initializationor(b)whileholdingtheRTNLlock,wedon'tneed *anythingfancyhere.
*/
bitmap_copy(pf->flags, new_flags, I40E_PF_FLAGS_NBITS);
/* Issue reset to cause things to take effect, as additional bits *areaddedwewillneedtocreateamaskofbitsrequiringreset
*/ if (reset_needed)
i40e_do_reset(pf, reset_needed, true);
if (hw->phy.link_info.module_type[0] == I40E_MODULE_TYPE_SFP)
is_sfp = true;
for (i = 0; i < ee->len; i++) {
u32 offset = i + ee->offset;
u32 addr = is_sfp ? I40E_I2C_EEPROM_DEV_ADDR : 0;
/* Check if we need to access the other memory page */ if (is_sfp) { if (offset >= ETH_MODULE_SFF_8079_LEN) {
offset -= ETH_MODULE_SFF_8079_LEN;
addr = I40E_I2C_EEPROM_DEV_ADDR2;
}
} else { while (offset >= ETH_MODULE_SFF_8436_LEN) { /* Compute memory page number and offset. */
offset -= ETH_MODULE_SFF_8436_LEN / 2;
addr++;
}
}
status = i40e_aq_get_phy_register(hw,
I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE,
addr, true, offset, &value, NULL); if (status) return -EIO;
data[i] = value;
} return0;
}
for (i = 0; i < ARRAY_SIZE(param); i++) { if (param[i].value) {
netdev_info(netdev, "EEE setting %s not supported\n",
param[i].name); return -EOPNOTSUPP;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.