1 /* 2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c 3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved. 4 * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the names of the copyright holders nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * Alternatively, this software may be distributed under the terms of the 19 * GNU General Public License ("GPL") version 2 as published by the Free 20 * Software Foundation. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #include <linux/netdevice.h> 36 #include <linux/string.h> 37 #include <linux/bitops.h> 38 #include <net/dcbnl.h> 39 40 #include "spectrum.h" 41 #include "reg.h" 42 43 static u8 mlxsw_sp_dcbnl_getdcbx(struct net_device __always_unused *dev) 44 { 45 return DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE; 46 } 47 48 static u8 mlxsw_sp_dcbnl_setdcbx(struct net_device __always_unused *dev, 49 u8 mode) 50 { 51 return (mode != (DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE)) ? 1 : 0; 52 } 53 54 static int mlxsw_sp_dcbnl_ieee_getets(struct net_device *dev, 55 struct ieee_ets *ets) 56 { 57 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 58 59 memcpy(ets, mlxsw_sp_port->dcb.ets, sizeof(*ets)); 60 61 return 0; 62 } 63 64 static int mlxsw_sp_port_ets_validate(struct mlxsw_sp_port *mlxsw_sp_port, 65 struct ieee_ets *ets) 66 { 67 struct net_device *dev = mlxsw_sp_port->dev; 68 bool has_ets_tc = false; 69 int i, tx_bw_sum = 0; 70 71 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 72 switch (ets->tc_tsa[i]) { 73 case IEEE_8021QAZ_TSA_STRICT: 74 break; 75 case IEEE_8021QAZ_TSA_ETS: 76 has_ets_tc = true; 77 tx_bw_sum += ets->tc_tx_bw[i]; 78 break; 79 default: 80 netdev_err(dev, "Only strict priority and ETS are supported\n"); 81 return -EINVAL; 82 } 83 84 if (ets->prio_tc[i] >= IEEE_8021QAZ_MAX_TCS) { 85 netdev_err(dev, "Invalid TC\n"); 86 return -EINVAL; 87 } 88 } 89 90 if (has_ets_tc && tx_bw_sum != 100) { 91 netdev_err(dev, "Total ETS bandwidth should equal 100\n"); 92 return -EINVAL; 93 } 94 95 return 0; 96 } 97 98 static int mlxsw_sp_port_pg_prio_map(struct mlxsw_sp_port *mlxsw_sp_port, 99 u8 *prio_tc) 100 { 101 char pptb_pl[MLXSW_REG_PPTB_LEN]; 102 int i; 103 104 mlxsw_reg_pptb_pack(pptb_pl, mlxsw_sp_port->local_port); 105 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) 106 mlxsw_reg_pptb_prio_to_buff_pack(pptb_pl, i, prio_tc[i]); 107 108 return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pptb), 109 pptb_pl); 110 } 111 112 static bool mlxsw_sp_ets_has_pg(u8 *prio_tc, u8 pg) 113 { 114 int i; 115 116 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) 117 if (prio_tc[i] == pg) 118 return true; 119 return false; 120 } 121 122 static int mlxsw_sp_port_pg_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 123 u8 *old_prio_tc, u8 *new_prio_tc) 124 { 125 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 126 char pbmc_pl[MLXSW_REG_PBMC_LEN]; 127 int err, i; 128 129 mlxsw_reg_pbmc_pack(pbmc_pl, mlxsw_sp_port->local_port, 0, 0); 130 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl); 131 if (err) 132 return err; 133 134 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 135 u8 pg = old_prio_tc[i]; 136 137 if (!mlxsw_sp_ets_has_pg(new_prio_tc, pg)) 138 mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, pg, 0); 139 } 140 141 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl); 142 } 143 144 static int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, 145 struct ieee_ets *ets) 146 { 147 bool pause_en = mlxsw_sp_port_is_pause_en(mlxsw_sp_port); 148 struct ieee_ets *my_ets = mlxsw_sp_port->dcb.ets; 149 struct net_device *dev = mlxsw_sp_port->dev; 150 int err; 151 152 /* Create the required PGs, but don't destroy existing ones, as 153 * traffic is still directed to them. 154 */ 155 err = __mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu, 156 ets->prio_tc, pause_en, 157 mlxsw_sp_port->dcb.pfc); 158 if (err) { 159 netdev_err(dev, "Failed to configure port's headroom\n"); 160 return err; 161 } 162 163 err = mlxsw_sp_port_pg_prio_map(mlxsw_sp_port, ets->prio_tc); 164 if (err) { 165 netdev_err(dev, "Failed to set PG-priority mapping\n"); 166 goto err_port_prio_pg_map; 167 } 168 169 err = mlxsw_sp_port_pg_destroy(mlxsw_sp_port, my_ets->prio_tc, 170 ets->prio_tc); 171 if (err) 172 netdev_warn(dev, "Failed to remove ununsed PGs\n"); 173 174 return 0; 175 176 err_port_prio_pg_map: 177 mlxsw_sp_port_pg_destroy(mlxsw_sp_port, ets->prio_tc, my_ets->prio_tc); 178 return err; 179 } 180 181 static int __mlxsw_sp_dcbnl_ieee_setets(struct mlxsw_sp_port *mlxsw_sp_port, 182 struct ieee_ets *ets) 183 { 184 struct ieee_ets *my_ets = mlxsw_sp_port->dcb.ets; 185 struct net_device *dev = mlxsw_sp_port->dev; 186 int i, err; 187 188 /* Egress configuration. */ 189 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 190 bool dwrr = ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS; 191 u8 weight = ets->tc_tx_bw[i]; 192 193 err = mlxsw_sp_port_ets_set(mlxsw_sp_port, 194 MLXSW_REG_QEEC_HIERARCY_SUBGROUP, i, 195 0, dwrr, weight); 196 if (err) { 197 netdev_err(dev, "Failed to link subgroup ETS element %d to group\n", 198 i); 199 goto err_port_ets_set; 200 } 201 } 202 203 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 204 err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, 205 ets->prio_tc[i]); 206 if (err) { 207 netdev_err(dev, "Failed to map prio %d to TC %d\n", i, 208 ets->prio_tc[i]); 209 goto err_port_prio_tc_set; 210 } 211 } 212 213 /* Ingress configuration. */ 214 err = mlxsw_sp_port_headroom_set(mlxsw_sp_port, ets); 215 if (err) 216 goto err_port_headroom_set; 217 218 return 0; 219 220 err_port_headroom_set: 221 i = IEEE_8021QAZ_MAX_TCS; 222 err_port_prio_tc_set: 223 for (i--; i >= 0; i--) 224 mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, my_ets->prio_tc[i]); 225 i = IEEE_8021QAZ_MAX_TCS; 226 err_port_ets_set: 227 for (i--; i >= 0; i--) { 228 bool dwrr = my_ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS; 229 u8 weight = my_ets->tc_tx_bw[i]; 230 231 err = mlxsw_sp_port_ets_set(mlxsw_sp_port, 232 MLXSW_REG_QEEC_HIERARCY_SUBGROUP, i, 233 0, dwrr, weight); 234 } 235 return err; 236 } 237 238 static int mlxsw_sp_dcbnl_ieee_setets(struct net_device *dev, 239 struct ieee_ets *ets) 240 { 241 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 242 int err; 243 244 err = mlxsw_sp_port_ets_validate(mlxsw_sp_port, ets); 245 if (err) 246 return err; 247 248 err = __mlxsw_sp_dcbnl_ieee_setets(mlxsw_sp_port, ets); 249 if (err) 250 return err; 251 252 memcpy(mlxsw_sp_port->dcb.ets, ets, sizeof(*ets)); 253 mlxsw_sp_port->dcb.ets->ets_cap = IEEE_8021QAZ_MAX_TCS; 254 255 return 0; 256 } 257 258 static int mlxsw_sp_dcbnl_ieee_getmaxrate(struct net_device *dev, 259 struct ieee_maxrate *maxrate) 260 { 261 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 262 263 memcpy(maxrate, mlxsw_sp_port->dcb.maxrate, sizeof(*maxrate)); 264 265 return 0; 266 } 267 268 static int mlxsw_sp_dcbnl_ieee_setmaxrate(struct net_device *dev, 269 struct ieee_maxrate *maxrate) 270 { 271 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 272 struct ieee_maxrate *my_maxrate = mlxsw_sp_port->dcb.maxrate; 273 int err, i; 274 275 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 276 err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, 277 MLXSW_REG_QEEC_HIERARCY_SUBGROUP, 278 i, 0, 279 maxrate->tc_maxrate[i]); 280 if (err) { 281 netdev_err(dev, "Failed to set maxrate for TC %d\n", i); 282 goto err_port_ets_maxrate_set; 283 } 284 } 285 286 memcpy(mlxsw_sp_port->dcb.maxrate, maxrate, sizeof(*maxrate)); 287 288 return 0; 289 290 err_port_ets_maxrate_set: 291 for (i--; i >= 0; i--) 292 mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, 293 MLXSW_REG_QEEC_HIERARCY_SUBGROUP, 294 i, 0, my_maxrate->tc_maxrate[i]); 295 return err; 296 } 297 298 static int mlxsw_sp_port_pfc_cnt_get(struct mlxsw_sp_port *mlxsw_sp_port, 299 u8 prio) 300 { 301 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 302 struct ieee_pfc *my_pfc = mlxsw_sp_port->dcb.pfc; 303 char ppcnt_pl[MLXSW_REG_PPCNT_LEN]; 304 int err; 305 306 mlxsw_reg_ppcnt_pack(ppcnt_pl, mlxsw_sp_port->local_port, 307 MLXSW_REG_PPCNT_PRIO_CNT, prio); 308 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ppcnt), ppcnt_pl); 309 if (err) 310 return err; 311 312 my_pfc->requests[prio] = mlxsw_reg_ppcnt_tx_pause_get(ppcnt_pl); 313 my_pfc->indications[prio] = mlxsw_reg_ppcnt_rx_pause_get(ppcnt_pl); 314 315 return 0; 316 } 317 318 static int mlxsw_sp_dcbnl_ieee_getpfc(struct net_device *dev, 319 struct ieee_pfc *pfc) 320 { 321 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 322 int err, i; 323 324 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 325 err = mlxsw_sp_port_pfc_cnt_get(mlxsw_sp_port, i); 326 if (err) { 327 netdev_err(dev, "Failed to get PFC count for priority %d\n", 328 i); 329 return err; 330 } 331 } 332 333 memcpy(pfc, mlxsw_sp_port->dcb.pfc, sizeof(*pfc)); 334 335 return 0; 336 } 337 338 static int mlxsw_sp_port_pfc_set(struct mlxsw_sp_port *mlxsw_sp_port, 339 struct ieee_pfc *pfc) 340 { 341 char pfcc_pl[MLXSW_REG_PFCC_LEN]; 342 343 mlxsw_reg_pfcc_pack(pfcc_pl, mlxsw_sp_port->local_port); 344 mlxsw_reg_pfcc_pprx_set(pfcc_pl, mlxsw_sp_port->link.rx_pause); 345 mlxsw_reg_pfcc_pptx_set(pfcc_pl, mlxsw_sp_port->link.tx_pause); 346 mlxsw_reg_pfcc_prio_pack(pfcc_pl, pfc->pfc_en); 347 348 return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pfcc), 349 pfcc_pl); 350 } 351 352 static int mlxsw_sp_dcbnl_ieee_setpfc(struct net_device *dev, 353 struct ieee_pfc *pfc) 354 { 355 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 356 bool pause_en = mlxsw_sp_port_is_pause_en(mlxsw_sp_port); 357 int err; 358 359 if (pause_en && pfc->pfc_en) { 360 netdev_err(dev, "PAUSE frames already enabled on port\n"); 361 return -EINVAL; 362 } 363 364 err = __mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu, 365 mlxsw_sp_port->dcb.ets->prio_tc, 366 pause_en, pfc); 367 if (err) { 368 netdev_err(dev, "Failed to configure port's headroom for PFC\n"); 369 return err; 370 } 371 372 err = mlxsw_sp_port_pfc_set(mlxsw_sp_port, pfc); 373 if (err) { 374 netdev_err(dev, "Failed to configure PFC\n"); 375 goto err_port_pfc_set; 376 } 377 378 memcpy(mlxsw_sp_port->dcb.pfc, pfc, sizeof(*pfc)); 379 mlxsw_sp_port->dcb.pfc->pfc_cap = IEEE_8021QAZ_MAX_TCS; 380 381 return 0; 382 383 err_port_pfc_set: 384 __mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu, 385 mlxsw_sp_port->dcb.ets->prio_tc, pause_en, 386 mlxsw_sp_port->dcb.pfc); 387 return err; 388 } 389 390 static const struct dcbnl_rtnl_ops mlxsw_sp_dcbnl_ops = { 391 .ieee_getets = mlxsw_sp_dcbnl_ieee_getets, 392 .ieee_setets = mlxsw_sp_dcbnl_ieee_setets, 393 .ieee_getmaxrate = mlxsw_sp_dcbnl_ieee_getmaxrate, 394 .ieee_setmaxrate = mlxsw_sp_dcbnl_ieee_setmaxrate, 395 .ieee_getpfc = mlxsw_sp_dcbnl_ieee_getpfc, 396 .ieee_setpfc = mlxsw_sp_dcbnl_ieee_setpfc, 397 398 .getdcbx = mlxsw_sp_dcbnl_getdcbx, 399 .setdcbx = mlxsw_sp_dcbnl_setdcbx, 400 }; 401 402 static int mlxsw_sp_port_ets_init(struct mlxsw_sp_port *mlxsw_sp_port) 403 { 404 mlxsw_sp_port->dcb.ets = kzalloc(sizeof(*mlxsw_sp_port->dcb.ets), 405 GFP_KERNEL); 406 if (!mlxsw_sp_port->dcb.ets) 407 return -ENOMEM; 408 409 mlxsw_sp_port->dcb.ets->ets_cap = IEEE_8021QAZ_MAX_TCS; 410 411 return 0; 412 } 413 414 static void mlxsw_sp_port_ets_fini(struct mlxsw_sp_port *mlxsw_sp_port) 415 { 416 kfree(mlxsw_sp_port->dcb.ets); 417 } 418 419 static int mlxsw_sp_port_maxrate_init(struct mlxsw_sp_port *mlxsw_sp_port) 420 { 421 int i; 422 423 mlxsw_sp_port->dcb.maxrate = kmalloc(sizeof(*mlxsw_sp_port->dcb.maxrate), 424 GFP_KERNEL); 425 if (!mlxsw_sp_port->dcb.maxrate) 426 return -ENOMEM; 427 428 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) 429 mlxsw_sp_port->dcb.maxrate->tc_maxrate[i] = MLXSW_REG_QEEC_MAS_DIS; 430 431 return 0; 432 } 433 434 static void mlxsw_sp_port_maxrate_fini(struct mlxsw_sp_port *mlxsw_sp_port) 435 { 436 kfree(mlxsw_sp_port->dcb.maxrate); 437 } 438 439 static int mlxsw_sp_port_pfc_init(struct mlxsw_sp_port *mlxsw_sp_port) 440 { 441 mlxsw_sp_port->dcb.pfc = kzalloc(sizeof(*mlxsw_sp_port->dcb.pfc), 442 GFP_KERNEL); 443 if (!mlxsw_sp_port->dcb.pfc) 444 return -ENOMEM; 445 446 mlxsw_sp_port->dcb.pfc->pfc_cap = IEEE_8021QAZ_MAX_TCS; 447 448 return 0; 449 } 450 451 static void mlxsw_sp_port_pfc_fini(struct mlxsw_sp_port *mlxsw_sp_port) 452 { 453 kfree(mlxsw_sp_port->dcb.pfc); 454 } 455 456 int mlxsw_sp_port_dcb_init(struct mlxsw_sp_port *mlxsw_sp_port) 457 { 458 int err; 459 460 err = mlxsw_sp_port_ets_init(mlxsw_sp_port); 461 if (err) 462 return err; 463 err = mlxsw_sp_port_maxrate_init(mlxsw_sp_port); 464 if (err) 465 goto err_port_maxrate_init; 466 err = mlxsw_sp_port_pfc_init(mlxsw_sp_port); 467 if (err) 468 goto err_port_pfc_init; 469 470 mlxsw_sp_port->dev->dcbnl_ops = &mlxsw_sp_dcbnl_ops; 471 472 return 0; 473 474 err_port_pfc_init: 475 mlxsw_sp_port_maxrate_fini(mlxsw_sp_port); 476 err_port_maxrate_init: 477 mlxsw_sp_port_ets_fini(mlxsw_sp_port); 478 return err; 479 } 480 481 void mlxsw_sp_port_dcb_fini(struct mlxsw_sp_port *mlxsw_sp_port) 482 { 483 mlxsw_sp_port_pfc_fini(mlxsw_sp_port); 484 mlxsw_sp_port_maxrate_fini(mlxsw_sp_port); 485 mlxsw_sp_port_ets_fini(mlxsw_sp_port); 486 } 487