/******************************************************************************* * * Intel Ethernet Controller XL710 Family Linux Driver * Copyright(c) 2013 - 2014 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * * Contact Information: * e1000-devel Mailing List * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * ******************************************************************************/ #ifdef CONFIG_I40E_DCB #include "i40e.h" #include /** * i40e_get_pfc_delay - retrieve PFC Link Delay * @hw: pointer to hardware struct * @delay: holds the PFC Link delay value * * Returns PFC Link Delay from the PRTDCB_GENC.PFCLDA **/ static void i40e_get_pfc_delay(struct i40e_hw *hw, u16 *delay) { u32 val; val = rd32(hw, I40E_PRTDCB_GENC); *delay = (u16)(val & I40E_PRTDCB_GENC_PFCLDA_MASK >> I40E_PRTDCB_GENC_PFCLDA_SHIFT); } /** * i40e_dcbnl_ieee_getets - retrieve local IEEE ETS configuration * @netdev: the corresponding netdev * @ets: structure to hold the ETS information * * Returns local IEEE ETS configuration **/ static int i40e_dcbnl_ieee_getets(struct net_device *dev, struct ieee_ets *ets) { struct i40e_pf *pf = i40e_netdev_to_pf(dev); struct i40e_dcbx_config *dcbxcfg; struct i40e_hw *hw = &pf->hw; if (!(pf->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) return -EINVAL; dcbxcfg = &hw->local_dcbx_config; ets->willing = dcbxcfg->etscfg.willing; ets->ets_cap = dcbxcfg->etscfg.maxtcs; ets->cbs = dcbxcfg->etscfg.cbs; memcpy(ets->tc_tx_bw, dcbxcfg->etscfg.tcbwtable, sizeof(ets->tc_tx_bw)); memcpy(ets->tc_rx_bw, dcbxcfg->etscfg.tcbwtable, sizeof(ets->tc_rx_bw)); memcpy(ets->tc_tsa, dcbxcfg->etscfg.tsatable, sizeof(ets->tc_tsa)); memcpy(ets->prio_tc, dcbxcfg->etscfg.prioritytable, sizeof(ets->prio_tc)); memcpy(ets->tc_reco_bw, dcbxcfg->etsrec.tcbwtable, sizeof(ets->tc_reco_bw)); memcpy(ets->tc_reco_tsa, dcbxcfg->etsrec.tsatable, sizeof(ets->tc_reco_tsa)); memcpy(ets->reco_prio_tc, dcbxcfg->etscfg.prioritytable, sizeof(ets->reco_prio_tc)); return 0; } /** * i40e_dcbnl_ieee_getpfc - retrieve local IEEE PFC configuration * @netdev: the corresponding netdev * @ets: structure to hold the PFC information * * Returns local IEEE PFC configuration **/ static int i40e_dcbnl_ieee_getpfc(struct net_device *dev, struct ieee_pfc *pfc) { struct i40e_pf *pf = i40e_netdev_to_pf(dev); struct i40e_dcbx_config *dcbxcfg; struct i40e_hw *hw = &pf->hw; int i; if (!(pf->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) return -EINVAL; dcbxcfg = &hw->local_dcbx_config; pfc->pfc_cap = dcbxcfg->pfc.pfccap; pfc->pfc_en = dcbxcfg->pfc.pfcenable; pfc->mbc = dcbxcfg->pfc.mbc; i40e_get_pfc_delay(hw, &pfc->delay); /* Get Requests/Indicatiosn */ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { pfc->requests[i] = pf->stats.priority_xoff_tx[i]; pfc->indications[i] = pf->stats.priority_xoff_rx[i]; } return 0; } /** * i40e_dcbnl_getdcbx - retrieve current DCBx capability * @netdev: the corresponding netdev * * Returns DCBx capability features **/ static u8 i40e_dcbnl_getdcbx(struct net_device *dev) { struct i40e_pf *pf = i40e_netdev_to_pf(dev); return pf->dcbx_cap; } /** * i40e_dcbnl_get_perm_hw_addr - MAC address used by DCBx * @netdev: the corresponding netdev * * Returns the SAN MAC address used for LLDP exchange **/ static void i40e_dcbnl_get_perm_hw_addr(struct net_device *dev, u8 *perm_addr) { struct i40e_pf *pf = i40e_netdev_to_pf(dev); int i, j; memset(perm_addr, 0xff, MAX_ADDR_LEN); for (i = 0; i < dev->addr_len; i++) perm_addr[i] = pf->hw.mac.perm_addr[i]; for (j = 0; j < dev->addr_len; j++, i++) perm_addr[i] = pf->hw.mac.san_addr[j]; } static const struct dcbnl_rtnl_ops dcbnl_ops = { .ieee_getets = i40e_dcbnl_ieee_getets, .ieee_getpfc = i40e_dcbnl_ieee_getpfc, .getdcbx = i40e_dcbnl_getdcbx, .getpermhwaddr = i40e_dcbnl_get_perm_hw_addr, }; /** * i40e_dcbnl_set_all - set all the apps and ieee data from DCBx config * @vsi: the corresponding vsi * * Set up all the IEEE APPs in the DCBNL App Table and generate event for * other settings **/ void i40e_dcbnl_set_all(struct i40e_vsi *vsi) { struct net_device *dev = vsi->netdev; struct i40e_pf *pf = i40e_netdev_to_pf(dev); struct i40e_dcbx_config *dcbxcfg; struct i40e_hw *hw = &pf->hw; struct dcb_app sapp; u8 prio, tc_map; int i; /* DCB not enabled */ if (!(pf->flags & I40E_FLAG_DCB_ENABLED)) return; dcbxcfg = &hw->local_dcbx_config; /* Set up all the App TLVs if DCBx is negotiated */ for (i = 0; i < dcbxcfg->numapps; i++) { prio = dcbxcfg->app[i].priority; tc_map = (1 << dcbxcfg->etscfg.prioritytable[prio]); /* Add APP only if the TC is enabled for this VSI */ if (tc_map & vsi->tc_config.enabled_tc) { sapp.selector = dcbxcfg->app[i].selector; sapp.protocol = dcbxcfg->app[i].protocolid; sapp.priority = prio; dcb_ieee_setapp(dev, &sapp); } } /* Notify user-space of the changes */ dcbnl_ieee_notify(dev, RTM_SETDCB, DCB_CMD_IEEE_SET, 0, 0); } /** * i40e_dcbnl_vsi_del_app - Delete APP for given VSI * @vsi: the corresponding vsi * @app: APP to delete * * Delete given APP from the DCBNL APP table for given * VSI **/ static int i40e_dcbnl_vsi_del_app(struct i40e_vsi *vsi, struct i40e_ieee_app_priority_table *app) { struct net_device *dev = vsi->netdev; struct dcb_app sapp; if (!dev) return -EINVAL; sapp.selector = app->selector; sapp.protocol = app->protocolid; sapp.priority = app->priority; return dcb_ieee_delapp(dev, &sapp); } /** * i40e_dcbnl_del_app - Delete APP on all VSIs * @pf: the corresponding pf * @app: APP to delete * * Delete given APP from all the VSIs for given PF **/ static void i40e_dcbnl_del_app(struct i40e_pf *pf, struct i40e_ieee_app_priority_table *app) { int v, err; for (v = 0; v < pf->num_alloc_vsi; v++) { if (pf->vsi[v] && pf->vsi[v]->netdev) { err = i40e_dcbnl_vsi_del_app(pf->vsi[v], app); if (err) dev_info(&pf->pdev->dev, "%s: Failed deleting app for VSI seid=%d err=%d sel=%d proto=0x%x prio=%d\n", __func__, pf->vsi[v]->seid, err, app->selector, app->protocolid, app->priority); } } } /** * i40e_dcbnl_find_app - Search APP in given DCB config * @cfg: DCBX configuration data * @app: APP to search for * * Find given APP in the DCB configuration **/ static bool i40e_dcbnl_find_app(struct i40e_dcbx_config *cfg, struct i40e_ieee_app_priority_table *app) { int i; for (i = 0; i < cfg->numapps; i++) { if (app->selector == cfg->app[i].selector && app->protocolid == cfg->app[i].protocolid && app->priority == cfg->app[i].priority) return true; } return false; } /** * i40e_dcbnl_flush_apps - Delete all removed APPs * @pf: the corresponding pf * @new_cfg: new DCBX configuration data * * Find and delete all APPs that are not present in the passed * DCB configuration **/ void i40e_dcbnl_flush_apps(struct i40e_pf *pf, struct i40e_dcbx_config *new_cfg) { struct i40e_ieee_app_priority_table app; struct i40e_dcbx_config *dcbxcfg; struct i40e_hw *hw = &pf->hw; int i; dcbxcfg = &hw->local_dcbx_config; for (i = 0; i < dcbxcfg->numapps; i++) { app = dcbxcfg->app[i]; /* The APP is not available anymore delete it */ if (!i40e_dcbnl_find_app(new_cfg, &app)) i40e_dcbnl_del_app(pf, &app); } } /** * i40e_dcbnl_setup - DCBNL setup * @vsi: the corresponding vsi * * Set up DCBNL ops and initial APP TLVs **/ void i40e_dcbnl_setup(struct i40e_vsi *vsi) { struct net_device *dev = vsi->netdev; struct i40e_pf *pf = i40e_netdev_to_pf(dev); /* DCB not enabled */ if (!(pf->flags & I40E_FLAG_DCB_ENABLED)) return; /* Do not setup DCB NL ops for MFP mode */ if (!(pf->flags & I40E_FLAG_MFP_ENABLED)) dev->dcbnl_ops = &dcbnl_ops; /* Set initial IEEE DCB settings */ i40e_dcbnl_set_all(vsi); } #endif /* CONFIG_I40E_DCB */