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_set(pptb_pl, i, prio_tc[i]); 107 return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pptb), 108 pptb_pl); 109 } 110 111 static bool mlxsw_sp_ets_has_pg(u8 *prio_tc, u8 pg) 112 { 113 int i; 114 115 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) 116 if (prio_tc[i] == pg) 117 return true; 118 return false; 119 } 120 121 static int mlxsw_sp_port_pg_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 122 u8 *old_prio_tc, u8 *new_prio_tc) 123 { 124 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 125 char pbmc_pl[MLXSW_REG_PBMC_LEN]; 126 int err, i; 127 128 mlxsw_reg_pbmc_pack(pbmc_pl, mlxsw_sp_port->local_port, 0, 0); 129 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl); 130 if (err) 131 return err; 132 133 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 134 u8 pg = old_prio_tc[i]; 135 136 if (!mlxsw_sp_ets_has_pg(new_prio_tc, pg)) 137 mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, pg, 0); 138 } 139 140 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl); 141 } 142 143 static int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, 144 struct ieee_ets *ets) 145 { 146 bool pause_en = mlxsw_sp_port_is_pause_en(mlxsw_sp_port); 147 struct ieee_ets *my_ets = mlxsw_sp_port->dcb.ets; 148 struct net_device *dev = mlxsw_sp_port->dev; 149 int err; 150 151 /* Create the required PGs, but don't destroy existing ones, as 152 * traffic is still directed to them. 153 */ 154 err = __mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu, 155 ets->prio_tc, pause_en, 156 mlxsw_sp_port->dcb.pfc); 157 if (err) { 158 netdev_err(dev, "Failed to configure port's headroom\n"); 159 return err; 160 } 161 162 err = mlxsw_sp_port_pg_prio_map(mlxsw_sp_port, ets->prio_tc); 163 if (err) { 164 netdev_err(dev, "Failed to set PG-priority mapping\n"); 165 goto err_port_prio_pg_map; 166 } 167 168 err = mlxsw_sp_port_pg_destroy(mlxsw_sp_port, my_ets->prio_tc, 169 ets->prio_tc); 170 if (err) 171 netdev_warn(dev, "Failed to remove ununsed PGs\n"); 172 173 return 0; 174 175 err_port_prio_pg_map: 176 mlxsw_sp_port_pg_destroy(mlxsw_sp_port, ets->prio_tc, my_ets->prio_tc); 177 return err; 178 } 179 180 static int __mlxsw_sp_dcbnl_ieee_setets(struct mlxsw_sp_port *mlxsw_sp_port, 181 struct ieee_ets *ets) 182 { 183 struct ieee_ets *my_ets = mlxsw_sp_port->dcb.ets; 184 struct net_device *dev = mlxsw_sp_port->dev; 185 int i, err; 186 187 /* Egress configuration. */ 188 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 189 bool dwrr = ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS; 190 u8 weight = ets->tc_tx_bw[i]; 191 192 err = mlxsw_sp_port_ets_set(mlxsw_sp_port, 193 MLXSW_REG_QEEC_HIERARCY_SUBGROUP, i, 194 0, dwrr, weight); 195 if (err) { 196 netdev_err(dev, "Failed to link subgroup ETS element %d to group\n", 197 i); 198 goto err_port_ets_set; 199 } 200 } 201 202 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 203 err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, 204 ets->prio_tc[i]); 205 if (err) { 206 netdev_err(dev, "Failed to map prio %d to TC %d\n", i, 207 ets->prio_tc[i]); 208 goto err_port_prio_tc_set; 209 } 210 } 211 212 /* Ingress configuration. */ 213 err = mlxsw_sp_port_headroom_set(mlxsw_sp_port, ets); 214 if (err) 215 goto err_port_headroom_set; 216 217 return 0; 218 219 err_port_headroom_set: 220 i = IEEE_8021QAZ_MAX_TCS; 221 err_port_prio_tc_set: 222 for (i--; i >= 0; i--) 223 mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, my_ets->prio_tc[i]); 224 i = IEEE_8021QAZ_MAX_TCS; 225 err_port_ets_set: 226 for (i--; i >= 0; i--) { 227 bool dwrr = my_ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS; 228 u8 weight = my_ets->tc_tx_bw[i]; 229 230 err = mlxsw_sp_port_ets_set(mlxsw_sp_port, 231 MLXSW_REG_QEEC_HIERARCY_SUBGROUP, i, 232 0, dwrr, weight); 233 } 234 return err; 235 } 236 237 static int mlxsw_sp_dcbnl_ieee_setets(struct net_device *dev, 238 struct ieee_ets *ets) 239 { 240 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 241 int err; 242 243 err = mlxsw_sp_port_ets_validate(mlxsw_sp_port, ets); 244 if (err) 245 return err; 246 247 err = __mlxsw_sp_dcbnl_ieee_setets(mlxsw_sp_port, ets); 248 if (err) 249 return err; 250 251 memcpy(mlxsw_sp_port->dcb.ets, ets, sizeof(*ets)); 252 253 return 0; 254 } 255 256 static int mlxsw_sp_dcbnl_ieee_getmaxrate(struct net_device *dev, 257 struct ieee_maxrate *maxrate) 258 { 259 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 260 261 memcpy(maxrate, mlxsw_sp_port->dcb.maxrate, sizeof(*maxrate)); 262 263 return 0; 264 } 265 266 static int mlxsw_sp_dcbnl_ieee_setmaxrate(struct net_device *dev, 267 struct ieee_maxrate *maxrate) 268 { 269 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 270 struct ieee_maxrate *my_maxrate = mlxsw_sp_port->dcb.maxrate; 271 int err, i; 272 273 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 274 err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, 275 MLXSW_REG_QEEC_HIERARCY_SUBGROUP, 276 i, 0, 277 maxrate->tc_maxrate[i]); 278 if (err) { 279 netdev_err(dev, "Failed to set maxrate for TC %d\n", i); 280 goto err_port_ets_maxrate_set; 281 } 282 } 283 284 memcpy(mlxsw_sp_port->dcb.maxrate, maxrate, sizeof(*maxrate)); 285 286 return 0; 287 288 err_port_ets_maxrate_set: 289 for (i--; i >= 0; i--) 290 mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, 291 MLXSW_REG_QEEC_HIERARCY_SUBGROUP, 292 i, 0, my_maxrate->tc_maxrate[i]); 293 return err; 294 } 295 296 static int mlxsw_sp_port_pfc_cnt_get(struct mlxsw_sp_port *mlxsw_sp_port, 297 u8 prio) 298 { 299 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 300 struct ieee_pfc *my_pfc = mlxsw_sp_port->dcb.pfc; 301 char ppcnt_pl[MLXSW_REG_PPCNT_LEN]; 302 int err; 303 304 mlxsw_reg_ppcnt_pack(ppcnt_pl, mlxsw_sp_port->local_port, 305 MLXSW_REG_PPCNT_PRIO_CNT, prio); 306 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ppcnt), ppcnt_pl); 307 if (err) 308 return err; 309 310 my_pfc->requests[prio] = mlxsw_reg_ppcnt_tx_pause_get(ppcnt_pl); 311 my_pfc->indications[prio] = mlxsw_reg_ppcnt_rx_pause_get(ppcnt_pl); 312 313 return 0; 314 } 315 316 static int mlxsw_sp_dcbnl_ieee_getpfc(struct net_device *dev, 317 struct ieee_pfc *pfc) 318 { 319 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 320 int err, i; 321 322 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 323 err = mlxsw_sp_port_pfc_cnt_get(mlxsw_sp_port, i); 324 if (err) { 325 netdev_err(dev, "Failed to get PFC count for priority %d\n", 326 i); 327 return err; 328 } 329 } 330 331 memcpy(pfc, mlxsw_sp_port->dcb.pfc, sizeof(*pfc)); 332 333 return 0; 334 } 335 336 static int mlxsw_sp_port_pfc_set(struct mlxsw_sp_port *mlxsw_sp_port, 337 struct ieee_pfc *pfc) 338 { 339 char pfcc_pl[MLXSW_REG_PFCC_LEN]; 340 341 mlxsw_reg_pfcc_pack(pfcc_pl, mlxsw_sp_port->local_port); 342 mlxsw_reg_pfcc_prio_pack(pfcc_pl, pfc->pfc_en); 343 344 return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pfcc), 345 pfcc_pl); 346 } 347 348 static int mlxsw_sp_dcbnl_ieee_setpfc(struct net_device *dev, 349 struct ieee_pfc *pfc) 350 { 351 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); 352 int err; 353 354 if (mlxsw_sp_port->link.tx_pause || mlxsw_sp_port->link.rx_pause) { 355 netdev_err(dev, "PAUSE frames already enabled on port\n"); 356 return -EINVAL; 357 } 358 359 err = __mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu, 360 mlxsw_sp_port->dcb.ets->prio_tc, 361 false, pfc); 362 if (err) { 363 netdev_err(dev, "Failed to configure port's headroom for PFC\n"); 364 return err; 365 } 366 367 err = mlxsw_sp_port_pfc_set(mlxsw_sp_port, pfc); 368 if (err) { 369 netdev_err(dev, "Failed to configure PFC\n"); 370 goto err_port_pfc_set; 371 } 372 373 memcpy(mlxsw_sp_port->dcb.pfc, pfc, sizeof(*pfc)); 374 375 return 0; 376 377 err_port_pfc_set: 378 __mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu, 379 mlxsw_sp_port->dcb.ets->prio_tc, false, 380 mlxsw_sp_port->dcb.pfc); 381 return err; 382 } 383 384 static const struct dcbnl_rtnl_ops mlxsw_sp_dcbnl_ops = { 385 .ieee_getets = mlxsw_sp_dcbnl_ieee_getets, 386 .ieee_setets = mlxsw_sp_dcbnl_ieee_setets, 387 .ieee_getmaxrate = mlxsw_sp_dcbnl_ieee_getmaxrate, 388 .ieee_setmaxrate = mlxsw_sp_dcbnl_ieee_setmaxrate, 389 .ieee_getpfc = mlxsw_sp_dcbnl_ieee_getpfc, 390 .ieee_setpfc = mlxsw_sp_dcbnl_ieee_setpfc, 391 392 .getdcbx = mlxsw_sp_dcbnl_getdcbx, 393 .setdcbx = mlxsw_sp_dcbnl_setdcbx, 394 }; 395 396 static int mlxsw_sp_port_ets_init(struct mlxsw_sp_port *mlxsw_sp_port) 397 { 398 mlxsw_sp_port->dcb.ets = kzalloc(sizeof(*mlxsw_sp_port->dcb.ets), 399 GFP_KERNEL); 400 if (!mlxsw_sp_port->dcb.ets) 401 return -ENOMEM; 402 403 mlxsw_sp_port->dcb.ets->ets_cap = IEEE_8021QAZ_MAX_TCS; 404 405 return 0; 406 } 407 408 static void mlxsw_sp_port_ets_fini(struct mlxsw_sp_port *mlxsw_sp_port) 409 { 410 kfree(mlxsw_sp_port->dcb.ets); 411 } 412 413 static int mlxsw_sp_port_maxrate_init(struct mlxsw_sp_port *mlxsw_sp_port) 414 { 415 int i; 416 417 mlxsw_sp_port->dcb.maxrate = kmalloc(sizeof(*mlxsw_sp_port->dcb.maxrate), 418 GFP_KERNEL); 419 if (!mlxsw_sp_port->dcb.maxrate) 420 return -ENOMEM; 421 422 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) 423 mlxsw_sp_port->dcb.maxrate->tc_maxrate[i] = MLXSW_REG_QEEC_MAS_DIS; 424 425 return 0; 426 } 427 428 static void mlxsw_sp_port_maxrate_fini(struct mlxsw_sp_port *mlxsw_sp_port) 429 { 430 kfree(mlxsw_sp_port->dcb.maxrate); 431 } 432 433 static int mlxsw_sp_port_pfc_init(struct mlxsw_sp_port *mlxsw_sp_port) 434 { 435 mlxsw_sp_port->dcb.pfc = kzalloc(sizeof(*mlxsw_sp_port->dcb.pfc), 436 GFP_KERNEL); 437 if (!mlxsw_sp_port->dcb.pfc) 438 return -ENOMEM; 439 440 mlxsw_sp_port->dcb.pfc->pfc_cap = IEEE_8021QAZ_MAX_TCS; 441 442 return 0; 443 } 444 445 static void mlxsw_sp_port_pfc_fini(struct mlxsw_sp_port *mlxsw_sp_port) 446 { 447 kfree(mlxsw_sp_port->dcb.pfc); 448 } 449 450 int mlxsw_sp_port_dcb_init(struct mlxsw_sp_port *mlxsw_sp_port) 451 { 452 int err; 453 454 err = mlxsw_sp_port_ets_init(mlxsw_sp_port); 455 if (err) 456 return err; 457 err = mlxsw_sp_port_maxrate_init(mlxsw_sp_port); 458 if (err) 459 goto err_port_maxrate_init; 460 err = mlxsw_sp_port_pfc_init(mlxsw_sp_port); 461 if (err) 462 goto err_port_pfc_init; 463 464 mlxsw_sp_port->dev->dcbnl_ops = &mlxsw_sp_dcbnl_ops; 465 466 return 0; 467 468 err_port_pfc_init: 469 mlxsw_sp_port_maxrate_fini(mlxsw_sp_port); 470 err_port_maxrate_init: 471 mlxsw_sp_port_ets_fini(mlxsw_sp_port); 472 return err; 473 } 474 475 void mlxsw_sp_port_dcb_fini(struct mlxsw_sp_port *mlxsw_sp_port) 476 { 477 mlxsw_sp_port_pfc_fini(mlxsw_sp_port); 478 mlxsw_sp_port_maxrate_fini(mlxsw_sp_port); 479 mlxsw_sp_port_ets_fini(mlxsw_sp_port); 480 } 481