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 /* MFP mode but not an iSCSI PF so return */ 182 if ((pf->flags & I40E_FLAG_MFP_ENABLED) && !(pf->hw.func_caps.iscsi)) 183 return; 184 185 dcbxcfg = &hw->local_dcbx_config; 186 187 /* Set up all the App TLVs if DCBx is negotiated */ 188 for (i = 0; i < dcbxcfg->numapps; i++) { 189 prio = dcbxcfg->app[i].priority; 190 tc_map = BIT(dcbxcfg->etscfg.prioritytable[prio]); 191 192 /* Add APP only if the TC is enabled for this VSI */ 193 if (tc_map & vsi->tc_config.enabled_tc) { 194 sapp.selector = dcbxcfg->app[i].selector; 195 sapp.protocol = dcbxcfg->app[i].protocolid; 196 sapp.priority = prio; 197 dcb_ieee_setapp(dev, &sapp); 198 } 199 } 200 201 /* Notify user-space of the changes */ 202 dcbnl_ieee_notify(dev, RTM_SETDCB, DCB_CMD_IEEE_SET, 0, 0); 203 } 204 205 /** 206 * i40e_dcbnl_vsi_del_app - Delete APP for given VSI 207 * @vsi: the corresponding vsi 208 * @app: APP to delete 209 * 210 * Delete given APP from the DCBNL APP table for given 211 * VSI 212 **/ 213 static int i40e_dcbnl_vsi_del_app(struct i40e_vsi *vsi, 214 struct i40e_dcb_app_priority_table *app) 215 { 216 struct net_device *dev = vsi->netdev; 217 struct dcb_app sapp; 218 219 if (!dev) 220 return -EINVAL; 221 222 sapp.selector = app->selector; 223 sapp.protocol = app->protocolid; 224 sapp.priority = app->priority; 225 return dcb_ieee_delapp(dev, &sapp); 226 } 227 228 /** 229 * i40e_dcbnl_del_app - Delete APP on all VSIs 230 * @pf: the corresponding PF 231 * @app: APP to delete 232 * 233 * Delete given APP from all the VSIs for given PF 234 **/ 235 static void i40e_dcbnl_del_app(struct i40e_pf *pf, 236 struct i40e_dcb_app_priority_table *app) 237 { 238 int v, err; 239 240 for (v = 0; v < pf->num_alloc_vsi; v++) { 241 if (pf->vsi[v] && pf->vsi[v]->netdev) { 242 err = i40e_dcbnl_vsi_del_app(pf->vsi[v], app); 243 dev_dbg(&pf->pdev->dev, "Deleting app for VSI seid=%d err=%d sel=%d proto=0x%x prio=%d\n", 244 pf->vsi[v]->seid, err, app->selector, 245 app->protocolid, app->priority); 246 } 247 } 248 } 249 250 /** 251 * i40e_dcbnl_find_app - Search APP in given DCB config 252 * @cfg: DCBX configuration data 253 * @app: APP to search for 254 * 255 * Find given APP in the DCB configuration 256 **/ 257 static bool i40e_dcbnl_find_app(struct i40e_dcbx_config *cfg, 258 struct i40e_dcb_app_priority_table *app) 259 { 260 int i; 261 262 for (i = 0; i < cfg->numapps; i++) { 263 if (app->selector == cfg->app[i].selector && 264 app->protocolid == cfg->app[i].protocolid && 265 app->priority == cfg->app[i].priority) 266 return true; 267 } 268 269 return false; 270 } 271 272 /** 273 * i40e_dcbnl_flush_apps - Delete all removed APPs 274 * @pf: the corresponding PF 275 * @old_cfg: old DCBX configuration data 276 * @new_cfg: new DCBX configuration data 277 * 278 * Find and delete all APPs that are not present in the passed 279 * DCB configuration 280 **/ 281 void i40e_dcbnl_flush_apps(struct i40e_pf *pf, 282 struct i40e_dcbx_config *old_cfg, 283 struct i40e_dcbx_config *new_cfg) 284 { 285 struct i40e_dcb_app_priority_table app; 286 int i; 287 288 /* MFP mode but not an iSCSI PF so return */ 289 if ((pf->flags & I40E_FLAG_MFP_ENABLED) && !(pf->hw.func_caps.iscsi)) 290 return; 291 292 for (i = 0; i < old_cfg->numapps; i++) { 293 app = old_cfg->app[i]; 294 /* The APP is not available anymore delete it */ 295 if (!i40e_dcbnl_find_app(new_cfg, &app)) 296 i40e_dcbnl_del_app(pf, &app); 297 } 298 } 299 300 /** 301 * i40e_dcbnl_setup - DCBNL setup 302 * @vsi: the corresponding vsi 303 * 304 * Set up DCBNL ops and initial APP TLVs 305 **/ 306 void i40e_dcbnl_setup(struct i40e_vsi *vsi) 307 { 308 struct net_device *dev = vsi->netdev; 309 struct i40e_pf *pf = i40e_netdev_to_pf(dev); 310 311 /* Not DCB capable */ 312 if (!(pf->flags & I40E_FLAG_DCB_CAPABLE)) 313 return; 314 315 dev->dcbnl_ops = &dcbnl_ops; 316 317 /* Set initial IEEE DCB settings */ 318 i40e_dcbnl_set_all(vsi); 319 } 320 #endif /* CONFIG_I40E_DCB */ 321