Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'mlxsw-refactor-qdisc-offload'

Petr Machata says:

====================
mlxsw: Refactor qdisc offload

Currently, mlxsw admits for offload a suitable root qdisc, and its
children. Thus up to two levels of hierarchy are offloaded. Often, this is
enough: one can configure TCs with RED and TCs with a shaper, and can even
see counters for each TC by looking at a qdisc at a sufficiently shallow
position.

While simple, the system has obvious shortcomings. It is not possible to
configure both RED and shaping on one TC. It is not possible to place a
PRIO below root TBF, which would then be offloaded as port shaper. FIFOs
are only offloaded at root or directly below, which is confusing to users,
because RED and TBF of course have their own FIFO.

This patchset is a step towards the end goal of allowing more comprehensive
qdisc tree offload and cleans up the qdisc offload code.

- Patches #1-#4 contain small cleanups.

- Up until now, since mlxsw offloaded only a very simple qdisc
configurations, basically all bookkeeping was done using one container
for the root qdisc, and 8 containers for its children. Patches #5, #6, #8
and #9 gradually introduce a more dynamic structure, where parent-child
relationships are tracked directly at qdiscs, instead of being implicit.

- This tree management assumes only one qdisc is created at a time. In FIFO
handlers, this condition was enforced simply by asserting RTNL lock. But
instead of furthering this RTNL dependence, patch #7 converts the whole
qdisc offload logic to a per-port mutex.

- Patch #10 adds a selftest.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>

