1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2 /* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */ 3 4 #include <linux/kernel.h> 5 #include <linux/errno.h> 6 #include <linux/netdevice.h> 7 #include <net/pkt_cls.h> 8 #include <net/red.h> 9 10 #include "spectrum.h" 11 #include "reg.h" 12 13 #define MLXSW_SP_PRIO_BAND_TO_TCLASS(band) (IEEE_8021QAZ_MAX_TCS - band - 1) 14 #define MLXSW_SP_PRIO_CHILD_TO_TCLASS(child) \ 15 MLXSW_SP_PRIO_BAND_TO_TCLASS((child - 1)) 16 17 enum mlxsw_sp_qdisc_type { 18 MLXSW_SP_QDISC_NO_QDISC, 19 MLXSW_SP_QDISC_RED, 20 MLXSW_SP_QDISC_PRIO, 21 MLXSW_SP_QDISC_ETS, 22 MLXSW_SP_QDISC_TBF, 23 MLXSW_SP_QDISC_FIFO, 24 }; 25 26 struct mlxsw_sp_qdisc; 27 28 struct mlxsw_sp_qdisc_ops { 29 enum mlxsw_sp_qdisc_type type; 30 int (*check_params)(struct mlxsw_sp_port *mlxsw_sp_port, 31 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 32 void *params); 33 int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 34 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params); 35 int (*destroy)(struct mlxsw_sp_port *mlxsw_sp_port, 36 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc); 37 int (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port, 38 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 39 struct tc_qopt_offload_stats *stats_ptr); 40 int (*get_xstats)(struct mlxsw_sp_port *mlxsw_sp_port, 41 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 42 void *xstats_ptr); 43 void (*clean_stats)(struct mlxsw_sp_port *mlxsw_sp_port, 44 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc); 45 /* unoffload - to be used for a qdisc that stops being offloaded without 46 * being destroyed. 47 */ 48 void (*unoffload)(struct mlxsw_sp_port *mlxsw_sp_port, 49 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params); 50 }; 51 52 struct mlxsw_sp_qdisc { 53 u32 handle; 54 u8 tclass_num; 55 u8 prio_bitmap; 56 union { 57 struct red_stats red; 58 } xstats_base; 59 struct mlxsw_sp_qdisc_stats { 60 u64 tx_bytes; 61 u64 tx_packets; 62 u64 drops; 63 u64 overlimits; 64 u64 backlog; 65 } stats_base; 66 67 struct mlxsw_sp_qdisc_ops *ops; 68 }; 69 70 struct mlxsw_sp_qdisc_state { 71 struct mlxsw_sp_qdisc root_qdisc; 72 struct mlxsw_sp_qdisc tclass_qdiscs[IEEE_8021QAZ_MAX_TCS]; 73 74 /* When a PRIO or ETS are added, the invisible FIFOs in their bands are 75 * created first. When notifications for these FIFOs arrive, it is not 76 * known what qdisc their parent handle refers to. It could be a 77 * newly-created PRIO that will replace the currently-offloaded one, or 78 * it could be e.g. a RED that will be attached below it. 79 * 80 * As the notifications start to arrive, use them to note what the 81 * future parent handle is, and keep track of which child FIFOs were 82 * seen. Then when the parent is known, retroactively offload those 83 * FIFOs. 84 */ 85 u32 future_handle; 86 bool future_fifos[IEEE_8021QAZ_MAX_TCS]; 87 }; 88 89 static bool 90 mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle, 91 enum mlxsw_sp_qdisc_type type) 92 { 93 return mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 94 mlxsw_sp_qdisc->ops->type == type && 95 mlxsw_sp_qdisc->handle == handle; 96 } 97 98 static struct mlxsw_sp_qdisc * 99 mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent, 100 bool root_only) 101 { 102 struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 103 int tclass, child_index; 104 105 if (parent == TC_H_ROOT) 106 return &qdisc_state->root_qdisc; 107 108 if (root_only || !qdisc_state || 109 !qdisc_state->root_qdisc.ops || 110 TC_H_MAJ(parent) != qdisc_state->root_qdisc.handle || 111 TC_H_MIN(parent) > IEEE_8021QAZ_MAX_TCS) 112 return NULL; 113 114 child_index = TC_H_MIN(parent); 115 tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index); 116 return &qdisc_state->tclass_qdiscs[tclass]; 117 } 118 119 static struct mlxsw_sp_qdisc * 120 mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle) 121 { 122 struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 123 int i; 124 125 if (qdisc_state->root_qdisc.handle == handle) 126 return &qdisc_state->root_qdisc; 127 128 if (qdisc_state->root_qdisc.handle == TC_H_UNSPEC) 129 return NULL; 130 131 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) 132 if (qdisc_state->tclass_qdiscs[i].handle == handle) 133 return &qdisc_state->tclass_qdiscs[i]; 134 135 return NULL; 136 } 137 138 static int 139 mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 140 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 141 { 142 int err = 0; 143 144 if (!mlxsw_sp_qdisc) 145 return 0; 146 147 if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->destroy) 148 err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port, 149 mlxsw_sp_qdisc); 150 151 mlxsw_sp_qdisc->handle = TC_H_UNSPEC; 152 mlxsw_sp_qdisc->ops = NULL; 153 return err; 154 } 155 156 static int 157 mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 158 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 159 struct mlxsw_sp_qdisc_ops *ops, void *params) 160 { 161 int err; 162 163 if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type) 164 /* In case this location contained a different qdisc of the 165 * same type we can override the old qdisc configuration. 166 * Otherwise, we need to remove the old qdisc before setting the 167 * new one. 168 */ 169 mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 170 err = ops->check_params(mlxsw_sp_port, mlxsw_sp_qdisc, params); 171 if (err) 172 goto err_bad_param; 173 174 err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params); 175 if (err) 176 goto err_config; 177 178 /* Check if the Qdisc changed. That includes a situation where an 179 * invisible Qdisc replaces another one, or is being added for the 180 * first time. 181 */ 182 if (mlxsw_sp_qdisc->handle != handle || handle == TC_H_UNSPEC) { 183 mlxsw_sp_qdisc->ops = ops; 184 if (ops->clean_stats) 185 ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc); 186 } 187 188 mlxsw_sp_qdisc->handle = handle; 189 return 0; 190 191 err_bad_param: 192 err_config: 193 if (mlxsw_sp_qdisc->handle == handle && ops->unoffload) 194 ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params); 195 196 mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 197 return err; 198 } 199 200 static int 201 mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port, 202 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 203 struct tc_qopt_offload_stats *stats_ptr) 204 { 205 if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 206 mlxsw_sp_qdisc->ops->get_stats) 207 return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port, 208 mlxsw_sp_qdisc, 209 stats_ptr); 210 211 return -EOPNOTSUPP; 212 } 213 214 static int 215 mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port, 216 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 217 void *xstats_ptr) 218 { 219 if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 220 mlxsw_sp_qdisc->ops->get_xstats) 221 return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port, 222 mlxsw_sp_qdisc, 223 xstats_ptr); 224 225 return -EOPNOTSUPP; 226 } 227 228 static u64 229 mlxsw_sp_xstats_backlog(struct mlxsw_sp_port_xstats *xstats, int tclass_num) 230 { 231 return xstats->backlog[tclass_num] + 232 xstats->backlog[tclass_num + 8]; 233 } 234 235 static u64 236 mlxsw_sp_xstats_tail_drop(struct mlxsw_sp_port_xstats *xstats, int tclass_num) 237 { 238 return xstats->tail_drop[tclass_num] + 239 xstats->tail_drop[tclass_num + 8]; 240 } 241 242 static void 243 mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats, 244 u8 prio_bitmap, u64 *tx_packets, 245 u64 *tx_bytes) 246 { 247 int i; 248 249 *tx_packets = 0; 250 *tx_bytes = 0; 251 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 252 if (prio_bitmap & BIT(i)) { 253 *tx_packets += xstats->tx_packets[i]; 254 *tx_bytes += xstats->tx_bytes[i]; 255 } 256 } 257 } 258 259 static void 260 mlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port, 261 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 262 u64 *p_tx_bytes, u64 *p_tx_packets, 263 u64 *p_drops, u64 *p_backlog) 264 { 265 u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 266 struct mlxsw_sp_port_xstats *xstats; 267 u64 tx_bytes, tx_packets; 268 269 xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 270 mlxsw_sp_qdisc_bstats_per_priority_get(xstats, 271 mlxsw_sp_qdisc->prio_bitmap, 272 &tx_packets, &tx_bytes); 273 274 *p_tx_packets += tx_packets; 275 *p_tx_bytes += tx_bytes; 276 *p_drops += xstats->wred_drop[tclass_num] + 277 mlxsw_sp_xstats_tail_drop(xstats, tclass_num); 278 *p_backlog += mlxsw_sp_xstats_backlog(xstats, tclass_num); 279 } 280 281 static void 282 mlxsw_sp_qdisc_update_stats(struct mlxsw_sp *mlxsw_sp, 283 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 284 u64 tx_bytes, u64 tx_packets, 285 u64 drops, u64 backlog, 286 struct tc_qopt_offload_stats *stats_ptr) 287 { 288 struct mlxsw_sp_qdisc_stats *stats_base = &mlxsw_sp_qdisc->stats_base; 289 290 tx_bytes -= stats_base->tx_bytes; 291 tx_packets -= stats_base->tx_packets; 292 drops -= stats_base->drops; 293 backlog -= stats_base->backlog; 294 295 _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets); 296 stats_ptr->qstats->drops += drops; 297 stats_ptr->qstats->backlog += mlxsw_sp_cells_bytes(mlxsw_sp, backlog); 298 299 stats_base->backlog += backlog; 300 stats_base->drops += drops; 301 stats_base->tx_bytes += tx_bytes; 302 stats_base->tx_packets += tx_packets; 303 } 304 305 static void 306 mlxsw_sp_qdisc_get_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port, 307 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 308 struct tc_qopt_offload_stats *stats_ptr) 309 { 310 u64 tx_packets = 0; 311 u64 tx_bytes = 0; 312 u64 backlog = 0; 313 u64 drops = 0; 314 315 mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 316 &tx_bytes, &tx_packets, 317 &drops, &backlog); 318 mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc, 319 tx_bytes, tx_packets, drops, backlog, 320 stats_ptr); 321 } 322 323 static int 324 mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port, 325 int tclass_num, u32 min, u32 max, 326 u32 probability, bool is_ecn) 327 { 328 char cwtpm_cmd[MLXSW_REG_CWTPM_LEN]; 329 char cwtp_cmd[MLXSW_REG_CWTP_LEN]; 330 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 331 int err; 332 333 mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num); 334 mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE, 335 roundup(min, MLXSW_REG_CWTP_MIN_VALUE), 336 roundup(max, MLXSW_REG_CWTP_MIN_VALUE), 337 probability); 338 339 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd); 340 if (err) 341 return err; 342 343 mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num, 344 MLXSW_REG_CWTP_DEFAULT_PROFILE, true, is_ecn); 345 346 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd); 347 } 348 349 static int 350 mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port, 351 int tclass_num) 352 { 353 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 354 char cwtpm_cmd[MLXSW_REG_CWTPM_LEN]; 355 356 mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num, 357 MLXSW_REG_CWTPM_RESET_PROFILE, false, false); 358 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd); 359 } 360 361 static void 362 mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 363 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 364 { 365 u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 366 struct mlxsw_sp_qdisc_stats *stats_base; 367 struct mlxsw_sp_port_xstats *xstats; 368 struct red_stats *red_base; 369 370 xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 371 stats_base = &mlxsw_sp_qdisc->stats_base; 372 red_base = &mlxsw_sp_qdisc->xstats_base.red; 373 374 mlxsw_sp_qdisc_bstats_per_priority_get(xstats, 375 mlxsw_sp_qdisc->prio_bitmap, 376 &stats_base->tx_packets, 377 &stats_base->tx_bytes); 378 red_base->prob_drop = xstats->wred_drop[tclass_num]; 379 red_base->pdrop = mlxsw_sp_xstats_tail_drop(xstats, tclass_num); 380 381 stats_base->overlimits = red_base->prob_drop + red_base->prob_mark; 382 stats_base->drops = red_base->prob_drop + red_base->pdrop; 383 384 stats_base->backlog = 0; 385 } 386 387 static int 388 mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 389 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 390 { 391 struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 392 struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc; 393 394 if (root_qdisc != mlxsw_sp_qdisc) 395 root_qdisc->stats_base.backlog -= 396 mlxsw_sp_qdisc->stats_base.backlog; 397 398 return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port, 399 mlxsw_sp_qdisc->tclass_num); 400 } 401 402 static int 403 mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 404 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 405 void *params) 406 { 407 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 408 struct tc_red_qopt_offload_params *p = params; 409 410 if (p->min > p->max) { 411 dev_err(mlxsw_sp->bus_info->dev, 412 "spectrum: RED: min %u is bigger then max %u\n", p->min, 413 p->max); 414 return -EINVAL; 415 } 416 if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core, 417 GUARANTEED_SHARED_BUFFER)) { 418 dev_err(mlxsw_sp->bus_info->dev, 419 "spectrum: RED: max value %u is too big\n", p->max); 420 return -EINVAL; 421 } 422 if (p->min == 0 || p->max == 0) { 423 dev_err(mlxsw_sp->bus_info->dev, 424 "spectrum: RED: 0 value is illegal for min and max\n"); 425 return -EINVAL; 426 } 427 return 0; 428 } 429 430 static int 431 mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 432 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 433 void *params) 434 { 435 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 436 struct tc_red_qopt_offload_params *p = params; 437 u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 438 u32 min, max; 439 u64 prob; 440 441 /* calculate probability in percentage */ 442 prob = p->probability; 443 prob *= 100; 444 prob = DIV_ROUND_UP(prob, 1 << 16); 445 prob = DIV_ROUND_UP(prob, 1 << 16); 446 min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min); 447 max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max); 448 return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num, min, 449 max, prob, p->is_ecn); 450 } 451 452 static void 453 mlxsw_sp_qdisc_leaf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 454 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 455 struct gnet_stats_queue *qstats) 456 { 457 u64 backlog; 458 459 backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 460 mlxsw_sp_qdisc->stats_base.backlog); 461 qstats->backlog -= backlog; 462 mlxsw_sp_qdisc->stats_base.backlog = 0; 463 } 464 465 static void 466 mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 467 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 468 void *params) 469 { 470 struct tc_red_qopt_offload_params *p = params; 471 472 mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats); 473 } 474 475 static int 476 mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port, 477 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 478 void *xstats_ptr) 479 { 480 struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red; 481 u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 482 struct mlxsw_sp_port_xstats *xstats; 483 struct red_stats *res = xstats_ptr; 484 int early_drops, pdrops; 485 486 xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 487 488 early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop; 489 pdrops = mlxsw_sp_xstats_tail_drop(xstats, tclass_num) - 490 xstats_base->pdrop; 491 492 res->pdrop += pdrops; 493 res->prob_drop += early_drops; 494 495 xstats_base->pdrop += pdrops; 496 xstats_base->prob_drop += early_drops; 497 return 0; 498 } 499 500 static int 501 mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port, 502 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 503 struct tc_qopt_offload_stats *stats_ptr) 504 { 505 u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 506 struct mlxsw_sp_qdisc_stats *stats_base; 507 struct mlxsw_sp_port_xstats *xstats; 508 u64 overlimits; 509 510 xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 511 stats_base = &mlxsw_sp_qdisc->stats_base; 512 513 mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, stats_ptr); 514 overlimits = xstats->wred_drop[tclass_num] - stats_base->overlimits; 515 516 stats_ptr->qstats->overlimits += overlimits; 517 stats_base->overlimits += overlimits; 518 519 return 0; 520 } 521 522 #define MLXSW_SP_PORT_DEFAULT_TCLASS 0 523 524 static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = { 525 .type = MLXSW_SP_QDISC_RED, 526 .check_params = mlxsw_sp_qdisc_red_check_params, 527 .replace = mlxsw_sp_qdisc_red_replace, 528 .unoffload = mlxsw_sp_qdisc_red_unoffload, 529 .destroy = mlxsw_sp_qdisc_red_destroy, 530 .get_stats = mlxsw_sp_qdisc_get_red_stats, 531 .get_xstats = mlxsw_sp_qdisc_get_red_xstats, 532 .clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats, 533 }; 534 535 int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, 536 struct tc_red_qopt_offload *p) 537 { 538 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 539 540 mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); 541 if (!mlxsw_sp_qdisc) 542 return -EOPNOTSUPP; 543 544 if (p->command == TC_RED_REPLACE) 545 return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 546 mlxsw_sp_qdisc, 547 &mlxsw_sp_qdisc_ops_red, 548 &p->set); 549 550 if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 551 MLXSW_SP_QDISC_RED)) 552 return -EOPNOTSUPP; 553 554 switch (p->command) { 555 case TC_RED_DESTROY: 556 return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 557 case TC_RED_XSTATS: 558 return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc, 559 p->xstats); 560 case TC_RED_STATS: 561 return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 562 &p->stats); 563 default: 564 return -EOPNOTSUPP; 565 } 566 } 567 568 static void 569 mlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 570 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 571 { 572 u64 backlog_cells = 0; 573 u64 tx_packets = 0; 574 u64 tx_bytes = 0; 575 u64 drops = 0; 576 577 mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 578 &tx_bytes, &tx_packets, 579 &drops, &backlog_cells); 580 581 mlxsw_sp_qdisc->stats_base.tx_packets = tx_packets; 582 mlxsw_sp_qdisc->stats_base.tx_bytes = tx_bytes; 583 mlxsw_sp_qdisc->stats_base.drops = drops; 584 mlxsw_sp_qdisc->stats_base.backlog = 0; 585 } 586 587 static int 588 mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 589 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 590 { 591 struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 592 struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc; 593 594 if (root_qdisc != mlxsw_sp_qdisc) 595 root_qdisc->stats_base.backlog -= 596 mlxsw_sp_qdisc->stats_base.backlog; 597 598 return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, 599 MLXSW_REG_QEEC_HR_SUBGROUP, 600 mlxsw_sp_qdisc->tclass_num, 0, 601 MLXSW_REG_QEEC_MAS_DIS, 0); 602 } 603 604 static int 605 mlxsw_sp_qdisc_tbf_bs(struct mlxsw_sp_port *mlxsw_sp_port, 606 u32 max_size, u8 *p_burst_size) 607 { 608 /* TBF burst size is configured in bytes. The ASIC burst size value is 609 * ((2 ^ bs) * 512 bits. Convert the TBF bytes to 512-bit units. 610 */ 611 u32 bs512 = max_size / 64; 612 u8 bs = fls(bs512); 613 614 if (!bs) 615 return -EINVAL; 616 --bs; 617 618 /* Demand a power of two. */ 619 if ((1 << bs) != bs512) 620 return -EINVAL; 621 622 if (bs < mlxsw_sp_port->mlxsw_sp->lowest_shaper_bs || 623 bs > MLXSW_REG_QEEC_HIGHEST_SHAPER_BS) 624 return -EINVAL; 625 626 *p_burst_size = bs; 627 return 0; 628 } 629 630 static u32 631 mlxsw_sp_qdisc_tbf_max_size(u8 bs) 632 { 633 return (1U << bs) * 64; 634 } 635 636 static u64 637 mlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params *p) 638 { 639 /* TBF interface is in bytes/s, whereas Spectrum ASIC is configured in 640 * Kbits/s. 641 */ 642 return div_u64(p->rate.rate_bytes_ps, 1000) * 8; 643 } 644 645 static int 646 mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 647 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 648 void *params) 649 { 650 struct tc_tbf_qopt_offload_replace_params *p = params; 651 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 652 u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p); 653 u8 burst_size; 654 int err; 655 656 if (rate_kbps >= MLXSW_REG_QEEC_MAS_DIS) { 657 dev_err(mlxsw_sp_port->mlxsw_sp->bus_info->dev, 658 "spectrum: TBF: rate of %lluKbps must be below %u\n", 659 rate_kbps, MLXSW_REG_QEEC_MAS_DIS); 660 return -EINVAL; 661 } 662 663 err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size); 664 if (err) { 665 u8 highest_shaper_bs = MLXSW_REG_QEEC_HIGHEST_SHAPER_BS; 666 667 dev_err(mlxsw_sp->bus_info->dev, 668 "spectrum: TBF: invalid burst size of %u, must be a power of two between %u and %u", 669 p->max_size, 670 mlxsw_sp_qdisc_tbf_max_size(mlxsw_sp->lowest_shaper_bs), 671 mlxsw_sp_qdisc_tbf_max_size(highest_shaper_bs)); 672 return -EINVAL; 673 } 674 675 return 0; 676 } 677 678 static int 679 mlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 680 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 681 void *params) 682 { 683 struct tc_tbf_qopt_offload_replace_params *p = params; 684 u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p); 685 u8 burst_size; 686 int err; 687 688 err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size); 689 if (WARN_ON_ONCE(err)) 690 /* check_params above was supposed to reject this value. */ 691 return -EINVAL; 692 693 /* Configure subgroup shaper, so that both UC and MC traffic is subject 694 * to shaping. That is unlike RED, however UC queue lengths are going to 695 * be different than MC ones due to different pool and quota 696 * configurations, so the configuration is not applicable. For shaper on 697 * the other hand, subjecting the overall stream to the configured 698 * shaper makes sense. Also note that that is what we do for 699 * ieee_setmaxrate(). 700 */ 701 return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, 702 MLXSW_REG_QEEC_HR_SUBGROUP, 703 mlxsw_sp_qdisc->tclass_num, 0, 704 rate_kbps, burst_size); 705 } 706 707 static void 708 mlxsw_sp_qdisc_tbf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 709 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 710 void *params) 711 { 712 struct tc_tbf_qopt_offload_replace_params *p = params; 713 714 mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats); 715 } 716 717 static int 718 mlxsw_sp_qdisc_get_tbf_stats(struct mlxsw_sp_port *mlxsw_sp_port, 719 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 720 struct tc_qopt_offload_stats *stats_ptr) 721 { 722 mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 723 stats_ptr); 724 return 0; 725 } 726 727 static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_tbf = { 728 .type = MLXSW_SP_QDISC_TBF, 729 .check_params = mlxsw_sp_qdisc_tbf_check_params, 730 .replace = mlxsw_sp_qdisc_tbf_replace, 731 .unoffload = mlxsw_sp_qdisc_tbf_unoffload, 732 .destroy = mlxsw_sp_qdisc_tbf_destroy, 733 .get_stats = mlxsw_sp_qdisc_get_tbf_stats, 734 .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, 735 }; 736 737 int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, 738 struct tc_tbf_qopt_offload *p) 739 { 740 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 741 742 mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); 743 if (!mlxsw_sp_qdisc) 744 return -EOPNOTSUPP; 745 746 if (p->command == TC_TBF_REPLACE) 747 return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 748 mlxsw_sp_qdisc, 749 &mlxsw_sp_qdisc_ops_tbf, 750 &p->replace_params); 751 752 if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 753 MLXSW_SP_QDISC_TBF)) 754 return -EOPNOTSUPP; 755 756 switch (p->command) { 757 case TC_TBF_DESTROY: 758 return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 759 case TC_TBF_STATS: 760 return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 761 &p->stats); 762 default: 763 return -EOPNOTSUPP; 764 } 765 } 766 767 static int 768 mlxsw_sp_qdisc_fifo_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 769 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 770 { 771 struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 772 struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc; 773 774 if (root_qdisc != mlxsw_sp_qdisc) 775 root_qdisc->stats_base.backlog -= 776 mlxsw_sp_qdisc->stats_base.backlog; 777 return 0; 778 } 779 780 static int 781 mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 782 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 783 void *params) 784 { 785 return 0; 786 } 787 788 static int 789 mlxsw_sp_qdisc_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 790 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 791 void *params) 792 { 793 return 0; 794 } 795 796 static int 797 mlxsw_sp_qdisc_get_fifo_stats(struct mlxsw_sp_port *mlxsw_sp_port, 798 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 799 struct tc_qopt_offload_stats *stats_ptr) 800 { 801 mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 802 stats_ptr); 803 return 0; 804 } 805 806 static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = { 807 .type = MLXSW_SP_QDISC_FIFO, 808 .check_params = mlxsw_sp_qdisc_fifo_check_params, 809 .replace = mlxsw_sp_qdisc_fifo_replace, 810 .destroy = mlxsw_sp_qdisc_fifo_destroy, 811 .get_stats = mlxsw_sp_qdisc_get_fifo_stats, 812 .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, 813 }; 814 815 int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, 816 struct tc_fifo_qopt_offload *p) 817 { 818 struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 819 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 820 int tclass, child_index; 821 u32 parent_handle; 822 823 /* Invisible FIFOs are tracked in future_handle and future_fifos. Make 824 * sure that not more than one qdisc is created for a port at a time. 825 * RTNL is a simple proxy for that. 826 */ 827 ASSERT_RTNL(); 828 829 mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); 830 if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) { 831 parent_handle = TC_H_MAJ(p->parent); 832 if (parent_handle != qdisc_state->future_handle) { 833 /* This notifications is for a different Qdisc than 834 * previously. Wipe the future cache. 835 */ 836 memset(qdisc_state->future_fifos, 0, 837 sizeof(qdisc_state->future_fifos)); 838 qdisc_state->future_handle = parent_handle; 839 } 840 841 child_index = TC_H_MIN(p->parent); 842 tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index); 843 if (tclass < IEEE_8021QAZ_MAX_TCS) { 844 if (p->command == TC_FIFO_REPLACE) 845 qdisc_state->future_fifos[tclass] = true; 846 else if (p->command == TC_FIFO_DESTROY) 847 qdisc_state->future_fifos[tclass] = false; 848 } 849 } 850 if (!mlxsw_sp_qdisc) 851 return -EOPNOTSUPP; 852 853 if (p->command == TC_FIFO_REPLACE) { 854 return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 855 mlxsw_sp_qdisc, 856 &mlxsw_sp_qdisc_ops_fifo, NULL); 857 } 858 859 if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 860 MLXSW_SP_QDISC_FIFO)) 861 return -EOPNOTSUPP; 862 863 switch (p->command) { 864 case TC_FIFO_DESTROY: 865 if (p->handle == mlxsw_sp_qdisc->handle) 866 return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, 867 mlxsw_sp_qdisc); 868 return 0; 869 case TC_FIFO_STATS: 870 return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 871 &p->stats); 872 case TC_FIFO_REPLACE: /* Handled above. */ 873 break; 874 } 875 876 return -EOPNOTSUPP; 877 } 878 879 static int 880 __mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port) 881 { 882 struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 883 int i; 884 885 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 886 mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, 887 MLXSW_SP_PORT_DEFAULT_TCLASS); 888 mlxsw_sp_port_ets_set(mlxsw_sp_port, 889 MLXSW_REG_QEEC_HR_SUBGROUP, 890 i, 0, false, 0); 891 mlxsw_sp_qdisc_destroy(mlxsw_sp_port, 892 &qdisc_state->tclass_qdiscs[i]); 893 qdisc_state->tclass_qdiscs[i].prio_bitmap = 0; 894 } 895 896 return 0; 897 } 898 899 static int 900 mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 901 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 902 { 903 return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port); 904 } 905 906 static int 907 __mlxsw_sp_qdisc_ets_check_params(unsigned int nbands) 908 { 909 if (nbands > IEEE_8021QAZ_MAX_TCS) 910 return -EOPNOTSUPP; 911 912 return 0; 913 } 914 915 static int 916 mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 917 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 918 void *params) 919 { 920 struct tc_prio_qopt_offload_params *p = params; 921 922 return __mlxsw_sp_qdisc_ets_check_params(p->bands); 923 } 924 925 static int 926 __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 927 unsigned int nbands, 928 const unsigned int *quanta, 929 const unsigned int *weights, 930 const u8 *priomap) 931 { 932 struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 933 struct mlxsw_sp_qdisc *child_qdisc; 934 int tclass, i, band, backlog; 935 u8 old_priomap; 936 int err; 937 938 for (band = 0; band < nbands; band++) { 939 tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 940 child_qdisc = &qdisc_state->tclass_qdiscs[tclass]; 941 old_priomap = child_qdisc->prio_bitmap; 942 child_qdisc->prio_bitmap = 0; 943 944 err = mlxsw_sp_port_ets_set(mlxsw_sp_port, 945 MLXSW_REG_QEEC_HR_SUBGROUP, 946 tclass, 0, !!quanta[band], 947 weights[band]); 948 if (err) 949 return err; 950 951 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 952 if (priomap[i] == band) { 953 child_qdisc->prio_bitmap |= BIT(i); 954 if (BIT(i) & old_priomap) 955 continue; 956 err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, 957 i, tclass); 958 if (err) 959 return err; 960 } 961 } 962 if (old_priomap != child_qdisc->prio_bitmap && 963 child_qdisc->ops && child_qdisc->ops->clean_stats) { 964 backlog = child_qdisc->stats_base.backlog; 965 child_qdisc->ops->clean_stats(mlxsw_sp_port, 966 child_qdisc); 967 child_qdisc->stats_base.backlog = backlog; 968 } 969 970 if (handle == qdisc_state->future_handle && 971 qdisc_state->future_fifos[tclass]) { 972 err = mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC, 973 child_qdisc, 974 &mlxsw_sp_qdisc_ops_fifo, 975 NULL); 976 if (err) 977 return err; 978 } 979 } 980 for (; band < IEEE_8021QAZ_MAX_TCS; band++) { 981 tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 982 child_qdisc = &qdisc_state->tclass_qdiscs[tclass]; 983 child_qdisc->prio_bitmap = 0; 984 mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc); 985 mlxsw_sp_port_ets_set(mlxsw_sp_port, 986 MLXSW_REG_QEEC_HR_SUBGROUP, 987 tclass, 0, false, 0); 988 } 989 990 qdisc_state->future_handle = TC_H_UNSPEC; 991 memset(qdisc_state->future_fifos, 0, sizeof(qdisc_state->future_fifos)); 992 return 0; 993 } 994 995 static int 996 mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 997 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 998 void *params) 999 { 1000 struct tc_prio_qopt_offload_params *p = params; 1001 unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0}; 1002 1003 return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands, 1004 zeroes, zeroes, p->priomap); 1005 } 1006 1007 static void 1008 __mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 1009 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1010 struct gnet_stats_queue *qstats) 1011 { 1012 u64 backlog; 1013 1014 backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, 1015 mlxsw_sp_qdisc->stats_base.backlog); 1016 qstats->backlog -= backlog; 1017 } 1018 1019 static void 1020 mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 1021 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1022 void *params) 1023 { 1024 struct tc_prio_qopt_offload_params *p = params; 1025 1026 __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, 1027 p->qstats); 1028 } 1029 1030 static int 1031 mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port, 1032 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1033 struct tc_qopt_offload_stats *stats_ptr) 1034 { 1035 struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 1036 struct mlxsw_sp_qdisc *tc_qdisc; 1037 u64 tx_packets = 0; 1038 u64 tx_bytes = 0; 1039 u64 backlog = 0; 1040 u64 drops = 0; 1041 int i; 1042 1043 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 1044 tc_qdisc = &qdisc_state->tclass_qdiscs[i]; 1045 mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc, 1046 &tx_bytes, &tx_packets, 1047 &drops, &backlog); 1048 } 1049 1050 mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc, 1051 tx_bytes, tx_packets, drops, backlog, 1052 stats_ptr); 1053 return 0; 1054 } 1055 1056 static void 1057 mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 1058 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 1059 { 1060 struct mlxsw_sp_qdisc_stats *stats_base; 1061 struct mlxsw_sp_port_xstats *xstats; 1062 struct rtnl_link_stats64 *stats; 1063 int i; 1064 1065 xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; 1066 stats = &mlxsw_sp_port->periodic_hw_stats.stats; 1067 stats_base = &mlxsw_sp_qdisc->stats_base; 1068 1069 stats_base->tx_packets = stats->tx_packets; 1070 stats_base->tx_bytes = stats->tx_bytes; 1071 1072 stats_base->drops = 0; 1073 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 1074 stats_base->drops += mlxsw_sp_xstats_tail_drop(xstats, i); 1075 stats_base->drops += xstats->wred_drop[i]; 1076 } 1077 1078 mlxsw_sp_qdisc->stats_base.backlog = 0; 1079 } 1080 1081 static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = { 1082 .type = MLXSW_SP_QDISC_PRIO, 1083 .check_params = mlxsw_sp_qdisc_prio_check_params, 1084 .replace = mlxsw_sp_qdisc_prio_replace, 1085 .unoffload = mlxsw_sp_qdisc_prio_unoffload, 1086 .destroy = mlxsw_sp_qdisc_prio_destroy, 1087 .get_stats = mlxsw_sp_qdisc_get_prio_stats, 1088 .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, 1089 }; 1090 1091 static int 1092 mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 1093 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1094 void *params) 1095 { 1096 struct tc_ets_qopt_offload_replace_params *p = params; 1097 1098 return __mlxsw_sp_qdisc_ets_check_params(p->bands); 1099 } 1100 1101 static int 1102 mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 1103 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1104 void *params) 1105 { 1106 struct tc_ets_qopt_offload_replace_params *p = params; 1107 1108 return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands, 1109 p->quanta, p->weights, p->priomap); 1110 } 1111 1112 static void 1113 mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, 1114 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1115 void *params) 1116 { 1117 struct tc_ets_qopt_offload_replace_params *p = params; 1118 1119 __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, 1120 p->qstats); 1121 } 1122 1123 static int 1124 mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 1125 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 1126 { 1127 return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port); 1128 } 1129 1130 static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = { 1131 .type = MLXSW_SP_QDISC_ETS, 1132 .check_params = mlxsw_sp_qdisc_ets_check_params, 1133 .replace = mlxsw_sp_qdisc_ets_replace, 1134 .unoffload = mlxsw_sp_qdisc_ets_unoffload, 1135 .destroy = mlxsw_sp_qdisc_ets_destroy, 1136 .get_stats = mlxsw_sp_qdisc_get_prio_stats, 1137 .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, 1138 }; 1139 1140 /* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting 1141 * graph is free of cycles). These operations do not change the parent handle 1142 * though, which means it can be incomplete (if there is more than one class 1143 * where the Qdisc in question is grafted) or outright wrong (if the Qdisc was 1144 * linked to a different class and then removed from the original class). 1145 * 1146 * E.g. consider this sequence of operations: 1147 * 1148 * # tc qdisc add dev swp1 root handle 1: prio 1149 * # tc qdisc add dev swp1 parent 1:3 handle 13: red limit 1000000 avpkt 10000 1150 * RED: set bandwidth to 10Mbit 1151 * # tc qdisc link dev swp1 handle 13: parent 1:2 1152 * 1153 * At this point, both 1:2 and 1:3 have the same RED Qdisc instance as their 1154 * child. But RED will still only claim that 1:3 is its parent. If it's removed 1155 * from that band, its only parent will be 1:2, but it will continue to claim 1156 * that it is in fact 1:3. 1157 * 1158 * The notification for child Qdisc replace (e.g. TC_RED_REPLACE) comes before 1159 * the notification for parent graft (e.g. TC_PRIO_GRAFT). We take the replace 1160 * notification to offload the child Qdisc, based on its parent handle, and use 1161 * the graft operation to validate that the class where the child is actually 1162 * grafted corresponds to the parent handle. If the two don't match, we 1163 * unoffload the child. 1164 */ 1165 static int 1166 __mlxsw_sp_qdisc_ets_graft(struct mlxsw_sp_port *mlxsw_sp_port, 1167 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1168 u8 band, u32 child_handle) 1169 { 1170 struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 1171 int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 1172 struct mlxsw_sp_qdisc *old_qdisc; 1173 1174 if (band < IEEE_8021QAZ_MAX_TCS && 1175 qdisc_state->tclass_qdiscs[tclass_num].handle == child_handle) 1176 return 0; 1177 1178 if (!child_handle) { 1179 /* This is an invisible FIFO replacing the original Qdisc. 1180 * Ignore it--the original Qdisc's destroy will follow. 1181 */ 1182 return 0; 1183 } 1184 1185 /* See if the grafted qdisc is already offloaded on any tclass. If so, 1186 * unoffload it. 1187 */ 1188 old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, 1189 child_handle); 1190 if (old_qdisc) 1191 mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc); 1192 1193 mlxsw_sp_qdisc_destroy(mlxsw_sp_port, 1194 &qdisc_state->tclass_qdiscs[tclass_num]); 1195 return -EOPNOTSUPP; 1196 } 1197 1198 static int 1199 mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port, 1200 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1201 struct tc_prio_qopt_offload_graft_params *p) 1202 { 1203 return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 1204 p->band, p->child_handle); 1205 } 1206 1207 int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, 1208 struct tc_prio_qopt_offload *p) 1209 { 1210 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 1211 1212 mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true); 1213 if (!mlxsw_sp_qdisc) 1214 return -EOPNOTSUPP; 1215 1216 if (p->command == TC_PRIO_REPLACE) 1217 return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 1218 mlxsw_sp_qdisc, 1219 &mlxsw_sp_qdisc_ops_prio, 1220 &p->replace_params); 1221 1222 if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 1223 MLXSW_SP_QDISC_PRIO)) 1224 return -EOPNOTSUPP; 1225 1226 switch (p->command) { 1227 case TC_PRIO_DESTROY: 1228 return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 1229 case TC_PRIO_STATS: 1230 return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 1231 &p->stats); 1232 case TC_PRIO_GRAFT: 1233 return mlxsw_sp_qdisc_prio_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 1234 &p->graft_params); 1235 default: 1236 return -EOPNOTSUPP; 1237 } 1238 } 1239 1240 int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, 1241 struct tc_ets_qopt_offload *p) 1242 { 1243 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 1244 1245 mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true); 1246 if (!mlxsw_sp_qdisc) 1247 return -EOPNOTSUPP; 1248 1249 if (p->command == TC_ETS_REPLACE) 1250 return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, 1251 mlxsw_sp_qdisc, 1252 &mlxsw_sp_qdisc_ops_ets, 1253 &p->replace_params); 1254 1255 if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 1256 MLXSW_SP_QDISC_ETS)) 1257 return -EOPNOTSUPP; 1258 1259 switch (p->command) { 1260 case TC_ETS_DESTROY: 1261 return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 1262 case TC_ETS_STATS: 1263 return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 1264 &p->stats); 1265 case TC_ETS_GRAFT: 1266 return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc, 1267 p->graft_params.band, 1268 p->graft_params.child_handle); 1269 default: 1270 return -EOPNOTSUPP; 1271 } 1272 } 1273 1274 int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port) 1275 { 1276 struct mlxsw_sp_qdisc_state *qdisc_state; 1277 int i; 1278 1279 qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL); 1280 if (!qdisc_state) 1281 return -ENOMEM; 1282 1283 qdisc_state->root_qdisc.prio_bitmap = 0xff; 1284 qdisc_state->root_qdisc.tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS; 1285 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) 1286 qdisc_state->tclass_qdiscs[i].tclass_num = i; 1287 1288 mlxsw_sp_port->qdisc = qdisc_state; 1289 return 0; 1290 } 1291 1292 void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port) 1293 { 1294 kfree(mlxsw_sp_port->qdisc); 1295 } 1296