| // SPDX-License-Identifier: GPL-2.0 |
| /* Copyright (c) 2019, Intel Corporation. */ |
| |
| #include "ice_dcb_lib.h" |
| #include "ice_dcb_nl.h" |
| |
| /** |
| * ice_vsi_cfg_netdev_tc - Setup the netdev TC configuration |
| * @vsi: the VSI being configured |
| * @ena_tc: TC map to be enabled |
| */ |
| void ice_vsi_cfg_netdev_tc(struct ice_vsi *vsi, u8 ena_tc) |
| { |
| struct net_device *netdev = vsi->netdev; |
| struct ice_pf *pf = vsi->back; |
| struct ice_dcbx_cfg *dcbcfg; |
| u8 netdev_tc; |
| int i; |
| |
| if (!netdev) |
| return; |
| |
| if (!ena_tc) { |
| netdev_reset_tc(netdev); |
| return; |
| } |
| |
| if (netdev_set_num_tc(netdev, vsi->tc_cfg.numtc)) |
| return; |
| |
| dcbcfg = &pf->hw.port_info->local_dcbx_cfg; |
| |
| ice_for_each_traffic_class(i) |
| if (vsi->tc_cfg.ena_tc & BIT(i)) |
| netdev_set_tc_queue(netdev, |
| vsi->tc_cfg.tc_info[i].netdev_tc, |
| vsi->tc_cfg.tc_info[i].qcount_tx, |
| vsi->tc_cfg.tc_info[i].qoffset); |
| |
| for (i = 0; i < ICE_MAX_USER_PRIORITY; i++) { |
| u8 ets_tc = dcbcfg->etscfg.prio_table[i]; |
| |
| /* Get the mapped netdev TC# for the UP */ |
| netdev_tc = vsi->tc_cfg.tc_info[ets_tc].netdev_tc; |
| netdev_set_prio_tc_map(netdev, i, netdev_tc); |
| } |
| } |
| |
| /** |
| * ice_dcb_get_ena_tc - return bitmap of enabled TCs |
| * @dcbcfg: DCB config to evaluate for enabled TCs |
| */ |
| u8 ice_dcb_get_ena_tc(struct ice_dcbx_cfg *dcbcfg) |
| { |
| u8 i, num_tc, ena_tc = 1; |
| |
| num_tc = ice_dcb_get_num_tc(dcbcfg); |
| |
| for (i = 0; i < num_tc; i++) |
| ena_tc |= BIT(i); |
| |
| return ena_tc; |
| } |
| |
| /** |
| * ice_dcb_get_num_tc - Get the number of TCs from DCBX config |
| * @dcbcfg: config to retrieve number of TCs from |
| */ |
| u8 ice_dcb_get_num_tc(struct ice_dcbx_cfg *dcbcfg) |
| { |
| bool tc_unused = false; |
| u8 num_tc = 0; |
| u8 ret = 0; |
| int i; |
| |
| /* Scan the ETS Config Priority Table to find traffic classes |
| * enabled and create a bitmask of enabled TCs |
| */ |
| for (i = 0; i < CEE_DCBX_MAX_PRIO; i++) |
| num_tc |= BIT(dcbcfg->etscfg.prio_table[i]); |
| |
| /* Scan bitmask for contiguous TCs starting with TC0 */ |
| for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { |
| if (num_tc & BIT(i)) { |
| if (!tc_unused) { |
| ret++; |
| } else { |
| pr_err("Non-contiguous TCs - Disabling DCB\n"); |
| return 1; |
| } |
| } else { |
| tc_unused = true; |
| } |
| } |
| |
| /* There is always at least 1 TC */ |
| if (!ret) |
| ret = 1; |
| |
| return ret; |
| } |
| |
| /** |
| * ice_dcb_get_tc - Get the TC associated with the queue |
| * @vsi: ptr to the VSI |
| * @queue_index: queue number associated with VSI |
| */ |
| u8 ice_dcb_get_tc(struct ice_vsi *vsi, int queue_index) |
| { |
| return vsi->tx_rings[queue_index]->dcb_tc; |
| } |
| |
| /** |
| * ice_vsi_cfg_dcb_rings - Update rings to reflect DCB TC |
| * @vsi: VSI owner of rings being updated |
| */ |
| void ice_vsi_cfg_dcb_rings(struct ice_vsi *vsi) |
| { |
| struct ice_ring *tx_ring, *rx_ring; |
| u16 qoffset, qcount; |
| int i, n; |
| |
| if (!test_bit(ICE_FLAG_DCB_ENA, vsi->back->flags)) { |
| /* Reset the TC information */ |
| for (i = 0; i < vsi->num_txq; i++) { |
| tx_ring = vsi->tx_rings[i]; |
| tx_ring->dcb_tc = 0; |
| } |
| for (i = 0; i < vsi->num_rxq; i++) { |
| rx_ring = vsi->rx_rings[i]; |
| rx_ring->dcb_tc = 0; |
| } |
| return; |
| } |
| |
| ice_for_each_traffic_class(n) { |
| if (!(vsi->tc_cfg.ena_tc & BIT(n))) |
| break; |
| |
| qoffset = vsi->tc_cfg.tc_info[n].qoffset; |
| qcount = vsi->tc_cfg.tc_info[n].qcount_tx; |
| for (i = qoffset; i < (qoffset + qcount); i++) { |
| tx_ring = vsi->tx_rings[i]; |
| rx_ring = vsi->rx_rings[i]; |
| tx_ring->dcb_tc = n; |
| rx_ring->dcb_tc = n; |
| } |
| } |
| } |
| |
| /** |
| * ice_pf_dcb_cfg - Apply new DCB configuration |
| * @pf: pointer to the PF struct |
| * @new_cfg: DCBX config to apply |
| * @locked: is the RTNL held |
| */ |
| int ice_pf_dcb_cfg(struct ice_pf *pf, struct ice_dcbx_cfg *new_cfg, bool locked) |
| { |
| struct ice_aqc_port_ets_elem buf = { 0 }; |
| struct ice_dcbx_cfg *old_cfg, *curr_cfg; |
| struct device *dev = ice_pf_to_dev(pf); |
| int ret = ICE_DCB_NO_HW_CHG; |
| struct ice_vsi *pf_vsi; |
| |
| curr_cfg = &pf->hw.port_info->local_dcbx_cfg; |
| |
| /* FW does not care if change happened */ |
| if (!pf->hw.port_info->is_sw_lldp) |
| ret = ICE_DCB_HW_CHG_RST; |
| |
| /* Enable DCB tagging only when more than one TC */ |
| if (ice_dcb_get_num_tc(new_cfg) > 1) { |
| dev_dbg(dev, "DCB tagging enabled (num TC > 1)\n"); |
| set_bit(ICE_FLAG_DCB_ENA, pf->flags); |
| } else { |
| dev_dbg(dev, "DCB tagging disabled (num TC = 1)\n"); |
| clear_bit(ICE_FLAG_DCB_ENA, pf->flags); |
| } |
| |
| if (!memcmp(new_cfg, curr_cfg, sizeof(*new_cfg))) { |
| dev_dbg(dev, "No change in DCB config required\n"); |
| return ret; |
| } |
| |
| /* Store old config in case FW config fails */ |
| old_cfg = kmemdup(curr_cfg, sizeof(*old_cfg), GFP_KERNEL); |
| if (!old_cfg) |
| return -ENOMEM; |
| |
| dev_info(dev, "Commit DCB Configuration to the hardware\n"); |
| pf_vsi = ice_get_main_vsi(pf); |
| if (!pf_vsi) { |
| dev_dbg(dev, "PF VSI doesn't exist\n"); |
| ret = -EINVAL; |
| goto free_cfg; |
| } |
| |
| /* avoid race conditions by holding the lock while disabling and |
| * re-enabling the VSI |
| */ |
| if (!locked) |
| rtnl_lock(); |
| ice_dis_vsi(pf_vsi, true); |
| |
| memcpy(curr_cfg, new_cfg, sizeof(*curr_cfg)); |
| memcpy(&curr_cfg->etsrec, &curr_cfg->etscfg, sizeof(curr_cfg->etsrec)); |
| memcpy(&new_cfg->etsrec, &curr_cfg->etscfg, sizeof(curr_cfg->etsrec)); |
| |
| /* Only send new config to HW if we are in SW LLDP mode. Otherwise, |
| * the new config came from the HW in the first place. |
| */ |
| if (pf->hw.port_info->is_sw_lldp) { |
| ret = ice_set_dcb_cfg(pf->hw.port_info); |
| if (ret) { |
| dev_err(dev, "Set DCB Config failed\n"); |
| /* Restore previous settings to local config */ |
| memcpy(curr_cfg, old_cfg, sizeof(*curr_cfg)); |
| goto out; |
| } |
| } |
| |
| ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL); |
| if (ret) { |
| dev_err(dev, "Query Port ETS failed\n"); |
| goto out; |
| } |
| |
| ice_pf_dcb_recfg(pf); |
| |
| out: |
| ice_ena_vsi(pf_vsi, true); |
| if (!locked) |
| rtnl_unlock(); |
| free_cfg: |
| kfree(old_cfg); |
| return ret; |
| } |
| |
| /** |
| * ice_cfg_etsrec_defaults - Set default ETS recommended DCB config |
| * @pi: port information structure |
| */ |
| static void ice_cfg_etsrec_defaults(struct ice_port_info *pi) |
| { |
| struct ice_dcbx_cfg *dcbcfg = &pi->local_dcbx_cfg; |
| u8 i; |
| |
| /* Ensure ETS recommended DCB configuration is not already set */ |
| if (dcbcfg->etsrec.maxtcs) |
| return; |
| |
| /* In CEE mode, set the default to 1 TC */ |
| dcbcfg->etsrec.maxtcs = 1; |
| for (i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) { |
| dcbcfg->etsrec.tcbwtable[i] = i ? 0 : 100; |
| dcbcfg->etsrec.tsatable[i] = i ? ICE_IEEE_TSA_STRICT : |
| ICE_IEEE_TSA_ETS; |
| } |
| } |
| |
| /** |
| * ice_dcb_need_recfg - Check if DCB needs reconfig |
| * @pf: board private structure |
| * @old_cfg: current DCB config |
| * @new_cfg: new DCB config |
| */ |
| static bool |
| ice_dcb_need_recfg(struct ice_pf *pf, struct ice_dcbx_cfg *old_cfg, |
| struct ice_dcbx_cfg *new_cfg) |
| { |
| struct device *dev = ice_pf_to_dev(pf); |
| bool need_reconfig = false; |
| |
| /* Check if ETS configuration has changed */ |
| if (memcmp(&new_cfg->etscfg, &old_cfg->etscfg, |
| sizeof(new_cfg->etscfg))) { |
| /* If Priority Table has changed reconfig is needed */ |
| if (memcmp(&new_cfg->etscfg.prio_table, |
| &old_cfg->etscfg.prio_table, |
| sizeof(new_cfg->etscfg.prio_table))) { |
| need_reconfig = true; |
| dev_dbg(dev, "ETS UP2TC changed.\n"); |
| } |
| |
| if (memcmp(&new_cfg->etscfg.tcbwtable, |
| &old_cfg->etscfg.tcbwtable, |
| sizeof(new_cfg->etscfg.tcbwtable))) |
| dev_dbg(dev, "ETS TC BW Table changed.\n"); |
| |
| if (memcmp(&new_cfg->etscfg.tsatable, |
| &old_cfg->etscfg.tsatable, |
| sizeof(new_cfg->etscfg.tsatable))) |
| dev_dbg(dev, "ETS TSA Table changed.\n"); |
| } |
| |
| /* Check if PFC configuration has changed */ |
| if (memcmp(&new_cfg->pfc, &old_cfg->pfc, sizeof(new_cfg->pfc))) { |
| need_reconfig = true; |
| dev_dbg(dev, "PFC config change detected.\n"); |
| } |
| |
| /* Check if APP Table has changed */ |
| if (memcmp(&new_cfg->app, &old_cfg->app, sizeof(new_cfg->app))) { |
| need_reconfig = true; |
| dev_dbg(dev, "APP Table change detected.\n"); |
| } |
| |
| dev_dbg(dev, "dcb need_reconfig=%d\n", need_reconfig); |
| return need_reconfig; |
| } |
| |
| /** |
| * ice_dcb_rebuild - rebuild DCB post reset |
| * @pf: physical function instance |
| */ |
| void ice_dcb_rebuild(struct ice_pf *pf) |
| { |
| struct ice_dcbx_cfg *local_dcbx_cfg, *desired_dcbx_cfg, *prev_cfg; |
| struct ice_aqc_port_ets_elem buf = { 0 }; |
| struct device *dev = ice_pf_to_dev(pf); |
| enum ice_status ret; |
| |
| ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL); |
| if (ret) { |
| dev_err(dev, "Query Port ETS failed\n"); |
| goto dcb_error; |
| } |
| |
| /* If DCB was not enabled previously, we are done */ |
| if (!test_bit(ICE_FLAG_DCB_ENA, pf->flags)) |
| return; |
| |
| local_dcbx_cfg = &pf->hw.port_info->local_dcbx_cfg; |
| desired_dcbx_cfg = &pf->hw.port_info->desired_dcbx_cfg; |
| |
| /* Save current willing state and force FW to unwilling */ |
| local_dcbx_cfg->etscfg.willing = 0x0; |
| local_dcbx_cfg->pfc.willing = 0x0; |
| local_dcbx_cfg->app_mode = ICE_DCBX_APPS_NON_WILLING; |
| |
| ice_cfg_etsrec_defaults(pf->hw.port_info); |
| ret = ice_set_dcb_cfg(pf->hw.port_info); |
| if (ret) { |
| dev_err(dev, "Failed to set DCB to unwilling\n"); |
| goto dcb_error; |
| } |
| |
| /* Retrieve DCB config and ensure same as current in SW */ |
| prev_cfg = kmemdup(local_dcbx_cfg, sizeof(*prev_cfg), GFP_KERNEL); |
| if (!prev_cfg) |
| goto dcb_error; |
| |
| ice_init_dcb(&pf->hw, true); |
| if (pf->hw.port_info->dcbx_status == ICE_DCBX_STATUS_DIS) |
| pf->hw.port_info->is_sw_lldp = true; |
| else |
| pf->hw.port_info->is_sw_lldp = false; |
| |
| if (ice_dcb_need_recfg(pf, prev_cfg, local_dcbx_cfg)) { |
| /* difference in cfg detected - disable DCB till next MIB */ |
| dev_err(dev, "Set local MIB not accurate\n"); |
| kfree(prev_cfg); |
| goto dcb_error; |
| } |
| |
| /* fetched config congruent to previous configuration */ |
| kfree(prev_cfg); |
| |
| /* Set the local desired config */ |
| if (local_dcbx_cfg->dcbx_mode == ICE_DCBX_MODE_CEE) |
| memcpy(local_dcbx_cfg, desired_dcbx_cfg, |
| sizeof(*local_dcbx_cfg)); |
| |
| ice_cfg_etsrec_defaults(pf->hw.port_info); |
| ret = ice_set_dcb_cfg(pf->hw.port_info); |
| if (ret) { |
| dev_err(dev, "Failed to set desired config\n"); |
| goto dcb_error; |
| } |
| dev_info(dev, "DCB restored after reset\n"); |
| ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL); |
| if (ret) { |
| dev_err(dev, "Query Port ETS failed\n"); |
| goto dcb_error; |
| } |
| |
| return; |
| |
| dcb_error: |
| dev_err(dev, "Disabling DCB until new settings occur\n"); |
| prev_cfg = kzalloc(sizeof(*prev_cfg), GFP_KERNEL); |
| if (!prev_cfg) |
| return; |
| |
| prev_cfg->etscfg.willing = true; |
| prev_cfg->etscfg.tcbwtable[0] = ICE_TC_MAX_BW; |
| prev_cfg->etscfg.tsatable[0] = ICE_IEEE_TSA_ETS; |
| memcpy(&prev_cfg->etsrec, &prev_cfg->etscfg, sizeof(prev_cfg->etsrec)); |
| /* Coverity warns the return code of ice_pf_dcb_cfg() is not checked |
| * here as is done for other calls to that function. That check is |
| * not necessary since this is in this function's error cleanup path. |
| * Suppress the Coverity warning with the following comment... |
| */ |
| /* coverity[check_return] */ |
| ice_pf_dcb_cfg(pf, prev_cfg, false); |
| kfree(prev_cfg); |
| } |
| |
| /** |
| * ice_dcb_init_cfg - set the initial DCB config in SW |
| * @pf: PF to apply config to |
| * @locked: Is the RTNL held |
| */ |
| static int ice_dcb_init_cfg(struct ice_pf *pf, bool locked) |
| { |
| struct ice_dcbx_cfg *newcfg; |
| struct ice_port_info *pi; |
| int ret = 0; |
| |
| pi = pf->hw.port_info; |
| newcfg = kmemdup(&pi->local_dcbx_cfg, sizeof(*newcfg), GFP_KERNEL); |
| if (!newcfg) |
| return -ENOMEM; |
| |
| memset(&pi->local_dcbx_cfg, 0, sizeof(*newcfg)); |
| |
| dev_info(ice_pf_to_dev(pf), "Configuring initial DCB values\n"); |
| if (ice_pf_dcb_cfg(pf, newcfg, locked)) |
| ret = -EINVAL; |
| |
| kfree(newcfg); |
| |
| return ret; |
| } |
| |
| /** |
| * ice_dcb_sw_default_config - Apply a default DCB config |
| * @pf: PF to apply config to |
| * @ets_willing: configure ets willing |
| * @locked: was this function called with RTNL held |
| */ |
| static int ice_dcb_sw_dflt_cfg(struct ice_pf *pf, bool ets_willing, bool locked) |
| { |
| struct ice_aqc_port_ets_elem buf = { 0 }; |
| struct ice_dcbx_cfg *dcbcfg; |
| struct ice_port_info *pi; |
| struct ice_hw *hw; |
| int ret; |
| |
| hw = &pf->hw; |
| pi = hw->port_info; |
| dcbcfg = kzalloc(sizeof(*dcbcfg), GFP_KERNEL); |
| if (!dcbcfg) |
| return -ENOMEM; |
| |
| memset(&pi->local_dcbx_cfg, 0, sizeof(*dcbcfg)); |
| |
| dcbcfg->etscfg.willing = ets_willing ? 1 : 0; |
| dcbcfg->etscfg.maxtcs = hw->func_caps.common_cap.maxtc; |
| dcbcfg->etscfg.tcbwtable[0] = 100; |
| dcbcfg->etscfg.tsatable[0] = ICE_IEEE_TSA_ETS; |
| |
| memcpy(&dcbcfg->etsrec, &dcbcfg->etscfg, |
| sizeof(dcbcfg->etsrec)); |
| dcbcfg->etsrec.willing = 0; |
| |
| dcbcfg->pfc.willing = 1; |
| dcbcfg->pfc.pfccap = hw->func_caps.common_cap.maxtc; |
| |
| dcbcfg->numapps = 1; |
| dcbcfg->app[0].selector = ICE_APP_SEL_ETHTYPE; |
| dcbcfg->app[0].priority = 3; |
| dcbcfg->app[0].prot_id = ICE_APP_PROT_ID_FCOE; |
| |
| ret = ice_pf_dcb_cfg(pf, dcbcfg, locked); |
| kfree(dcbcfg); |
| if (ret) |
| return ret; |
| |
| return ice_query_port_ets(pi, &buf, sizeof(buf), NULL); |
| } |
| |
| /** |
| * ice_dcb_tc_contig - Check that TCs are contiguous |
| * @prio_table: pointer to priority table |
| * |
| * Check if TCs begin with TC0 and are contiguous |
| */ |
| static bool ice_dcb_tc_contig(u8 *prio_table) |
| { |
| u8 max_tc = 0; |
| int i; |
| |
| for (i = 0; i < CEE_DCBX_MAX_PRIO; i++) { |
| u8 cur_tc = prio_table[i]; |
| |
| if (cur_tc > max_tc) |
| return false; |
| else if (cur_tc == max_tc) |
| max_tc++; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * ice_dcb_noncontig_cfg - Configure DCB for non-contiguous TCs |
| * @pf: pointer to the PF struct |
| * |
| * If non-contiguous TCs, then configure SW DCB with TC0 and ETS non-willing |
| */ |
| static int ice_dcb_noncontig_cfg(struct ice_pf *pf) |
| { |
| struct ice_dcbx_cfg *dcbcfg = &pf->hw.port_info->local_dcbx_cfg; |
| struct device *dev = ice_pf_to_dev(pf); |
| int ret; |
| |
| /* Configure SW DCB default with ETS non-willing */ |
| ret = ice_dcb_sw_dflt_cfg(pf, false, true); |
| if (ret) { |
| dev_err(dev, "Failed to set local DCB config %d\n", ret); |
| return ret; |
| } |
| |
| /* Reconfigure with ETS willing so that FW will send LLDP MIB event */ |
| dcbcfg->etscfg.willing = 1; |
| ret = ice_set_dcb_cfg(pf->hw.port_info); |
| if (ret) |
| dev_err(dev, "Failed to set DCB to unwilling\n"); |
| |
| return ret; |
| } |
| |
| /** |
| * ice_pf_dcb_recfg - Reconfigure all VEBs and VSIs |
| * @pf: pointer to the PF struct |
| * |
| * Assumed caller has already disabled all VSIs before |
| * calling this function. Reconfiguring DCB based on |
| * local_dcbx_cfg. |
| */ |
| void ice_pf_dcb_recfg(struct ice_pf *pf) |
| { |
| struct ice_dcbx_cfg *dcbcfg = &pf->hw.port_info->local_dcbx_cfg; |
| u8 tc_map = 0; |
| int v, ret; |
| |
| /* Update each VSI */ |
| ice_for_each_vsi(pf, v) { |
| struct ice_vsi *vsi = pf->vsi[v]; |
| |
| if (!vsi) |
| continue; |
| |
| if (vsi->type == ICE_VSI_PF) { |
| tc_map = ice_dcb_get_ena_tc(dcbcfg); |
| |
| /* If DCBX request non-contiguous TC, then configure |
| * default TC |
| */ |
| if (!ice_dcb_tc_contig(dcbcfg->etscfg.prio_table)) { |
| tc_map = ICE_DFLT_TRAFFIC_CLASS; |
| ice_dcb_noncontig_cfg(pf); |
| } |
| } else { |
| tc_map = ICE_DFLT_TRAFFIC_CLASS; |
| } |
| |
| ret = ice_vsi_cfg_tc(vsi, tc_map); |
| if (ret) { |
| dev_err(ice_pf_to_dev(pf), "Failed to config TC for VSI index: %d\n", |
| vsi->idx); |
| continue; |
| } |
| |
| ice_vsi_map_rings_to_vectors(vsi); |
| if (vsi->type == ICE_VSI_PF) |
| ice_dcbnl_set_all(vsi); |
| } |
| } |
| |
| /** |
| * ice_init_pf_dcb - initialize DCB for a PF |
| * @pf: PF to initialize DCB for |
| * @locked: Was function called with RTNL held |
| */ |
| int ice_init_pf_dcb(struct ice_pf *pf, bool locked) |
| { |
| struct device *dev = ice_pf_to_dev(pf); |
| struct ice_port_info *port_info; |
| struct ice_hw *hw = &pf->hw; |
| int err; |
| |
| port_info = hw->port_info; |
| |
| err = ice_init_dcb(hw, false); |
| if (err && !port_info->is_sw_lldp) { |
| dev_err(dev, "Error initializing DCB %d\n", err); |
| goto dcb_init_err; |
| } |
| |
| dev_info(dev, |
| "DCB is enabled in the hardware, max number of TCs supported on this port are %d\n", |
| pf->hw.func_caps.common_cap.maxtc); |
| if (err) { |
| struct ice_vsi *pf_vsi; |
| |
| /* FW LLDP is disabled, activate SW DCBX/LLDP mode */ |
| dev_info(dev, "FW LLDP is disabled, DCBx/LLDP in SW mode.\n"); |
| clear_bit(ICE_FLAG_FW_LLDP_AGENT, pf->flags); |
| err = ice_dcb_sw_dflt_cfg(pf, true, locked); |
| if (err) { |
| dev_err(dev, |
| "Failed to set local DCB config %d\n", err); |
| err = -EIO; |
| goto dcb_init_err; |
| } |
| |
| /* If the FW DCBX engine is not running then Rx LLDP packets |
| * need to be redirected up the stack. |
| */ |
| pf_vsi = ice_get_main_vsi(pf); |
| if (!pf_vsi) { |
| dev_err(dev, "Failed to set local DCB config\n"); |
| err = -EIO; |
| goto dcb_init_err; |
| } |
| |
| ice_cfg_sw_lldp(pf_vsi, false, true); |
| |
| pf->dcbx_cap = DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE; |
| return 0; |
| } |
| |
| set_bit(ICE_FLAG_FW_LLDP_AGENT, pf->flags); |
| |
| /* DCBX in FW and LLDP enabled in FW */ |
| pf->dcbx_cap = DCB_CAP_DCBX_LLD_MANAGED | DCB_CAP_DCBX_VER_IEEE; |
| |
| err = ice_dcb_init_cfg(pf, locked); |
| if (err) |
| goto dcb_init_err; |
| |
| return err; |
| |
| dcb_init_err: |
| dev_err(dev, "DCB init failed\n"); |
| return err; |
| } |
| |
| /** |
| * ice_update_dcb_stats - Update DCB stats counters |
| * @pf: PF whose stats needs to be updated |
| */ |
| void ice_update_dcb_stats(struct ice_pf *pf) |
| { |
| struct ice_hw_port_stats *prev_ps, *cur_ps; |
| struct ice_hw *hw = &pf->hw; |
| u8 port; |
| int i; |
| |
| port = hw->port_info->lport; |
| prev_ps = &pf->stats_prev; |
| cur_ps = &pf->stats; |
| |
| for (i = 0; i < 8; i++) { |
| ice_stat_update32(hw, GLPRT_PXOFFRXC(port, i), |
| pf->stat_prev_loaded, |
| &prev_ps->priority_xoff_rx[i], |
| &cur_ps->priority_xoff_rx[i]); |
| ice_stat_update32(hw, GLPRT_PXONRXC(port, i), |
| pf->stat_prev_loaded, |
| &prev_ps->priority_xon_rx[i], |
| &cur_ps->priority_xon_rx[i]); |
| ice_stat_update32(hw, GLPRT_PXONTXC(port, i), |
| pf->stat_prev_loaded, |
| &prev_ps->priority_xon_tx[i], |
| &cur_ps->priority_xon_tx[i]); |
| ice_stat_update32(hw, GLPRT_PXOFFTXC(port, i), |
| pf->stat_prev_loaded, |
| &prev_ps->priority_xoff_tx[i], |
| &cur_ps->priority_xoff_tx[i]); |
| ice_stat_update32(hw, GLPRT_RXON2OFFCNT(port, i), |
| pf->stat_prev_loaded, |
| &prev_ps->priority_xon_2_xoff[i], |
| &cur_ps->priority_xon_2_xoff[i]); |
| } |
| } |
| |
| /** |
| * ice_tx_prepare_vlan_flags_dcb - prepare VLAN tagging for DCB |
| * @tx_ring: ring to send buffer on |
| * @first: pointer to struct ice_tx_buf |
| */ |
| int |
| ice_tx_prepare_vlan_flags_dcb(struct ice_ring *tx_ring, |
| struct ice_tx_buf *first) |
| { |
| struct sk_buff *skb = first->skb; |
| |
| if (!test_bit(ICE_FLAG_DCB_ENA, tx_ring->vsi->back->flags)) |
| return 0; |
| |
| /* Insert 802.1p priority into VLAN header */ |
| if ((first->tx_flags & (ICE_TX_FLAGS_HW_VLAN | ICE_TX_FLAGS_SW_VLAN)) || |
| skb->priority != TC_PRIO_CONTROL) { |
| first->tx_flags &= ~ICE_TX_FLAGS_VLAN_PR_M; |
| /* Mask the lower 3 bits to set the 802.1p priority */ |
| first->tx_flags |= (skb->priority & 0x7) << |
| ICE_TX_FLAGS_VLAN_PR_S; |
| if (first->tx_flags & ICE_TX_FLAGS_SW_VLAN) { |
| struct vlan_ethhdr *vhdr; |
| int rc; |
| |
| rc = skb_cow_head(skb, 0); |
| if (rc < 0) |
| return rc; |
| vhdr = (struct vlan_ethhdr *)skb->data; |
| vhdr->h_vlan_TCI = htons(first->tx_flags >> |
| ICE_TX_FLAGS_VLAN_S); |
| } else { |
| first->tx_flags |= ICE_TX_FLAGS_HW_VLAN; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * ice_dcb_process_lldp_set_mib_change - Process MIB change |
| * @pf: ptr to ice_pf |
| * @event: pointer to the admin queue receive event |
| */ |
| void |
| ice_dcb_process_lldp_set_mib_change(struct ice_pf *pf, |
| struct ice_rq_event_info *event) |
| { |
| struct ice_aqc_port_ets_elem buf = { 0 }; |
| struct device *dev = ice_pf_to_dev(pf); |
| struct ice_aqc_lldp_get_mib *mib; |
| struct ice_dcbx_cfg tmp_dcbx_cfg; |
| bool need_reconfig = false; |
| struct ice_port_info *pi; |
| struct ice_vsi *pf_vsi; |
| u8 type; |
| int ret; |
| |
| /* Not DCB capable or capability disabled */ |
| if (!(test_bit(ICE_FLAG_DCB_CAPABLE, pf->flags))) |
| return; |
| |
| if (pf->dcbx_cap & DCB_CAP_DCBX_HOST) { |
| dev_dbg(dev, "MIB Change Event in HOST mode\n"); |
| return; |
| } |
| |
| pi = pf->hw.port_info; |
| mib = (struct ice_aqc_lldp_get_mib *)&event->desc.params.raw; |
| /* Ignore if event is not for Nearest Bridge */ |
| type = ((mib->type >> ICE_AQ_LLDP_BRID_TYPE_S) & |
| ICE_AQ_LLDP_BRID_TYPE_M); |
| dev_dbg(dev, "LLDP event MIB bridge type 0x%x\n", type); |
| if (type != ICE_AQ_LLDP_BRID_TYPE_NEAREST_BRID) |
| return; |
| |
| /* Check MIB Type and return if event for Remote MIB update */ |
| type = mib->type & ICE_AQ_LLDP_MIB_TYPE_M; |
| dev_dbg(dev, "LLDP event mib type %s\n", type ? "remote" : "local"); |
| if (type == ICE_AQ_LLDP_MIB_REMOTE) { |
| /* Update the remote cached instance and return */ |
| ret = ice_aq_get_dcb_cfg(pi->hw, ICE_AQ_LLDP_MIB_REMOTE, |
| ICE_AQ_LLDP_BRID_TYPE_NEAREST_BRID, |
| &pi->remote_dcbx_cfg); |
| if (ret) { |
| dev_err(dev, "Failed to get remote DCB config\n"); |
| return; |
| } |
| } |
| |
| /* store the old configuration */ |
| tmp_dcbx_cfg = pf->hw.port_info->local_dcbx_cfg; |
| |
| /* Reset the old DCBX configuration data */ |
| memset(&pi->local_dcbx_cfg, 0, sizeof(pi->local_dcbx_cfg)); |
| |
| /* Get updated DCBX data from firmware */ |
| ret = ice_get_dcb_cfg(pf->hw.port_info); |
| if (ret) { |
| dev_err(dev, "Failed to get DCB config\n"); |
| return; |
| } |
| |
| /* No change detected in DCBX configs */ |
| if (!memcmp(&tmp_dcbx_cfg, &pi->local_dcbx_cfg, sizeof(tmp_dcbx_cfg))) { |
| dev_dbg(dev, "No change detected in DCBX configuration.\n"); |
| return; |
| } |
| |
| need_reconfig = ice_dcb_need_recfg(pf, &tmp_dcbx_cfg, |
| &pi->local_dcbx_cfg); |
| ice_dcbnl_flush_apps(pf, &tmp_dcbx_cfg, &pi->local_dcbx_cfg); |
| if (!need_reconfig) |
| return; |
| |
| /* Enable DCB tagging only when more than one TC */ |
| if (ice_dcb_get_num_tc(&pi->local_dcbx_cfg) > 1) { |
| dev_dbg(dev, "DCB tagging enabled (num TC > 1)\n"); |
| set_bit(ICE_FLAG_DCB_ENA, pf->flags); |
| } else { |
| dev_dbg(dev, "DCB tagging disabled (num TC = 1)\n"); |
| clear_bit(ICE_FLAG_DCB_ENA, pf->flags); |
| } |
| |
| pf_vsi = ice_get_main_vsi(pf); |
| if (!pf_vsi) { |
| dev_dbg(dev, "PF VSI doesn't exist\n"); |
| return; |
| } |
| |
| rtnl_lock(); |
| ice_dis_vsi(pf_vsi, true); |
| |
| ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL); |
| if (ret) { |
| dev_err(dev, "Query Port ETS failed\n"); |
| rtnl_unlock(); |
| return; |
| } |
| |
| /* changes in configuration update VSI */ |
| ice_pf_dcb_recfg(pf); |
| |
| ice_ena_vsi(pf_vsi, true); |
| rtnl_unlock(); |
| } |