1 /******************************************************************************* 2 * 3 * Intel Ethernet Controller XL710 Family Linux Driver 4 * Copyright(c) 2013 - 2014 Intel Corporation. 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms and conditions of the GNU General Public License, 8 * version 2, as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 * more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 * The full GNU General Public License is included in this distribution in 19 * the file called "COPYING". 20 * 21 * Contact Information: 22 * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net> 23 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 24 * 25 ******************************************************************************/ 26 27 #ifdef CONFIG_I40E_DCB 28 #include "i40e.h" 29 #include <net/dcbnl.h> 30 31 /** 32 * i40e_get_pfc_delay - retrieve PFC Link Delay 33 * @hw: pointer to hardware struct 34 * @delay: holds the PFC Link delay value 35 * 36 * Returns PFC Link Delay from the PRTDCB_GENC.PFCLDA 37 **/ 38 static void i40e_get_pfc_delay(struct i40e_hw *hw, u16 *delay) 39 { 40 u32 val; 41 42 val = rd32(hw, I40E_PRTDCB_GENC); 43 *delay = (u16)(val & I40E_PRTDCB_GENC_PFCLDA_MASK >> 44 I40E_PRTDCB_GENC_PFCLDA_SHIFT); 45 } 46 47 /** 48 * i40e_dcbnl_ieee_getets - retrieve local IEEE ETS configuration 49 * @netdev: the corresponding netdev 50 * @ets: structure to hold the ETS information 51 * 52 * Returns local IEEE ETS configuration 53 **/ 54 static int i40e_dcbnl_ieee_getets(struct net_device *dev, 55 struct ieee_ets *ets) 56 { 57 struct i40e_pf *pf = i40e_netdev_to_pf(dev); 58 struct i40e_dcbx_config *dcbxcfg; 59 struct i40e_hw *hw = &pf->hw; 60 61 if (!(pf->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) 62 return -EINVAL; 63 64 dcbxcfg = &hw->local_dcbx_config; 65 ets->willing = dcbxcfg->etscfg.willing; 66 ets->ets_cap = dcbxcfg->etscfg.maxtcs; 67 ets->cbs = dcbxcfg->etscfg.cbs; 68 memcpy(ets->tc_tx_bw, dcbxcfg->etscfg.tcbwtable, 69 sizeof(ets->tc_tx_bw)); 70 memcpy(ets->tc_rx_bw, dcbxcfg->etscfg.tcbwtable, 71 sizeof(ets->tc_rx_bw)); 72 memcpy(ets->tc_tsa, dcbxcfg->etscfg.tsatable, 73 sizeof(ets->tc_tsa)); 74 memcpy(ets->prio_tc, dcbxcfg->etscfg.prioritytable, 75 sizeof(ets->prio_tc)); 76 memcpy(ets->tc_reco_bw, dcbxcfg->etsrec.tcbwtable, 77 sizeof(ets->tc_reco_bw)); 78 memcpy(ets->tc_reco_tsa, dcbxcfg->etsrec.tsatable, 79 sizeof(ets->tc_reco_tsa)); 80 memcpy(ets->reco_prio_tc, dcbxcfg->etscfg.prioritytable, 81 sizeof(ets->reco_prio_tc)); 82 83 return 0; 84 } 85 86 /** 87 * i40e_dcbnl_ieee_getpfc - retrieve local IEEE PFC configuration 88 * @netdev: the corresponding netdev 89 * @ets: structure to hold the PFC information 90 * 91 * Returns local IEEE PFC configuration 92 **/ 93 static int i40e_dcbnl_ieee_getpfc(struct net_device *dev, 94 struct ieee_pfc *pfc) 95 { 96 struct i40e_pf *pf = i40e_netdev_to_pf(dev); 97 struct i40e_dcbx_config *dcbxcfg; 98 struct i40e_hw *hw = &pf->hw; 99 int i; 100 101 if (!(pf->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) 102 return -EINVAL; 103 104 dcbxcfg = &hw->local_dcbx_config; 105 pfc->pfc_cap = dcbxcfg->pfc.pfccap; 106 pfc->pfc_en = dcbxcfg->pfc.pfcenable; 107 pfc->mbc = dcbxcfg->pfc.mbc; 108 i40e_get_pfc_delay(hw, &pfc->delay); 109 110 /* Get Requests/Indicatiosn */ 111 for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { 112 pfc->requests[i] = pf->stats.priority_xoff_tx[i]; 113 pfc->indications[i] = pf->stats.priority_xoff_rx[i]; 114 } 115 116 return 0; 117 } 118 119 /** 120 * i40e_dcbnl_getdcbx - retrieve current DCBx capability 121 * @netdev: the corresponding netdev 122 * 123 * Returns DCBx capability features 124 **/ 125 static u8 i40e_dcbnl_getdcbx(struct net_device *dev) 126 { 127 struct i40e_pf *pf = i40e_netdev_to_pf(dev); 128 129 return pf->dcbx_cap; 130 } 131 132 /** 133 * i40e_dcbnl_get_perm_hw_addr - MAC address used by DCBx 134 * @netdev: the corresponding netdev 135 * 136 * Returns the SAN MAC address used for LLDP exchange 137 **/ 138 static void i40e_dcbnl_get_perm_hw_addr(struct net_device *dev, 139 u8 *perm_addr) 140 { 141 struct i40e_pf *pf = i40e_netdev_to_pf(dev); 142 int i, j; 143 144 memset(perm_addr, 0xff, MAX_ADDR_LEN); 145 146 for (i = 0; i < dev->addr_len; i++) 147 perm_addr[i] = pf->hw.mac.perm_addr[i]; 148 149 for (j = 0; j < dev->addr_len; j++, i++) 150 perm_addr[i] = pf->hw.mac.san_addr[j]; 151 } 152 153 static const struct dcbnl_rtnl_ops dcbnl_ops = { 154 .ieee_getets = i40e_dcbnl_ieee_getets, 155 .ieee_getpfc = i40e_dcbnl_ieee_getpfc, 156 .getdcbx = i40e_dcbnl_getdcbx, 157 .getpermhwaddr = i40e_dcbnl_get_perm_hw_addr, 158 }; 159 160 /** 161 * i40e_dcbnl_set_all - set all the apps and ieee data from DCBx config 162 * @vsi: the corresponding vsi 163 * 164 * Set up all the IEEE APPs in the DCBNL App Table and generate event for 165 * other settings 166 **/ 167 void i40e_dcbnl_set_all(struct i40e_vsi *vsi) 168 { 169 struct net_device *dev = vsi->netdev; 170 struct i40e_pf *pf = i40e_netdev_to_pf(dev); 171 struct i40e_dcbx_config *dcbxcfg; 172 struct i40e_hw *hw = &pf->hw; 173 struct dcb_app sapp; 174 u8 prio, tc_map; 175 int i; 176 177 /* DCB not enabled */ 178 if (!(pf->flags & I40E_FLAG_DCB_ENABLED)) 179 return; 180 181 dcbxcfg = &hw->local_dcbx_config; 182 183 /* Set up all the App TLVs if DCBx is negotiated */ 184 for (i = 0; i < dcbxcfg->numapps; i++) { 185 prio = dcbxcfg->app[i].priority; 186 tc_map = (1 << dcbxcfg->etscfg.prioritytable[prio]); 187 188 /* Add APP only if the TC is enabled for this VSI */ 189 if (tc_map & vsi->tc_config.enabled_tc) { 190 sapp.selector = dcbxcfg->app[i].selector; 191 sapp.protocol = dcbxcfg->app[i].protocolid; 192 sapp.priority = prio; 193 dcb_ieee_setapp(dev, &sapp); 194 } 195 } 196 197 /* Notify user-space of the changes */ 198 dcbnl_ieee_notify(dev, RTM_SETDCB, DCB_CMD_IEEE_SET, 0, 0); 199 } 200 201 /** 202 * i40e_dcbnl_vsi_del_app - Delete APP for given VSI 203 * @vsi: the corresponding vsi 204 * @app: APP to delete 205 * 206 * Delete given APP from the DCBNL APP table for given 207 * VSI 208 **/ 209 static int i40e_dcbnl_vsi_del_app(struct i40e_vsi *vsi, 210 struct i40e_dcb_app_priority_table *app) 211 { 212 struct net_device *dev = vsi->netdev; 213 struct dcb_app sapp; 214 215 if (!dev) 216 return -EINVAL; 217 218 sapp.selector = app->selector; 219 sapp.protocol = app->protocolid; 220 sapp.priority = app->priority; 221 return dcb_ieee_delapp(dev, &sapp); 222 } 223 224 /** 225 * i40e_dcbnl_del_app - Delete APP on all VSIs 226 * @pf: the corresponding pf 227 * @app: APP to delete 228 * 229 * Delete given APP from all the VSIs for given PF 230 **/ 231 static void i40e_dcbnl_del_app(struct i40e_pf *pf, 232 struct i40e_dcb_app_priority_table *app) 233 { 234 int v, err; 235 for (v = 0; v < pf->num_alloc_vsi; v++) { 236 if (pf->vsi[v] && pf->vsi[v]->netdev) { 237 err = i40e_dcbnl_vsi_del_app(pf->vsi[v], app); 238 if (err) 239 dev_info(&pf->pdev->dev, "%s: Failed deleting app for VSI seid=%d err=%d sel=%d proto=0x%x prio=%d\n", 240 __func__, pf->vsi[v]->seid, 241 err, app->selector, 242 app->protocolid, app->priority); 243 } 244 } 245 } 246 247 /** 248 * i40e_dcbnl_find_app - Search APP in given DCB config 249 * @cfg: DCBX configuration data 250 * @app: APP to search for 251 * 252 * Find given APP in the DCB configuration 253 **/ 254 static bool i40e_dcbnl_find_app(struct i40e_dcbx_config *cfg, 255 struct i40e_dcb_app_priority_table *app) 256 { 257 int i; 258 259 for (i = 0; i < cfg->numapps; i++) { 260 if (app->selector == cfg->app[i].selector && 261 app->protocolid == cfg->app[i].protocolid && 262 app->priority == cfg->app[i].priority) 263 return true; 264 } 265 266 return false; 267 } 268 269 /** 270 * i40e_dcbnl_flush_apps - Delete all removed APPs 271 * @pf: the corresponding pf 272 * @new_cfg: new DCBX configuration data 273 * 274 * Find and delete all APPs that are not present in the passed 275 * DCB configuration 276 **/ 277 void i40e_dcbnl_flush_apps(struct i40e_pf *pf, 278 struct i40e_dcbx_config *new_cfg) 279 { 280 struct i40e_dcb_app_priority_table app; 281 struct i40e_dcbx_config *dcbxcfg; 282 struct i40e_hw *hw = &pf->hw; 283 int i; 284 285 dcbxcfg = &hw->local_dcbx_config; 286 for (i = 0; i < dcbxcfg->numapps; i++) { 287 app = dcbxcfg->app[i]; 288 /* The APP is not available anymore delete it */ 289 if (!i40e_dcbnl_find_app(new_cfg, &app)) 290 i40e_dcbnl_del_app(pf, &app); 291 } 292 } 293 294 /** 295 * i40e_dcbnl_setup - DCBNL setup 296 * @vsi: the corresponding vsi 297 * 298 * Set up DCBNL ops and initial APP TLVs 299 **/ 300 void i40e_dcbnl_setup(struct i40e_vsi *vsi) 301 { 302 struct net_device *dev = vsi->netdev; 303 struct i40e_pf *pf = i40e_netdev_to_pf(dev); 304 305 /* Not DCB capable */ 306 if (!(pf->flags & I40E_FLAG_DCB_CAPABLE)) 307 return; 308 309 /* Do not setup DCB NL ops for MFP mode */ 310 if (!(pf->flags & I40E_FLAG_MFP_ENABLED)) 311 dev->dcbnl_ops = &dcbnl_ops; 312 313 /* Set initial IEEE DCB settings */ 314 i40e_dcbnl_set_all(vsi); 315 } 316 #endif /* CONFIG_I40E_DCB */ 317