+306 -149
+299 -149
drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c
··· 29 29 struct mlxsw_sp_qdisc_ops { 30 30 enum mlxsw_sp_qdisc_type type; 31 31 int (*check_params)(struct mlxsw_sp_port *mlxsw_sp_port, 32 - struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 33 32 void *params); 34 33 int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 35 34 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params); ··· 47 48 */ 48 49 void (*unoffload)(struct mlxsw_sp_port *mlxsw_sp_port, 49 50 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params); 51 + struct mlxsw_sp_qdisc *(*find_class)(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 52 + u32 parent); 53 + unsigned int num_classes; 50 54 }; 51 55 52 56 struct mlxsw_sp_qdisc { 53 57 u32 handle; 54 - u8 tclass_num; 58 + int tclass_num; 55 59 u8 prio_bitmap; 56 60 union { 57 61 struct red_stats red; ··· 68 66 } stats_base; 69 67 70 68 struct mlxsw_sp_qdisc_ops *ops; 69 + struct mlxsw_sp_qdisc *parent; 70 + struct mlxsw_sp_qdisc *qdiscs; 71 + unsigned int num_classes; 71 72 }; 72 73 73 74 struct mlxsw_sp_qdisc_state { 74 75 struct mlxsw_sp_qdisc root_qdisc; 75 - struct mlxsw_sp_qdisc tclass_qdiscs[IEEE_8021QAZ_MAX_TCS]; 76 76 77 77 /* When a PRIO or ETS are added, the invisible FIFOs in their bands are 78 78 * created first. When notifications for these FIFOs arrive, it is not ··· 89 85 */ 90 86 u32 future_handle; 91 87 bool future_fifos[IEEE_8021QAZ_MAX_TCS]; 88 + struct mutex lock; /* Protects qdisc state. */ 92 89 }; 93 90 94 91 static bool 95 - mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle, 96 - enum mlxsw_sp_qdisc_type type) 92 + mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle) 97 93 { 98 - return mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops && 99 - mlxsw_sp_qdisc->ops->type == type && 100 - mlxsw_sp_qdisc->handle == handle; 94 + return mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->handle == handle; 95 + } 96 + 97 + static struct mlxsw_sp_qdisc * 98 + mlxsw_sp_qdisc_walk(struct mlxsw_sp_qdisc *qdisc, 99 + struct mlxsw_sp_qdisc *(*pre)(struct mlxsw_sp_qdisc *, 100 + void *), 101 + void *data) 102 + { 103 + struct mlxsw_sp_qdisc *tmp; 104 + unsigned int i; 105 + 106 + if (pre) { 107 + tmp = pre(qdisc, data); 108 + if (tmp) 109 + return tmp; 110 + } 111 + 112 + if (qdisc->ops) { 113 + for (i = 0; i < qdisc->num_classes; i++) { 114 + tmp = &qdisc->qdiscs[i]; 115 + if (qdisc->ops) { 116 + tmp = mlxsw_sp_qdisc_walk(tmp, pre, data); 117 + if (tmp) 118 + return tmp; 119 + } 120 + } 121 + } 122 + 123 + return NULL; 124 + } 125 + 126 + static struct mlxsw_sp_qdisc * 127 + mlxsw_sp_qdisc_walk_cb_find(struct mlxsw_sp_qdisc *qdisc, void *data) 128 + { 129 + u32 parent = *(u32 *)data; 130 + 131 + if (qdisc->ops && TC_H_MAJ(qdisc->handle) == TC_H_MAJ(parent)) { 132 + if (qdisc->ops->find_class) 133 + return qdisc->ops->find_class(qdisc, parent); 134 + } 135 + 136 + return NULL; 101 137 } 102 138 103 139 static struct mlxsw_sp_qdisc * ··· 145 101 bool root_only) 146 102 { 147 103 struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 148 - int tclass, child_index; 149 104 105 + if (!qdisc_state) 106 + return NULL; 150 107 if (parent == TC_H_ROOT) 151 108 return &qdisc_state->root_qdisc; 152 - 153 - if (root_only || !qdisc_state || 154 - !qdisc_state->root_qdisc.ops || 155 - TC_H_MAJ(parent) != qdisc_state->root_qdisc.handle || 156 - TC_H_MIN(parent) > IEEE_8021QAZ_MAX_TCS) 109 + if (root_only) 157 110 return NULL; 111 + return mlxsw_sp_qdisc_walk(&qdisc_state->root_qdisc, 112 + mlxsw_sp_qdisc_walk_cb_find, &parent); 113 + } 158 114 159 - child_index = TC_H_MIN(parent); 160 - tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index); 161 - return &qdisc_state->tclass_qdiscs[tclass]; 115 + static struct mlxsw_sp_qdisc * 116 + mlxsw_sp_qdisc_walk_cb_find_by_handle(struct mlxsw_sp_qdisc *qdisc, void *data) 117 + { 118 + u32 handle = *(u32 *)data; 119 + 120 + if (qdisc->ops && qdisc->handle == handle) 121 + return qdisc; 122 + return NULL; 162 123 } 163 124 164 125 static struct mlxsw_sp_qdisc * 165 126 mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle) 166 127 { 167 128 struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 168 - int i; 169 129 170 - if (qdisc_state->root_qdisc.handle == handle) 171 - return &qdisc_state->root_qdisc; 172 - 173 - if (qdisc_state->root_qdisc.handle == TC_H_UNSPEC) 130 + if (!qdisc_state) 174 131 return NULL; 132 + return mlxsw_sp_qdisc_walk(&qdisc_state->root_qdisc, 133 + mlxsw_sp_qdisc_walk_cb_find_by_handle, 134 + &handle); 135 + } 175 136 176 - for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) 177 - if (qdisc_state->tclass_qdiscs[i].handle == handle) 178 - return &qdisc_state->tclass_qdiscs[i]; 137 + static void 138 + mlxsw_sp_qdisc_reduce_parent_backlog(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 139 + { 140 + struct mlxsw_sp_qdisc *tmp; 179 141 180 - return NULL; 142 + for (tmp = mlxsw_sp_qdisc->parent; tmp; tmp = tmp->parent) 143 + tmp->stats_base.backlog -= mlxsw_sp_qdisc->stats_base.backlog; 181 144 } 182 145 183 146 static int ··· 208 157 err_hdroom = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom); 209 158 } 210 159 211 - if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->destroy) 160 + if (!mlxsw_sp_qdisc->ops) 161 + return 0; 162 + 163 + mlxsw_sp_qdisc_reduce_parent_backlog(mlxsw_sp_qdisc); 164 + if (mlxsw_sp_qdisc->ops->destroy) 212 165 err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port, 213 166 mlxsw_sp_qdisc); 167 + if (mlxsw_sp_qdisc->ops->clean_stats) 168 + mlxsw_sp_qdisc->ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc); 214 169 215 170 mlxsw_sp_qdisc->handle = TC_H_UNSPEC; 216 171 mlxsw_sp_qdisc->ops = NULL; 217 - 172 + mlxsw_sp_qdisc->num_classes = 0; 173 + kfree(mlxsw_sp_qdisc->qdiscs); 174 + mlxsw_sp_qdisc->qdiscs = NULL; 218 175 return err_hdroom ?: err; 219 176 } 220 177 221 - static int 222 - mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 223 - struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 224 - struct mlxsw_sp_qdisc_ops *ops, void *params) 178 + static int mlxsw_sp_qdisc_create(struct mlxsw_sp_port *mlxsw_sp_port, 179 + u32 handle, 180 + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 181 + struct mlxsw_sp_qdisc_ops *ops, void *params) 225 182 { 226 183 struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; 227 184 struct mlxsw_sp_hdroom orig_hdroom; 185 + unsigned int i; 228 186 int err; 229 187 230 - if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type) 231 - /* In case this location contained a different qdisc of the 232 - * same type we can override the old qdisc configuration. 233 - * Otherwise, we need to remove the old qdisc before setting the 234 - * new one. 235 - */ 236 - mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 188 + err = ops->check_params(mlxsw_sp_port, params); 189 + if (err) 190 + return err; 191 + 192 + if (ops->num_classes) { 193 + mlxsw_sp_qdisc->qdiscs = kcalloc(ops->num_classes, 194 + sizeof(*mlxsw_sp_qdisc->qdiscs), 195 + GFP_KERNEL); 196 + if (!mlxsw_sp_qdisc->qdiscs) 197 + return -ENOMEM; 198 + 199 + for (i = 0; i < ops->num_classes; i++) 200 + mlxsw_sp_qdisc->qdiscs[i].parent = mlxsw_sp_qdisc; 201 + } 237 202 238 203 orig_hdroom = *mlxsw_sp_port->hdroom; 239 204 if (root_qdisc == mlxsw_sp_qdisc) { ··· 265 198 goto err_hdroom_configure; 266 199 } 267 200 268 - err = ops->check_params(mlxsw_sp_port, mlxsw_sp_qdisc, params); 201 + mlxsw_sp_qdisc->num_classes = ops->num_classes; 202 + mlxsw_sp_qdisc->ops = ops; 203 + mlxsw_sp_qdisc->handle = handle; 204 + err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params); 269 205 if (err) 270 - goto err_bad_param; 206 + goto err_replace; 207 + 208 + return 0; 209 + 210 + err_replace: 211 + mlxsw_sp_qdisc->handle = TC_H_UNSPEC; 212 + mlxsw_sp_qdisc->ops = NULL; 213 + mlxsw_sp_qdisc->num_classes = 0; 214 + mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom); 215 + err_hdroom_configure: 216 + kfree(mlxsw_sp_qdisc->qdiscs); 217 + mlxsw_sp_qdisc->qdiscs = NULL; 218 + return err; 219 + } 220 + 221 + static int 222 + mlxsw_sp_qdisc_change(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 223 + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params) 224 + { 225 + struct mlxsw_sp_qdisc_ops *ops = mlxsw_sp_qdisc->ops; 226 + int err; 227 + 228 + err = ops->check_params(mlxsw_sp_port, params); 229 + if (err) 230 + goto unoffload; 271 231 272 232 err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params); 273 233 if (err) 274 - goto err_config; 234 + goto unoffload; 275 235 276 236 /* Check if the Qdisc changed. That includes a situation where an 277 237 * invisible Qdisc replaces another one, or is being added for the 278 238 * first time. 279 239 */ 280 - if (mlxsw_sp_qdisc->handle != handle || handle == TC_H_UNSPEC) { 281 - mlxsw_sp_qdisc->ops = ops; 240 + if (mlxsw_sp_qdisc->handle != handle) { 282 241 if (ops->clean_stats) 283 242 ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc); 284 243 } ··· 312 219 mlxsw_sp_qdisc->handle = handle; 313 220 return 0; 314 221 315 - err_bad_param: 316 - err_config: 317 - mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom); 318 - err_hdroom_configure: 319 - if (mlxsw_sp_qdisc->handle == handle && ops->unoffload) 222 + unoffload: 223 + if (ops->unoffload) 320 224 ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params); 321 225 322 226 mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 323 227 return err; 228 + } 229 + 230 + static int 231 + mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 232 + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 233 + struct mlxsw_sp_qdisc_ops *ops, void *params) 234 + { 235 + if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type) 236 + /* In case this location contained a different qdisc of the 237 + * same type we can override the old qdisc configuration. 238 + * Otherwise, we need to remove the old qdisc before setting the 239 + * new one. 240 + */ 241 + mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 242 + 243 + if (!mlxsw_sp_qdisc->ops) 244 + return mlxsw_sp_qdisc_create(mlxsw_sp_port, handle, 245 + mlxsw_sp_qdisc, ops, params); 246 + else 247 + return mlxsw_sp_qdisc_change(mlxsw_sp_port, handle, 248 + mlxsw_sp_qdisc, params); 324 249 } 325 250 326 251 static int ··· 406 295 u64 *p_tx_bytes, u64 *p_tx_packets, 407 296 u64 *p_drops, u64 *p_backlog) 408 297 { 409 - u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 298 + int tclass_num = mlxsw_sp_qdisc->tclass_num; 410 299 struct mlxsw_sp_port_xstats *xstats; 411 300 u64 tx_bytes, tx_packets; 412 301 ··· 506 395 mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, 507 396 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 508 397 { 509 - u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 398 + int tclass_num = mlxsw_sp_qdisc->tclass_num; 510 399 struct mlxsw_sp_qdisc_stats *stats_base; 511 400 struct mlxsw_sp_port_xstats *xstats; 512 401 struct red_stats *red_base; ··· 532 421 mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 533 422 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 534 423 { 535 - struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 536 - struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc; 537 - 538 - if (root_qdisc != mlxsw_sp_qdisc) 539 - root_qdisc->stats_base.backlog -= 540 - mlxsw_sp_qdisc->stats_base.backlog; 541 - 542 424 return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port, 543 425 mlxsw_sp_qdisc->tclass_num); 544 426 } 545 427 546 428 static int 547 429 mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 548 - struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 549 430 void *params) 550 431 { 551 432 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; ··· 570 467 { 571 468 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 572 469 struct tc_red_qopt_offload_params *p = params; 573 - u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 470 + int tclass_num = mlxsw_sp_qdisc->tclass_num; 574 471 u32 min, max; 575 472 u64 prob; 576 473 ··· 615 512 void *xstats_ptr) 616 513 { 617 514 struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red; 618 - u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 515 + int tclass_num = mlxsw_sp_qdisc->tclass_num; 619 516 struct mlxsw_sp_port_xstats *xstats; 620 517 struct red_stats *res = xstats_ptr; 621 518 int early_drops, pdrops; ··· 639 536 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 640 537 struct tc_qopt_offload_stats *stats_ptr) 641 538 { 642 - u8 tclass_num = mlxsw_sp_qdisc->tclass_num; 539 + int tclass_num = mlxsw_sp_qdisc->tclass_num; 643 540 struct mlxsw_sp_qdisc_stats *stats_base; 644 541 struct mlxsw_sp_port_xstats *xstats; 645 542 u64 overlimits; ··· 656 553 return 0; 657 554 } 658 555 556 + static struct mlxsw_sp_qdisc * 557 + mlxsw_sp_qdisc_leaf_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 558 + u32 parent) 559 + { 560 + return NULL; 561 + } 562 + 659 563 #define MLXSW_SP_PORT_DEFAULT_TCLASS 0 660 564 661 565 static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = { ··· 674 564 .get_stats = mlxsw_sp_qdisc_get_red_stats, 675 565 .get_xstats = mlxsw_sp_qdisc_get_red_xstats, 676 566 .clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats, 567 + .find_class = mlxsw_sp_qdisc_leaf_find_class, 677 568 }; 678 569 679 - int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, 680 - struct tc_red_qopt_offload *p) 570 + static int __mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, 571 + struct tc_red_qopt_offload *p) 681 572 { 682 573 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 683 574 ··· 692 581 &mlxsw_sp_qdisc_ops_red, 693 582 &p->set); 694 583 695 - if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 696 - MLXSW_SP_QDISC_RED)) 584 + if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 697 585 return -EOPNOTSUPP; 698 586 699 587 switch (p->command) { ··· 707 597 default: 708 598 return -EOPNOTSUPP; 709 599 } 600 + } 601 + 602 + int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port, 603 + struct tc_red_qopt_offload *p) 604 + { 605 + int err; 606 + 607 + mutex_lock(&mlxsw_sp_port->qdisc->lock); 608 + err = __mlxsw_sp_setup_tc_red(mlxsw_sp_port, p); 609 + mutex_unlock(&mlxsw_sp_port->qdisc->lock); 610 + 611 + return err; 710 612 } 711 613 712 614 static void ··· 744 622 mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 745 623 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 746 624 { 747 - struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 748 - struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc; 749 - 750 - if (root_qdisc != mlxsw_sp_qdisc) 751 - root_qdisc->stats_base.backlog -= 752 - mlxsw_sp_qdisc->stats_base.backlog; 753 - 754 625 return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, 755 626 MLXSW_REG_QEEC_HR_SUBGROUP, 756 627 mlxsw_sp_qdisc->tclass_num, 0, ··· 793 678 794 679 static int 795 680 mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 796 - struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 797 681 void *params) 798 682 { 799 683 struct tc_tbf_qopt_offload_replace_params *p = params; ··· 880 766 .destroy = mlxsw_sp_qdisc_tbf_destroy, 881 767 .get_stats = mlxsw_sp_qdisc_get_tbf_stats, 882 768 .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, 769 + .find_class = mlxsw_sp_qdisc_leaf_find_class, 883 770 }; 884 771 885 - int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, 886 - struct tc_tbf_qopt_offload *p) 772 + static int __mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, 773 + struct tc_tbf_qopt_offload *p) 887 774 { 888 775 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 889 776 ··· 898 783 &mlxsw_sp_qdisc_ops_tbf, 899 784 &p->replace_params); 900 785 901 - if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 902 - MLXSW_SP_QDISC_TBF)) 786 + if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 903 787 return -EOPNOTSUPP; 904 788 905 789 switch (p->command) { ··· 912 798 } 913 799 } 914 800 915 - static int 916 - mlxsw_sp_qdisc_fifo_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 917 - struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 801 + int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, 802 + struct tc_tbf_qopt_offload *p) 918 803 { 919 - struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 920 - struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc; 804 + int err; 921 805 922 - if (root_qdisc != mlxsw_sp_qdisc) 923 - root_qdisc->stats_base.backlog -= 924 - mlxsw_sp_qdisc->stats_base.backlog; 925 - return 0; 806 + mutex_lock(&mlxsw_sp_port->qdisc->lock); 807 + err = __mlxsw_sp_setup_tc_tbf(mlxsw_sp_port, p); 808 + mutex_unlock(&mlxsw_sp_port->qdisc->lock); 809 + 810 + return err; 926 811 } 927 812 928 813 static int 929 814 mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 930 - struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 931 815 void *params) 932 816 { 933 817 return 0; ··· 953 841 .type = MLXSW_SP_QDISC_FIFO, 954 842 .check_params = mlxsw_sp_qdisc_fifo_check_params, 955 843 .replace = mlxsw_sp_qdisc_fifo_replace, 956 - .destroy = mlxsw_sp_qdisc_fifo_destroy, 957 844 .get_stats = mlxsw_sp_qdisc_get_fifo_stats, 958 845 .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, 959 846 }; 960 847 961 - int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, 962 - struct tc_fifo_qopt_offload *p) 848 + static int __mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, 849 + struct tc_fifo_qopt_offload *p) 963 850 { 964 851 struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 965 852 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 966 - int tclass, child_index; 853 + unsigned int band; 967 854 u32 parent_handle; 968 - 969 - /* Invisible FIFOs are tracked in future_handle and future_fifos. Make 970 - * sure that not more than one qdisc is created for a port at a time. 971 - * RTNL is a simple proxy for that. 972 - */ 973 - ASSERT_RTNL(); 974 855 975 856 mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); 976 857 if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) { ··· 977 872 qdisc_state->future_handle = parent_handle; 978 873 } 979 874 980 - child_index = TC_H_MIN(p->parent); 981 - tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index); 982 - if (tclass < IEEE_8021QAZ_MAX_TCS) { 875 + band = TC_H_MIN(p->parent) - 1; 876 + if (band < IEEE_8021QAZ_MAX_TCS) { 983 877 if (p->command == TC_FIFO_REPLACE) 984 - qdisc_state->future_fifos[tclass] = true; 878 + qdisc_state->future_fifos[band] = true; 985 879 else if (p->command == TC_FIFO_DESTROY) 986 - qdisc_state->future_fifos[tclass] = false; 880 + qdisc_state->future_fifos[band] = false; 987 881 } 988 882 } 989 883 if (!mlxsw_sp_qdisc) ··· 994 890 &mlxsw_sp_qdisc_ops_fifo, NULL); 995 891 } 996 892 997 - if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 998 - MLXSW_SP_QDISC_FIFO)) 893 + if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 999 894 return -EOPNOTSUPP; 1000 895 1001 896 switch (p->command) { 1002 897 case TC_FIFO_DESTROY: 1003 - if (p->handle == mlxsw_sp_qdisc->handle) 1004 - return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, 1005 - mlxsw_sp_qdisc); 1006 - return 0; 898 + return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 1007 899 case TC_FIFO_STATS: 1008 900 return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, 1009 901 &p->stats); ··· 1010 910 return -EOPNOTSUPP; 1011 911 } 1012 912 1013 - static int 1014 - __mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port) 913 + int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, 914 + struct tc_fifo_qopt_offload *p) 1015 915 { 1016 - struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 916 + int err; 917 + 918 + mutex_lock(&mlxsw_sp_port->qdisc->lock); 919 + err = __mlxsw_sp_setup_tc_fifo(mlxsw_sp_port, p); 920 + mutex_unlock(&mlxsw_sp_port->qdisc->lock); 921 + 922 + return err; 923 + } 924 + 925 + static int __mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 926 + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 927 + { 1017 928 int i; 1018 929 1019 - for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 930 + for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) { 1020 931 mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, 1021 932 MLXSW_SP_PORT_DEFAULT_TCLASS); 1022 933 mlxsw_sp_port_ets_set(mlxsw_sp_port, 1023 934 MLXSW_REG_QEEC_HR_SUBGROUP, 1024 935 i, 0, false, 0); 1025 936 mlxsw_sp_qdisc_destroy(mlxsw_sp_port, 1026 - &qdisc_state->tclass_qdiscs[i]); 1027 - qdisc_state->tclass_qdiscs[i].prio_bitmap = 0; 937 + &mlxsw_sp_qdisc->qdiscs[i]); 938 + mlxsw_sp_qdisc->qdiscs[i].prio_bitmap = 0; 1028 939 } 1029 940 1030 941 return 0; ··· 1045 934 mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 1046 935 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 1047 936 { 1048 - return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port); 937 + return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 1049 938 } 1050 939 1051 940 static int ··· 1059 948 1060 949 static int 1061 950 mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 1062 - struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1063 951 void *params) 1064 952 { 1065 953 struct tc_prio_qopt_offload_params *p = params; ··· 1067 957 } 1068 958 1069 959 static int 1070 - __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, 1071 - unsigned int nbands, 960 + __mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, 961 + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 962 + u32 handle, unsigned int nbands, 1072 963 const unsigned int *quanta, 1073 964 const unsigned int *weights, 1074 965 const u8 *priomap) ··· 1082 971 1083 972 for (band = 0; band < nbands; band++) { 1084 973 tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 1085 - child_qdisc = &qdisc_state->tclass_qdiscs[tclass]; 974 + child_qdisc = &mlxsw_sp_qdisc->qdiscs[band]; 1086 975 old_priomap = child_qdisc->prio_bitmap; 1087 976 child_qdisc->prio_bitmap = 0; 1088 977 ··· 1104 993 return err; 1105 994 } 1106 995 } 996 + 997 + child_qdisc->tclass_num = tclass; 998 + 1107 999 if (old_priomap != child_qdisc->prio_bitmap && 1108 1000 child_qdisc->ops && child_qdisc->ops->clean_stats) { 1109 1001 backlog = child_qdisc->stats_base.backlog; ··· 1116 1002 } 1117 1003 1118 1004 if (handle == qdisc_state->future_handle && 1119 - qdisc_state->future_fifos[tclass]) { 1005 + qdisc_state->future_fifos[band]) { 1120 1006 err = mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC, 1121 1007 child_qdisc, 1122 1008 &mlxsw_sp_qdisc_ops_fifo, ··· 1127 1013 } 1128 1014 for (; band < IEEE_8021QAZ_MAX_TCS; band++) { 1129 1015 tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 1130 - child_qdisc = &qdisc_state->tclass_qdiscs[tclass]; 1016 + child_qdisc = &mlxsw_sp_qdisc->qdiscs[band]; 1131 1017 child_qdisc->prio_bitmap = 0; 1132 1018 mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc); 1133 1019 mlxsw_sp_port_ets_set(mlxsw_sp_port, ··· 1148 1034 struct tc_prio_qopt_offload_params *p = params; 1149 1035 unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0}; 1150 1036 1151 - return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands, 1152 - zeroes, zeroes, p->priomap); 1037 + return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc, 1038 + handle, p->bands, zeroes, 1039 + zeroes, p->priomap); 1153 1040 } 1154 1041 1155 1042 static void ··· 1181 1066 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1182 1067 struct tc_qopt_offload_stats *stats_ptr) 1183 1068 { 1184 - struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 1185 1069 struct mlxsw_sp_qdisc *tc_qdisc; 1186 1070 u64 tx_packets = 0; 1187 1071 u64 tx_bytes = 0; ··· 1188 1074 u64 drops = 0; 1189 1075 int i; 1190 1076 1191 - for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 1192 - tc_qdisc = &qdisc_state->tclass_qdiscs[i]; 1077 + for (i = 0; i < mlxsw_sp_qdisc->num_classes; i++) { 1078 + tc_qdisc = &mlxsw_sp_qdisc->qdiscs[i]; 1193 1079 mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc, 1194 1080 &tx_bytes, &tx_packets, 1195 1081 &drops, &backlog); ··· 1226 1112 mlxsw_sp_qdisc->stats_base.backlog = 0; 1227 1113 } 1228 1114 1115 + static struct mlxsw_sp_qdisc * 1116 + mlxsw_sp_qdisc_prio_find_class(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1117 + u32 parent) 1118 + { 1119 + int child_index = TC_H_MIN(parent); 1120 + int band = child_index - 1; 1121 + 1122 + if (band < 0 || band >= mlxsw_sp_qdisc->num_classes) 1123 + return NULL; 1124 + return &mlxsw_sp_qdisc->qdiscs[band]; 1125 + } 1126 + 1229 1127 static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = { 1230 1128 .type = MLXSW_SP_QDISC_PRIO, 1231 1129 .check_params = mlxsw_sp_qdisc_prio_check_params, ··· 1246 1120 .destroy = mlxsw_sp_qdisc_prio_destroy, 1247 1121 .get_stats = mlxsw_sp_qdisc_get_prio_stats, 1248 1122 .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, 1123 + .find_class = mlxsw_sp_qdisc_prio_find_class, 1124 + .num_classes = IEEE_8021QAZ_MAX_TCS, 1249 1125 }; 1250 1126 1251 1127 static int 1252 1128 mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port, 1253 - struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1254 1129 void *params) 1255 1130 { 1256 1131 struct tc_ets_qopt_offload_replace_params *p = params; ··· 1266 1139 { 1267 1140 struct tc_ets_qopt_offload_replace_params *p = params; 1268 1141 1269 - return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands, 1270 - p->quanta, p->weights, p->priomap); 1142 + return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, mlxsw_sp_qdisc, 1143 + handle, p->bands, p->quanta, 1144 + p->weights, p->priomap); 1271 1145 } 1272 1146 1273 1147 static void ··· 1286 1158 mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 1287 1159 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) 1288 1160 { 1289 - return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port); 1161 + return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 1290 1162 } 1291 1163 1292 1164 static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = { ··· 1297 1169 .destroy = mlxsw_sp_qdisc_ets_destroy, 1298 1170 .get_stats = mlxsw_sp_qdisc_get_prio_stats, 1299 1171 .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, 1172 + .find_class = mlxsw_sp_qdisc_prio_find_class, 1173 + .num_classes = IEEE_8021QAZ_MAX_TCS, 1300 1174 }; 1301 1175 1302 1176 /* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting ··· 1331 1201 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, 1332 1202 u8 band, u32 child_handle) 1333 1203 { 1334 - struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; 1335 - int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); 1336 1204 struct mlxsw_sp_qdisc *old_qdisc; 1337 1205 1338 - if (band < IEEE_8021QAZ_MAX_TCS && 1339 - qdisc_state->tclass_qdiscs[tclass_num].handle == child_handle) 1206 + if (band < mlxsw_sp_qdisc->num_classes && 1207 + mlxsw_sp_qdisc->qdiscs[band].handle == child_handle) 1340 1208 return 0; 1341 1209 1342 1210 if (!child_handle) { ··· 1352 1224 if (old_qdisc) 1353 1225 mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc); 1354 1226 1355 - mlxsw_sp_qdisc_destroy(mlxsw_sp_port, 1356 - &qdisc_state->tclass_qdiscs[tclass_num]); 1227 + mlxsw_sp_qdisc = mlxsw_sp_qdisc->ops->find_class(mlxsw_sp_qdisc, band); 1228 + if (!WARN_ON(!mlxsw_sp_qdisc)) 1229 + mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); 1230 + 1357 1231 return -EOPNOTSUPP; 1358 1232 } 1359 1233 ··· 1368 1238 p->band, p->child_handle); 1369 1239 } 1370 1240 1371 - int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, 1372 - struct tc_prio_qopt_offload *p) 1241 + static int __mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, 1242 + struct tc_prio_qopt_offload *p) 1373 1243 { 1374 1244 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 1375 1245 ··· 1383 1253 &mlxsw_sp_qdisc_ops_prio, 1384 1254 &p->replace_params); 1385 1255 1386 - if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 1387 - MLXSW_SP_QDISC_PRIO)) 1256 + if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 1388 1257 return -EOPNOTSUPP; 1389 1258 1390 1259 switch (p->command) { ··· 1400 1271 } 1401 1272 } 1402 1273 1403 - int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, 1404 - struct tc_ets_qopt_offload *p) 1274 + int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, 1275 + struct tc_prio_qopt_offload *p) 1276 + { 1277 + int err; 1278 + 1279 + mutex_lock(&mlxsw_sp_port->qdisc->lock); 1280 + err = __mlxsw_sp_setup_tc_prio(mlxsw_sp_port, p); 1281 + mutex_unlock(&mlxsw_sp_port->qdisc->lock); 1282 + 1283 + return err; 1284 + } 1285 + 1286 + static int __mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, 1287 + struct tc_ets_qopt_offload *p) 1405 1288 { 1406 1289 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; 1407 1290 ··· 1427 1286 &mlxsw_sp_qdisc_ops_ets, 1428 1287 &p->replace_params); 1429 1288 1430 - if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, 1431 - MLXSW_SP_QDISC_ETS)) 1289 + if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle)) 1432 1290 return -EOPNOTSUPP; 1433 1291 1434 1292 switch (p->command) { ··· 1443 1303 default: 1444 1304 return -EOPNOTSUPP; 1445 1305 } 1306 + } 1307 + 1308 + int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, 1309 + struct tc_ets_qopt_offload *p) 1310 + { 1311 + int err; 1312 + 1313 + mutex_lock(&mlxsw_sp_port->qdisc->lock); 1314 + err = __mlxsw_sp_setup_tc_ets(mlxsw_sp_port, p); 1315 + mutex_unlock(&mlxsw_sp_port->qdisc->lock); 1316 + 1317 + return err; 1446 1318 } 1447 1319 1448 1320 struct mlxsw_sp_qevent_block { ··· 1986 1834 int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port) 1987 1835 { 1988 1836 struct mlxsw_sp_qdisc_state *qdisc_state; 1989 - int i; 1990 1837 1991 1838 qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL); 1992 1839 if (!qdisc_state) 1993 1840 return -ENOMEM; 1994 1841 1842 + mutex_init(&qdisc_state->lock); 1995 1843 qdisc_state->root_qdisc.prio_bitmap = 0xff; 1996 1844 qdisc_state->root_qdisc.tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS; 1997 - for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) 1998 - qdisc_state->tclass_qdiscs[i].tclass_num = i; 1999 - 2000 1845 mlxsw_sp_port->qdisc = qdisc_state; 2001 1846 return 0; 2002 1847 } 2003 1848 2004 1849 void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port) 2005 1850 { 1851 + mutex_destroy(&mlxsw_sp_port->qdisc->lock); 2006 1852 kfree(mlxsw_sp_port->qdisc); 2007 1853 }
+7
tools/testing/selftests/drivers/net/mlxsw/sch_red_ets.sh
··· 67 67 { 68 68 install_qdisc 69 69 70 + # Make sure that we get the non-zero value if there is any. 71 + local cur=$(busywait 1100 until_counter_is "> 0" \ 72 + qdisc_stats_get $swp3 10: .backlog) 73 + (( cur == 0 )) 74 + check_err $? "backlog of $cur observed on non-busy qdisc" 75 + log_test "$QDISC backlog properly cleaned" 76 + 70 77 do_red_test 10 $BACKLOG1 71 78 do_red_test 11 $BACKLOG2 72 79