| .. | .. |
|---|
| 8 | 8 | #include <net/red.h> |
|---|
| 9 | 9 | |
|---|
| 10 | 10 | #include "spectrum.h" |
|---|
| 11 | +#include "spectrum_span.h" |
|---|
| 11 | 12 | #include "reg.h" |
|---|
| 12 | 13 | |
|---|
| 13 | 14 | #define MLXSW_SP_PRIO_BAND_TO_TCLASS(band) (IEEE_8021QAZ_MAX_TCS - band - 1) |
|---|
| .. | .. |
|---|
| 18 | 19 | MLXSW_SP_QDISC_NO_QDISC, |
|---|
| 19 | 20 | MLXSW_SP_QDISC_RED, |
|---|
| 20 | 21 | MLXSW_SP_QDISC_PRIO, |
|---|
| 22 | + MLXSW_SP_QDISC_ETS, |
|---|
| 23 | + MLXSW_SP_QDISC_TBF, |
|---|
| 24 | + MLXSW_SP_QDISC_FIFO, |
|---|
| 21 | 25 | }; |
|---|
| 26 | + |
|---|
| 27 | +struct mlxsw_sp_qdisc; |
|---|
| 22 | 28 | |
|---|
| 23 | 29 | struct mlxsw_sp_qdisc_ops { |
|---|
| 24 | 30 | enum mlxsw_sp_qdisc_type type; |
|---|
| 25 | 31 | int (*check_params)(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 26 | 32 | struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 27 | 33 | void *params); |
|---|
| 28 | | - int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 34 | + int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, |
|---|
| 29 | 35 | struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params); |
|---|
| 30 | 36 | int (*destroy)(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 31 | 37 | struct mlxsw_sp_qdisc *mlxsw_sp_qdisc); |
|---|
| .. | .. |
|---|
| 62 | 68 | struct mlxsw_sp_qdisc_ops *ops; |
|---|
| 63 | 69 | }; |
|---|
| 64 | 70 | |
|---|
| 71 | +struct mlxsw_sp_qdisc_state { |
|---|
| 72 | + struct mlxsw_sp_qdisc root_qdisc; |
|---|
| 73 | + struct mlxsw_sp_qdisc tclass_qdiscs[IEEE_8021QAZ_MAX_TCS]; |
|---|
| 74 | + |
|---|
| 75 | + /* When a PRIO or ETS are added, the invisible FIFOs in their bands are |
|---|
| 76 | + * created first. When notifications for these FIFOs arrive, it is not |
|---|
| 77 | + * known what qdisc their parent handle refers to. It could be a |
|---|
| 78 | + * newly-created PRIO that will replace the currently-offloaded one, or |
|---|
| 79 | + * it could be e.g. a RED that will be attached below it. |
|---|
| 80 | + * |
|---|
| 81 | + * As the notifications start to arrive, use them to note what the |
|---|
| 82 | + * future parent handle is, and keep track of which child FIFOs were |
|---|
| 83 | + * seen. Then when the parent is known, retroactively offload those |
|---|
| 84 | + * FIFOs. |
|---|
| 85 | + */ |
|---|
| 86 | + u32 future_handle; |
|---|
| 87 | + bool future_fifos[IEEE_8021QAZ_MAX_TCS]; |
|---|
| 88 | +}; |
|---|
| 89 | + |
|---|
| 65 | 90 | static bool |
|---|
| 66 | 91 | mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle, |
|---|
| 67 | 92 | enum mlxsw_sp_qdisc_type type) |
|---|
| .. | .. |
|---|
| 75 | 100 | mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent, |
|---|
| 76 | 101 | bool root_only) |
|---|
| 77 | 102 | { |
|---|
| 103 | + struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; |
|---|
| 78 | 104 | int tclass, child_index; |
|---|
| 79 | 105 | |
|---|
| 80 | 106 | if (parent == TC_H_ROOT) |
|---|
| 81 | | - return mlxsw_sp_port->root_qdisc; |
|---|
| 107 | + return &qdisc_state->root_qdisc; |
|---|
| 82 | 108 | |
|---|
| 83 | | - if (root_only || !mlxsw_sp_port->root_qdisc || |
|---|
| 84 | | - !mlxsw_sp_port->root_qdisc->ops || |
|---|
| 85 | | - TC_H_MAJ(parent) != mlxsw_sp_port->root_qdisc->handle || |
|---|
| 109 | + if (root_only || !qdisc_state || |
|---|
| 110 | + !qdisc_state->root_qdisc.ops || |
|---|
| 111 | + TC_H_MAJ(parent) != qdisc_state->root_qdisc.handle || |
|---|
| 86 | 112 | TC_H_MIN(parent) > IEEE_8021QAZ_MAX_TCS) |
|---|
| 87 | 113 | return NULL; |
|---|
| 88 | 114 | |
|---|
| 89 | 115 | child_index = TC_H_MIN(parent); |
|---|
| 90 | 116 | tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index); |
|---|
| 91 | | - return &mlxsw_sp_port->tclass_qdiscs[tclass]; |
|---|
| 117 | + return &qdisc_state->tclass_qdiscs[tclass]; |
|---|
| 92 | 118 | } |
|---|
| 93 | 119 | |
|---|
| 94 | 120 | static struct mlxsw_sp_qdisc * |
|---|
| 95 | 121 | mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle) |
|---|
| 96 | 122 | { |
|---|
| 123 | + struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; |
|---|
| 97 | 124 | int i; |
|---|
| 98 | 125 | |
|---|
| 99 | | - if (mlxsw_sp_port->root_qdisc->handle == handle) |
|---|
| 100 | | - return mlxsw_sp_port->root_qdisc; |
|---|
| 126 | + if (qdisc_state->root_qdisc.handle == handle) |
|---|
| 127 | + return &qdisc_state->root_qdisc; |
|---|
| 101 | 128 | |
|---|
| 102 | | - if (mlxsw_sp_port->root_qdisc->handle == TC_H_UNSPEC) |
|---|
| 129 | + if (qdisc_state->root_qdisc.handle == TC_H_UNSPEC) |
|---|
| 103 | 130 | return NULL; |
|---|
| 104 | 131 | |
|---|
| 105 | 132 | for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) |
|---|
| 106 | | - if (mlxsw_sp_port->tclass_qdiscs[i].handle == handle) |
|---|
| 107 | | - return &mlxsw_sp_port->tclass_qdiscs[i]; |
|---|
| 133 | + if (qdisc_state->tclass_qdiscs[i].handle == handle) |
|---|
| 134 | + return &qdisc_state->tclass_qdiscs[i]; |
|---|
| 108 | 135 | |
|---|
| 109 | 136 | return NULL; |
|---|
| 110 | 137 | } |
|---|
| .. | .. |
|---|
| 113 | 140 | mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 114 | 141 | struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) |
|---|
| 115 | 142 | { |
|---|
| 143 | + struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; |
|---|
| 144 | + int err_hdroom = 0; |
|---|
| 116 | 145 | int err = 0; |
|---|
| 117 | 146 | |
|---|
| 118 | 147 | if (!mlxsw_sp_qdisc) |
|---|
| 119 | 148 | return 0; |
|---|
| 149 | + |
|---|
| 150 | + if (root_qdisc == mlxsw_sp_qdisc) { |
|---|
| 151 | + struct mlxsw_sp_hdroom hdroom = *mlxsw_sp_port->hdroom; |
|---|
| 152 | + |
|---|
| 153 | + hdroom.mode = MLXSW_SP_HDROOM_MODE_DCB; |
|---|
| 154 | + mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom); |
|---|
| 155 | + mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom); |
|---|
| 156 | + mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom); |
|---|
| 157 | + err_hdroom = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom); |
|---|
| 158 | + } |
|---|
| 120 | 159 | |
|---|
| 121 | 160 | if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->destroy) |
|---|
| 122 | 161 | err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port, |
|---|
| .. | .. |
|---|
| 124 | 163 | |
|---|
| 125 | 164 | mlxsw_sp_qdisc->handle = TC_H_UNSPEC; |
|---|
| 126 | 165 | mlxsw_sp_qdisc->ops = NULL; |
|---|
| 127 | | - return err; |
|---|
| 166 | + |
|---|
| 167 | + return err_hdroom ?: err; |
|---|
| 128 | 168 | } |
|---|
| 129 | 169 | |
|---|
| 130 | 170 | static int |
|---|
| .. | .. |
|---|
| 132 | 172 | struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 133 | 173 | struct mlxsw_sp_qdisc_ops *ops, void *params) |
|---|
| 134 | 174 | { |
|---|
| 175 | + struct mlxsw_sp_qdisc *root_qdisc = &mlxsw_sp_port->qdisc->root_qdisc; |
|---|
| 176 | + struct mlxsw_sp_hdroom orig_hdroom; |
|---|
| 135 | 177 | int err; |
|---|
| 136 | 178 | |
|---|
| 137 | 179 | if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type) |
|---|
| .. | .. |
|---|
| 141 | 183 | * new one. |
|---|
| 142 | 184 | */ |
|---|
| 143 | 185 | mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); |
|---|
| 186 | + |
|---|
| 187 | + orig_hdroom = *mlxsw_sp_port->hdroom; |
|---|
| 188 | + if (root_qdisc == mlxsw_sp_qdisc) { |
|---|
| 189 | + struct mlxsw_sp_hdroom hdroom = orig_hdroom; |
|---|
| 190 | + |
|---|
| 191 | + hdroom.mode = MLXSW_SP_HDROOM_MODE_TC; |
|---|
| 192 | + mlxsw_sp_hdroom_prios_reset_buf_idx(&hdroom); |
|---|
| 193 | + mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom); |
|---|
| 194 | + mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom); |
|---|
| 195 | + |
|---|
| 196 | + err = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom); |
|---|
| 197 | + if (err) |
|---|
| 198 | + goto err_hdroom_configure; |
|---|
| 199 | + } |
|---|
| 200 | + |
|---|
| 144 | 201 | err = ops->check_params(mlxsw_sp_port, mlxsw_sp_qdisc, params); |
|---|
| 145 | 202 | if (err) |
|---|
| 146 | 203 | goto err_bad_param; |
|---|
| 147 | 204 | |
|---|
| 148 | | - err = ops->replace(mlxsw_sp_port, mlxsw_sp_qdisc, params); |
|---|
| 205 | + err = ops->replace(mlxsw_sp_port, handle, mlxsw_sp_qdisc, params); |
|---|
| 149 | 206 | if (err) |
|---|
| 150 | 207 | goto err_config; |
|---|
| 151 | 208 | |
|---|
| 152 | | - if (mlxsw_sp_qdisc->handle != handle) { |
|---|
| 209 | + /* Check if the Qdisc changed. That includes a situation where an |
|---|
| 210 | + * invisible Qdisc replaces another one, or is being added for the |
|---|
| 211 | + * first time. |
|---|
| 212 | + */ |
|---|
| 213 | + if (mlxsw_sp_qdisc->handle != handle || handle == TC_H_UNSPEC) { |
|---|
| 153 | 214 | mlxsw_sp_qdisc->ops = ops; |
|---|
| 154 | 215 | if (ops->clean_stats) |
|---|
| 155 | 216 | ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc); |
|---|
| .. | .. |
|---|
| 160 | 221 | |
|---|
| 161 | 222 | err_bad_param: |
|---|
| 162 | 223 | err_config: |
|---|
| 224 | + mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom); |
|---|
| 225 | +err_hdroom_configure: |
|---|
| 163 | 226 | if (mlxsw_sp_qdisc->handle == handle && ops->unoffload) |
|---|
| 164 | 227 | ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params); |
|---|
| 165 | 228 | |
|---|
| .. | .. |
|---|
| 226 | 289 | } |
|---|
| 227 | 290 | } |
|---|
| 228 | 291 | |
|---|
| 292 | +static void |
|---|
| 293 | +mlxsw_sp_qdisc_collect_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 294 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 295 | + u64 *p_tx_bytes, u64 *p_tx_packets, |
|---|
| 296 | + u64 *p_drops, u64 *p_backlog) |
|---|
| 297 | +{ |
|---|
| 298 | + u8 tclass_num = mlxsw_sp_qdisc->tclass_num; |
|---|
| 299 | + struct mlxsw_sp_port_xstats *xstats; |
|---|
| 300 | + u64 tx_bytes, tx_packets; |
|---|
| 301 | + |
|---|
| 302 | + xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; |
|---|
| 303 | + mlxsw_sp_qdisc_bstats_per_priority_get(xstats, |
|---|
| 304 | + mlxsw_sp_qdisc->prio_bitmap, |
|---|
| 305 | + &tx_packets, &tx_bytes); |
|---|
| 306 | + |
|---|
| 307 | + *p_tx_packets += tx_packets; |
|---|
| 308 | + *p_tx_bytes += tx_bytes; |
|---|
| 309 | + *p_drops += xstats->wred_drop[tclass_num] + |
|---|
| 310 | + mlxsw_sp_xstats_tail_drop(xstats, tclass_num); |
|---|
| 311 | + *p_backlog += mlxsw_sp_xstats_backlog(xstats, tclass_num); |
|---|
| 312 | +} |
|---|
| 313 | + |
|---|
| 314 | +static void |
|---|
| 315 | +mlxsw_sp_qdisc_update_stats(struct mlxsw_sp *mlxsw_sp, |
|---|
| 316 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 317 | + u64 tx_bytes, u64 tx_packets, |
|---|
| 318 | + u64 drops, u64 backlog, |
|---|
| 319 | + struct tc_qopt_offload_stats *stats_ptr) |
|---|
| 320 | +{ |
|---|
| 321 | + struct mlxsw_sp_qdisc_stats *stats_base = &mlxsw_sp_qdisc->stats_base; |
|---|
| 322 | + |
|---|
| 323 | + tx_bytes -= stats_base->tx_bytes; |
|---|
| 324 | + tx_packets -= stats_base->tx_packets; |
|---|
| 325 | + drops -= stats_base->drops; |
|---|
| 326 | + backlog -= stats_base->backlog; |
|---|
| 327 | + |
|---|
| 328 | + _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets); |
|---|
| 329 | + stats_ptr->qstats->drops += drops; |
|---|
| 330 | + stats_ptr->qstats->backlog += mlxsw_sp_cells_bytes(mlxsw_sp, backlog); |
|---|
| 331 | + |
|---|
| 332 | + stats_base->backlog += backlog; |
|---|
| 333 | + stats_base->drops += drops; |
|---|
| 334 | + stats_base->tx_bytes += tx_bytes; |
|---|
| 335 | + stats_base->tx_packets += tx_packets; |
|---|
| 336 | +} |
|---|
| 337 | + |
|---|
| 338 | +static void |
|---|
| 339 | +mlxsw_sp_qdisc_get_tc_stats(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 340 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 341 | + struct tc_qopt_offload_stats *stats_ptr) |
|---|
| 342 | +{ |
|---|
| 343 | + u64 tx_packets = 0; |
|---|
| 344 | + u64 tx_bytes = 0; |
|---|
| 345 | + u64 backlog = 0; |
|---|
| 346 | + u64 drops = 0; |
|---|
| 347 | + |
|---|
| 348 | + mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, |
|---|
| 349 | + &tx_bytes, &tx_packets, |
|---|
| 350 | + &drops, &backlog); |
|---|
| 351 | + mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc, |
|---|
| 352 | + tx_bytes, tx_packets, drops, backlog, |
|---|
| 353 | + stats_ptr); |
|---|
| 354 | +} |
|---|
| 355 | + |
|---|
| 229 | 356 | static int |
|---|
| 230 | 357 | mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 231 | 358 | int tclass_num, u32 min, u32 max, |
|---|
| 232 | | - u32 probability, bool is_ecn) |
|---|
| 359 | + u32 probability, bool is_wred, bool is_ecn) |
|---|
| 233 | 360 | { |
|---|
| 234 | 361 | char cwtpm_cmd[MLXSW_REG_CWTPM_LEN]; |
|---|
| 235 | 362 | char cwtp_cmd[MLXSW_REG_CWTP_LEN]; |
|---|
| .. | .. |
|---|
| 247 | 374 | return err; |
|---|
| 248 | 375 | |
|---|
| 249 | 376 | mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num, |
|---|
| 250 | | - MLXSW_REG_CWTP_DEFAULT_PROFILE, true, is_ecn); |
|---|
| 377 | + MLXSW_REG_CWTP_DEFAULT_PROFILE, is_wred, is_ecn); |
|---|
| 251 | 378 | |
|---|
| 252 | 379 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd); |
|---|
| 253 | 380 | } |
|---|
| .. | .. |
|---|
| 281 | 408 | mlxsw_sp_qdisc->prio_bitmap, |
|---|
| 282 | 409 | &stats_base->tx_packets, |
|---|
| 283 | 410 | &stats_base->tx_bytes); |
|---|
| 284 | | - red_base->prob_mark = xstats->ecn; |
|---|
| 285 | 411 | red_base->prob_drop = xstats->wred_drop[tclass_num]; |
|---|
| 286 | 412 | red_base->pdrop = mlxsw_sp_xstats_tail_drop(xstats, tclass_num); |
|---|
| 287 | 413 | |
|---|
| .. | .. |
|---|
| 295 | 421 | mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 296 | 422 | struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) |
|---|
| 297 | 423 | { |
|---|
| 298 | | - struct mlxsw_sp_qdisc *root_qdisc = mlxsw_sp_port->root_qdisc; |
|---|
| 424 | + struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; |
|---|
| 425 | + struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc; |
|---|
| 299 | 426 | |
|---|
| 300 | 427 | if (root_qdisc != mlxsw_sp_qdisc) |
|---|
| 301 | 428 | root_qdisc->stats_base.backlog -= |
|---|
| .. | .. |
|---|
| 319 | 446 | p->max); |
|---|
| 320 | 447 | return -EINVAL; |
|---|
| 321 | 448 | } |
|---|
| 322 | | - if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_BUFFER_SIZE)) { |
|---|
| 449 | + if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core, |
|---|
| 450 | + GUARANTEED_SHARED_BUFFER)) { |
|---|
| 323 | 451 | dev_err(mlxsw_sp->bus_info->dev, |
|---|
| 324 | 452 | "spectrum: RED: max value %u is too big\n", p->max); |
|---|
| 325 | 453 | return -EINVAL; |
|---|
| .. | .. |
|---|
| 333 | 461 | } |
|---|
| 334 | 462 | |
|---|
| 335 | 463 | static int |
|---|
| 336 | | -mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 464 | +mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, |
|---|
| 337 | 465 | struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 338 | 466 | void *params) |
|---|
| 339 | 467 | { |
|---|
| .. | .. |
|---|
| 350 | 478 | prob = DIV_ROUND_UP(prob, 1 << 16); |
|---|
| 351 | 479 | min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min); |
|---|
| 352 | 480 | max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max); |
|---|
| 353 | | - return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num, min, |
|---|
| 354 | | - max, prob, p->is_ecn); |
|---|
| 481 | + return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num, |
|---|
| 482 | + min, max, prob, |
|---|
| 483 | + !p->is_nodrop, p->is_ecn); |
|---|
| 484 | +} |
|---|
| 485 | + |
|---|
| 486 | +static void |
|---|
| 487 | +mlxsw_sp_qdisc_leaf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 488 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 489 | + struct gnet_stats_queue *qstats) |
|---|
| 490 | +{ |
|---|
| 491 | + u64 backlog; |
|---|
| 492 | + |
|---|
| 493 | + backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, |
|---|
| 494 | + mlxsw_sp_qdisc->stats_base.backlog); |
|---|
| 495 | + qstats->backlog -= backlog; |
|---|
| 496 | + mlxsw_sp_qdisc->stats_base.backlog = 0; |
|---|
| 355 | 497 | } |
|---|
| 356 | 498 | |
|---|
| 357 | 499 | static void |
|---|
| .. | .. |
|---|
| 360 | 502 | void *params) |
|---|
| 361 | 503 | { |
|---|
| 362 | 504 | struct tc_red_qopt_offload_params *p = params; |
|---|
| 363 | | - u64 backlog; |
|---|
| 364 | 505 | |
|---|
| 365 | | - backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, |
|---|
| 366 | | - mlxsw_sp_qdisc->stats_base.backlog); |
|---|
| 367 | | - p->qstats->backlog -= backlog; |
|---|
| 368 | | - mlxsw_sp_qdisc->stats_base.backlog = 0; |
|---|
| 506 | + mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats); |
|---|
| 369 | 507 | } |
|---|
| 370 | 508 | |
|---|
| 371 | 509 | static int |
|---|
| .. | .. |
|---|
| 377 | 515 | u8 tclass_num = mlxsw_sp_qdisc->tclass_num; |
|---|
| 378 | 516 | struct mlxsw_sp_port_xstats *xstats; |
|---|
| 379 | 517 | struct red_stats *res = xstats_ptr; |
|---|
| 380 | | - int early_drops, marks, pdrops; |
|---|
| 518 | + int early_drops, pdrops; |
|---|
| 381 | 519 | |
|---|
| 382 | 520 | xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; |
|---|
| 383 | 521 | |
|---|
| 384 | 522 | early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop; |
|---|
| 385 | | - marks = xstats->ecn - xstats_base->prob_mark; |
|---|
| 386 | 523 | pdrops = mlxsw_sp_xstats_tail_drop(xstats, tclass_num) - |
|---|
| 387 | 524 | xstats_base->pdrop; |
|---|
| 388 | 525 | |
|---|
| 389 | 526 | res->pdrop += pdrops; |
|---|
| 390 | 527 | res->prob_drop += early_drops; |
|---|
| 391 | | - res->prob_mark += marks; |
|---|
| 392 | 528 | |
|---|
| 393 | 529 | xstats_base->pdrop += pdrops; |
|---|
| 394 | 530 | xstats_base->prob_drop += early_drops; |
|---|
| 395 | | - xstats_base->prob_mark += marks; |
|---|
| 396 | 531 | return 0; |
|---|
| 397 | 532 | } |
|---|
| 398 | 533 | |
|---|
| .. | .. |
|---|
| 401 | 536 | struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 402 | 537 | struct tc_qopt_offload_stats *stats_ptr) |
|---|
| 403 | 538 | { |
|---|
| 404 | | - u64 tx_bytes, tx_packets, overlimits, drops, backlog; |
|---|
| 405 | 539 | u8 tclass_num = mlxsw_sp_qdisc->tclass_num; |
|---|
| 406 | 540 | struct mlxsw_sp_qdisc_stats *stats_base; |
|---|
| 407 | 541 | struct mlxsw_sp_port_xstats *xstats; |
|---|
| 542 | + u64 overlimits; |
|---|
| 408 | 543 | |
|---|
| 409 | 544 | xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; |
|---|
| 410 | 545 | stats_base = &mlxsw_sp_qdisc->stats_base; |
|---|
| 411 | 546 | |
|---|
| 412 | | - mlxsw_sp_qdisc_bstats_per_priority_get(xstats, |
|---|
| 413 | | - mlxsw_sp_qdisc->prio_bitmap, |
|---|
| 414 | | - &tx_packets, &tx_bytes); |
|---|
| 415 | | - tx_bytes = tx_bytes - stats_base->tx_bytes; |
|---|
| 416 | | - tx_packets = tx_packets - stats_base->tx_packets; |
|---|
| 547 | + mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, stats_ptr); |
|---|
| 548 | + overlimits = xstats->wred_drop[tclass_num] - stats_base->overlimits; |
|---|
| 417 | 549 | |
|---|
| 418 | | - overlimits = xstats->wred_drop[tclass_num] + xstats->ecn - |
|---|
| 419 | | - stats_base->overlimits; |
|---|
| 420 | | - drops = xstats->wred_drop[tclass_num] + |
|---|
| 421 | | - mlxsw_sp_xstats_tail_drop(xstats, tclass_num) - |
|---|
| 422 | | - stats_base->drops; |
|---|
| 423 | | - backlog = mlxsw_sp_xstats_backlog(xstats, tclass_num); |
|---|
| 424 | | - |
|---|
| 425 | | - _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets); |
|---|
| 426 | 550 | stats_ptr->qstats->overlimits += overlimits; |
|---|
| 427 | | - stats_ptr->qstats->drops += drops; |
|---|
| 428 | | - stats_ptr->qstats->backlog += |
|---|
| 429 | | - mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, |
|---|
| 430 | | - backlog) - |
|---|
| 431 | | - mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, |
|---|
| 432 | | - stats_base->backlog); |
|---|
| 433 | | - |
|---|
| 434 | | - stats_base->backlog = backlog; |
|---|
| 435 | | - stats_base->drops += drops; |
|---|
| 436 | 551 | stats_base->overlimits += overlimits; |
|---|
| 437 | | - stats_base->tx_bytes += tx_bytes; |
|---|
| 438 | | - stats_base->tx_packets += tx_packets; |
|---|
| 552 | + |
|---|
| 439 | 553 | return 0; |
|---|
| 440 | 554 | } |
|---|
| 441 | 555 | |
|---|
| .. | .. |
|---|
| 485 | 599 | } |
|---|
| 486 | 600 | } |
|---|
| 487 | 601 | |
|---|
| 602 | +static void |
|---|
| 603 | +mlxsw_sp_setup_tc_qdisc_leaf_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 604 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) |
|---|
| 605 | +{ |
|---|
| 606 | + u64 backlog_cells = 0; |
|---|
| 607 | + u64 tx_packets = 0; |
|---|
| 608 | + u64 tx_bytes = 0; |
|---|
| 609 | + u64 drops = 0; |
|---|
| 610 | + |
|---|
| 611 | + mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, |
|---|
| 612 | + &tx_bytes, &tx_packets, |
|---|
| 613 | + &drops, &backlog_cells); |
|---|
| 614 | + |
|---|
| 615 | + mlxsw_sp_qdisc->stats_base.tx_packets = tx_packets; |
|---|
| 616 | + mlxsw_sp_qdisc->stats_base.tx_bytes = tx_bytes; |
|---|
| 617 | + mlxsw_sp_qdisc->stats_base.drops = drops; |
|---|
| 618 | + mlxsw_sp_qdisc->stats_base.backlog = 0; |
|---|
| 619 | +} |
|---|
| 620 | + |
|---|
| 488 | 621 | static int |
|---|
| 489 | | -mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 622 | +mlxsw_sp_qdisc_tbf_destroy(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 623 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) |
|---|
| 624 | +{ |
|---|
| 625 | + struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; |
|---|
| 626 | + struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc; |
|---|
| 627 | + |
|---|
| 628 | + if (root_qdisc != mlxsw_sp_qdisc) |
|---|
| 629 | + root_qdisc->stats_base.backlog -= |
|---|
| 630 | + mlxsw_sp_qdisc->stats_base.backlog; |
|---|
| 631 | + |
|---|
| 632 | + return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, |
|---|
| 633 | + MLXSW_REG_QEEC_HR_SUBGROUP, |
|---|
| 634 | + mlxsw_sp_qdisc->tclass_num, 0, |
|---|
| 635 | + MLXSW_REG_QEEC_MAS_DIS, 0); |
|---|
| 636 | +} |
|---|
| 637 | + |
|---|
| 638 | +static int |
|---|
| 639 | +mlxsw_sp_qdisc_tbf_bs(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 640 | + u32 max_size, u8 *p_burst_size) |
|---|
| 641 | +{ |
|---|
| 642 | + /* TBF burst size is configured in bytes. The ASIC burst size value is |
|---|
| 643 | + * ((2 ^ bs) * 512 bits. Convert the TBF bytes to 512-bit units. |
|---|
| 644 | + */ |
|---|
| 645 | + u32 bs512 = max_size / 64; |
|---|
| 646 | + u8 bs = fls(bs512); |
|---|
| 647 | + |
|---|
| 648 | + if (!bs) |
|---|
| 649 | + return -EINVAL; |
|---|
| 650 | + --bs; |
|---|
| 651 | + |
|---|
| 652 | + /* Demand a power of two. */ |
|---|
| 653 | + if ((1 << bs) != bs512) |
|---|
| 654 | + return -EINVAL; |
|---|
| 655 | + |
|---|
| 656 | + if (bs < mlxsw_sp_port->mlxsw_sp->lowest_shaper_bs || |
|---|
| 657 | + bs > MLXSW_REG_QEEC_HIGHEST_SHAPER_BS) |
|---|
| 658 | + return -EINVAL; |
|---|
| 659 | + |
|---|
| 660 | + *p_burst_size = bs; |
|---|
| 661 | + return 0; |
|---|
| 662 | +} |
|---|
| 663 | + |
|---|
| 664 | +static u32 |
|---|
| 665 | +mlxsw_sp_qdisc_tbf_max_size(u8 bs) |
|---|
| 666 | +{ |
|---|
| 667 | + return (1U << bs) * 64; |
|---|
| 668 | +} |
|---|
| 669 | + |
|---|
| 670 | +static u64 |
|---|
| 671 | +mlxsw_sp_qdisc_tbf_rate_kbps(struct tc_tbf_qopt_offload_replace_params *p) |
|---|
| 672 | +{ |
|---|
| 673 | + /* TBF interface is in bytes/s, whereas Spectrum ASIC is configured in |
|---|
| 674 | + * Kbits/s. |
|---|
| 675 | + */ |
|---|
| 676 | + return div_u64(p->rate.rate_bytes_ps, 1000) * 8; |
|---|
| 677 | +} |
|---|
| 678 | + |
|---|
| 679 | +static int |
|---|
| 680 | +mlxsw_sp_qdisc_tbf_check_params(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 681 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 682 | + void *params) |
|---|
| 683 | +{ |
|---|
| 684 | + struct tc_tbf_qopt_offload_replace_params *p = params; |
|---|
| 685 | + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
|---|
| 686 | + u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p); |
|---|
| 687 | + u8 burst_size; |
|---|
| 688 | + int err; |
|---|
| 689 | + |
|---|
| 690 | + if (rate_kbps >= MLXSW_REG_QEEC_MAS_DIS) { |
|---|
| 691 | + dev_err(mlxsw_sp_port->mlxsw_sp->bus_info->dev, |
|---|
| 692 | + "spectrum: TBF: rate of %lluKbps must be below %u\n", |
|---|
| 693 | + rate_kbps, MLXSW_REG_QEEC_MAS_DIS); |
|---|
| 694 | + return -EINVAL; |
|---|
| 695 | + } |
|---|
| 696 | + |
|---|
| 697 | + err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size); |
|---|
| 698 | + if (err) { |
|---|
| 699 | + u8 highest_shaper_bs = MLXSW_REG_QEEC_HIGHEST_SHAPER_BS; |
|---|
| 700 | + |
|---|
| 701 | + dev_err(mlxsw_sp->bus_info->dev, |
|---|
| 702 | + "spectrum: TBF: invalid burst size of %u, must be a power of two between %u and %u", |
|---|
| 703 | + p->max_size, |
|---|
| 704 | + mlxsw_sp_qdisc_tbf_max_size(mlxsw_sp->lowest_shaper_bs), |
|---|
| 705 | + mlxsw_sp_qdisc_tbf_max_size(highest_shaper_bs)); |
|---|
| 706 | + return -EINVAL; |
|---|
| 707 | + } |
|---|
| 708 | + |
|---|
| 709 | + return 0; |
|---|
| 710 | +} |
|---|
| 711 | + |
|---|
| 712 | +static int |
|---|
| 713 | +mlxsw_sp_qdisc_tbf_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, |
|---|
| 714 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 715 | + void *params) |
|---|
| 716 | +{ |
|---|
| 717 | + struct tc_tbf_qopt_offload_replace_params *p = params; |
|---|
| 718 | + u64 rate_kbps = mlxsw_sp_qdisc_tbf_rate_kbps(p); |
|---|
| 719 | + u8 burst_size; |
|---|
| 720 | + int err; |
|---|
| 721 | + |
|---|
| 722 | + err = mlxsw_sp_qdisc_tbf_bs(mlxsw_sp_port, p->max_size, &burst_size); |
|---|
| 723 | + if (WARN_ON_ONCE(err)) |
|---|
| 724 | + /* check_params above was supposed to reject this value. */ |
|---|
| 725 | + return -EINVAL; |
|---|
| 726 | + |
|---|
| 727 | + /* Configure subgroup shaper, so that both UC and MC traffic is subject |
|---|
| 728 | + * to shaping. That is unlike RED, however UC queue lengths are going to |
|---|
| 729 | + * be different than MC ones due to different pool and quota |
|---|
| 730 | + * configurations, so the configuration is not applicable. For shaper on |
|---|
| 731 | + * the other hand, subjecting the overall stream to the configured |
|---|
| 732 | + * shaper makes sense. Also note that that is what we do for |
|---|
| 733 | + * ieee_setmaxrate(). |
|---|
| 734 | + */ |
|---|
| 735 | + return mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, |
|---|
| 736 | + MLXSW_REG_QEEC_HR_SUBGROUP, |
|---|
| 737 | + mlxsw_sp_qdisc->tclass_num, 0, |
|---|
| 738 | + rate_kbps, burst_size); |
|---|
| 739 | +} |
|---|
| 740 | + |
|---|
| 741 | +static void |
|---|
| 742 | +mlxsw_sp_qdisc_tbf_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 743 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 744 | + void *params) |
|---|
| 745 | +{ |
|---|
| 746 | + struct tc_tbf_qopt_offload_replace_params *p = params; |
|---|
| 747 | + |
|---|
| 748 | + mlxsw_sp_qdisc_leaf_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, p->qstats); |
|---|
| 749 | +} |
|---|
| 750 | + |
|---|
| 751 | +static int |
|---|
| 752 | +mlxsw_sp_qdisc_get_tbf_stats(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 753 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 754 | + struct tc_qopt_offload_stats *stats_ptr) |
|---|
| 755 | +{ |
|---|
| 756 | + mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, |
|---|
| 757 | + stats_ptr); |
|---|
| 758 | + return 0; |
|---|
| 759 | +} |
|---|
| 760 | + |
|---|
| 761 | +static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_tbf = { |
|---|
| 762 | + .type = MLXSW_SP_QDISC_TBF, |
|---|
| 763 | + .check_params = mlxsw_sp_qdisc_tbf_check_params, |
|---|
| 764 | + .replace = mlxsw_sp_qdisc_tbf_replace, |
|---|
| 765 | + .unoffload = mlxsw_sp_qdisc_tbf_unoffload, |
|---|
| 766 | + .destroy = mlxsw_sp_qdisc_tbf_destroy, |
|---|
| 767 | + .get_stats = mlxsw_sp_qdisc_get_tbf_stats, |
|---|
| 768 | + .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, |
|---|
| 769 | +}; |
|---|
| 770 | + |
|---|
| 771 | +int mlxsw_sp_setup_tc_tbf(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 772 | + struct tc_tbf_qopt_offload *p) |
|---|
| 773 | +{ |
|---|
| 774 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; |
|---|
| 775 | + |
|---|
| 776 | + mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); |
|---|
| 777 | + if (!mlxsw_sp_qdisc) |
|---|
| 778 | + return -EOPNOTSUPP; |
|---|
| 779 | + |
|---|
| 780 | + if (p->command == TC_TBF_REPLACE) |
|---|
| 781 | + return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, |
|---|
| 782 | + mlxsw_sp_qdisc, |
|---|
| 783 | + &mlxsw_sp_qdisc_ops_tbf, |
|---|
| 784 | + &p->replace_params); |
|---|
| 785 | + |
|---|
| 786 | + if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, |
|---|
| 787 | + MLXSW_SP_QDISC_TBF)) |
|---|
| 788 | + return -EOPNOTSUPP; |
|---|
| 789 | + |
|---|
| 790 | + switch (p->command) { |
|---|
| 791 | + case TC_TBF_DESTROY: |
|---|
| 792 | + return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); |
|---|
| 793 | + case TC_TBF_STATS: |
|---|
| 794 | + return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, |
|---|
| 795 | + &p->stats); |
|---|
| 796 | + default: |
|---|
| 797 | + return -EOPNOTSUPP; |
|---|
| 798 | + } |
|---|
| 799 | +} |
|---|
| 800 | + |
|---|
| 801 | +static int |
|---|
| 802 | +mlxsw_sp_qdisc_fifo_destroy(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 490 | 803 | struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) |
|---|
| 491 | 804 | { |
|---|
| 805 | + struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; |
|---|
| 806 | + struct mlxsw_sp_qdisc *root_qdisc = &qdisc_state->root_qdisc; |
|---|
| 807 | + |
|---|
| 808 | + if (root_qdisc != mlxsw_sp_qdisc) |
|---|
| 809 | + root_qdisc->stats_base.backlog -= |
|---|
| 810 | + mlxsw_sp_qdisc->stats_base.backlog; |
|---|
| 811 | + return 0; |
|---|
| 812 | +} |
|---|
| 813 | + |
|---|
| 814 | +static int |
|---|
| 815 | +mlxsw_sp_qdisc_fifo_check_params(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 816 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 817 | + void *params) |
|---|
| 818 | +{ |
|---|
| 819 | + return 0; |
|---|
| 820 | +} |
|---|
| 821 | + |
|---|
| 822 | +static int |
|---|
| 823 | +mlxsw_sp_qdisc_fifo_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, |
|---|
| 824 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 825 | + void *params) |
|---|
| 826 | +{ |
|---|
| 827 | + return 0; |
|---|
| 828 | +} |
|---|
| 829 | + |
|---|
| 830 | +static int |
|---|
| 831 | +mlxsw_sp_qdisc_get_fifo_stats(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 832 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 833 | + struct tc_qopt_offload_stats *stats_ptr) |
|---|
| 834 | +{ |
|---|
| 835 | + mlxsw_sp_qdisc_get_tc_stats(mlxsw_sp_port, mlxsw_sp_qdisc, |
|---|
| 836 | + stats_ptr); |
|---|
| 837 | + return 0; |
|---|
| 838 | +} |
|---|
| 839 | + |
|---|
| 840 | +static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_fifo = { |
|---|
| 841 | + .type = MLXSW_SP_QDISC_FIFO, |
|---|
| 842 | + .check_params = mlxsw_sp_qdisc_fifo_check_params, |
|---|
| 843 | + .replace = mlxsw_sp_qdisc_fifo_replace, |
|---|
| 844 | + .destroy = mlxsw_sp_qdisc_fifo_destroy, |
|---|
| 845 | + .get_stats = mlxsw_sp_qdisc_get_fifo_stats, |
|---|
| 846 | + .clean_stats = mlxsw_sp_setup_tc_qdisc_leaf_clean_stats, |
|---|
| 847 | +}; |
|---|
| 848 | + |
|---|
| 849 | +int mlxsw_sp_setup_tc_fifo(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 850 | + struct tc_fifo_qopt_offload *p) |
|---|
| 851 | +{ |
|---|
| 852 | + struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; |
|---|
| 853 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; |
|---|
| 854 | + int tclass, child_index; |
|---|
| 855 | + u32 parent_handle; |
|---|
| 856 | + |
|---|
| 857 | + /* Invisible FIFOs are tracked in future_handle and future_fifos. Make |
|---|
| 858 | + * sure that not more than one qdisc is created for a port at a time. |
|---|
| 859 | + * RTNL is a simple proxy for that. |
|---|
| 860 | + */ |
|---|
| 861 | + ASSERT_RTNL(); |
|---|
| 862 | + |
|---|
| 863 | + mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false); |
|---|
| 864 | + if (!mlxsw_sp_qdisc && p->handle == TC_H_UNSPEC) { |
|---|
| 865 | + parent_handle = TC_H_MAJ(p->parent); |
|---|
| 866 | + if (parent_handle != qdisc_state->future_handle) { |
|---|
| 867 | + /* This notifications is for a different Qdisc than |
|---|
| 868 | + * previously. Wipe the future cache. |
|---|
| 869 | + */ |
|---|
| 870 | + memset(qdisc_state->future_fifos, 0, |
|---|
| 871 | + sizeof(qdisc_state->future_fifos)); |
|---|
| 872 | + qdisc_state->future_handle = parent_handle; |
|---|
| 873 | + } |
|---|
| 874 | + |
|---|
| 875 | + child_index = TC_H_MIN(p->parent); |
|---|
| 876 | + tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index); |
|---|
| 877 | + if (tclass < IEEE_8021QAZ_MAX_TCS) { |
|---|
| 878 | + if (p->command == TC_FIFO_REPLACE) |
|---|
| 879 | + qdisc_state->future_fifos[tclass] = true; |
|---|
| 880 | + else if (p->command == TC_FIFO_DESTROY) |
|---|
| 881 | + qdisc_state->future_fifos[tclass] = false; |
|---|
| 882 | + } |
|---|
| 883 | + } |
|---|
| 884 | + if (!mlxsw_sp_qdisc) |
|---|
| 885 | + return -EOPNOTSUPP; |
|---|
| 886 | + |
|---|
| 887 | + if (p->command == TC_FIFO_REPLACE) { |
|---|
| 888 | + return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, |
|---|
| 889 | + mlxsw_sp_qdisc, |
|---|
| 890 | + &mlxsw_sp_qdisc_ops_fifo, NULL); |
|---|
| 891 | + } |
|---|
| 892 | + |
|---|
| 893 | + if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, |
|---|
| 894 | + MLXSW_SP_QDISC_FIFO)) |
|---|
| 895 | + return -EOPNOTSUPP; |
|---|
| 896 | + |
|---|
| 897 | + switch (p->command) { |
|---|
| 898 | + case TC_FIFO_DESTROY: |
|---|
| 899 | + if (p->handle == mlxsw_sp_qdisc->handle) |
|---|
| 900 | + return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, |
|---|
| 901 | + mlxsw_sp_qdisc); |
|---|
| 902 | + return 0; |
|---|
| 903 | + case TC_FIFO_STATS: |
|---|
| 904 | + return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, |
|---|
| 905 | + &p->stats); |
|---|
| 906 | + case TC_FIFO_REPLACE: /* Handled above. */ |
|---|
| 907 | + break; |
|---|
| 908 | + } |
|---|
| 909 | + |
|---|
| 910 | + return -EOPNOTSUPP; |
|---|
| 911 | +} |
|---|
| 912 | + |
|---|
| 913 | +static int |
|---|
| 914 | +__mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port) |
|---|
| 915 | +{ |
|---|
| 916 | + struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; |
|---|
| 492 | 917 | int i; |
|---|
| 493 | 918 | |
|---|
| 494 | 919 | for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { |
|---|
| 495 | 920 | mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, |
|---|
| 496 | 921 | MLXSW_SP_PORT_DEFAULT_TCLASS); |
|---|
| 922 | + mlxsw_sp_port_ets_set(mlxsw_sp_port, |
|---|
| 923 | + MLXSW_REG_QEEC_HR_SUBGROUP, |
|---|
| 924 | + i, 0, false, 0); |
|---|
| 497 | 925 | mlxsw_sp_qdisc_destroy(mlxsw_sp_port, |
|---|
| 498 | | - &mlxsw_sp_port->tclass_qdiscs[i]); |
|---|
| 499 | | - mlxsw_sp_port->tclass_qdiscs[i].prio_bitmap = 0; |
|---|
| 926 | + &qdisc_state->tclass_qdiscs[i]); |
|---|
| 927 | + qdisc_state->tclass_qdiscs[i].prio_bitmap = 0; |
|---|
| 500 | 928 | } |
|---|
| 929 | + |
|---|
| 930 | + return 0; |
|---|
| 931 | +} |
|---|
| 932 | + |
|---|
| 933 | +static int |
|---|
| 934 | +mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 935 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) |
|---|
| 936 | +{ |
|---|
| 937 | + return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port); |
|---|
| 938 | +} |
|---|
| 939 | + |
|---|
| 940 | +static int |
|---|
| 941 | +__mlxsw_sp_qdisc_ets_check_params(unsigned int nbands) |
|---|
| 942 | +{ |
|---|
| 943 | + if (nbands > IEEE_8021QAZ_MAX_TCS) |
|---|
| 944 | + return -EOPNOTSUPP; |
|---|
| 501 | 945 | |
|---|
| 502 | 946 | return 0; |
|---|
| 503 | 947 | } |
|---|
| .. | .. |
|---|
| 509 | 953 | { |
|---|
| 510 | 954 | struct tc_prio_qopt_offload_params *p = params; |
|---|
| 511 | 955 | |
|---|
| 512 | | - if (p->bands > IEEE_8021QAZ_MAX_TCS) |
|---|
| 513 | | - return -EOPNOTSUPP; |
|---|
| 514 | | - |
|---|
| 515 | | - return 0; |
|---|
| 956 | + return __mlxsw_sp_qdisc_ets_check_params(p->bands); |
|---|
| 516 | 957 | } |
|---|
| 517 | 958 | |
|---|
| 518 | 959 | static int |
|---|
| 519 | | -mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 520 | | - struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 521 | | - void *params) |
|---|
| 960 | +__mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, |
|---|
| 961 | + unsigned int nbands, |
|---|
| 962 | + const unsigned int *quanta, |
|---|
| 963 | + const unsigned int *weights, |
|---|
| 964 | + const u8 *priomap) |
|---|
| 522 | 965 | { |
|---|
| 523 | | - struct tc_prio_qopt_offload_params *p = params; |
|---|
| 966 | + struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; |
|---|
| 524 | 967 | struct mlxsw_sp_qdisc *child_qdisc; |
|---|
| 525 | 968 | int tclass, i, band, backlog; |
|---|
| 526 | 969 | u8 old_priomap; |
|---|
| 527 | 970 | int err; |
|---|
| 528 | 971 | |
|---|
| 529 | | - for (band = 0; band < p->bands; band++) { |
|---|
| 972 | + for (band = 0; band < nbands; band++) { |
|---|
| 530 | 973 | tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); |
|---|
| 531 | | - child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass]; |
|---|
| 974 | + child_qdisc = &qdisc_state->tclass_qdiscs[tclass]; |
|---|
| 532 | 975 | old_priomap = child_qdisc->prio_bitmap; |
|---|
| 533 | 976 | child_qdisc->prio_bitmap = 0; |
|---|
| 977 | + |
|---|
| 978 | + err = mlxsw_sp_port_ets_set(mlxsw_sp_port, |
|---|
| 979 | + MLXSW_REG_QEEC_HR_SUBGROUP, |
|---|
| 980 | + tclass, 0, !!quanta[band], |
|---|
| 981 | + weights[band]); |
|---|
| 982 | + if (err) |
|---|
| 983 | + return err; |
|---|
| 984 | + |
|---|
| 534 | 985 | for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { |
|---|
| 535 | | - if (p->priomap[i] == band) { |
|---|
| 986 | + if (priomap[i] == band) { |
|---|
| 536 | 987 | child_qdisc->prio_bitmap |= BIT(i); |
|---|
| 537 | 988 | if (BIT(i) & old_priomap) |
|---|
| 538 | 989 | continue; |
|---|
| .. | .. |
|---|
| 549 | 1000 | child_qdisc); |
|---|
| 550 | 1001 | child_qdisc->stats_base.backlog = backlog; |
|---|
| 551 | 1002 | } |
|---|
| 1003 | + |
|---|
| 1004 | + if (handle == qdisc_state->future_handle && |
|---|
| 1005 | + qdisc_state->future_fifos[tclass]) { |
|---|
| 1006 | + err = mlxsw_sp_qdisc_replace(mlxsw_sp_port, TC_H_UNSPEC, |
|---|
| 1007 | + child_qdisc, |
|---|
| 1008 | + &mlxsw_sp_qdisc_ops_fifo, |
|---|
| 1009 | + NULL); |
|---|
| 1010 | + if (err) |
|---|
| 1011 | + return err; |
|---|
| 1012 | + } |
|---|
| 552 | 1013 | } |
|---|
| 553 | 1014 | for (; band < IEEE_8021QAZ_MAX_TCS; band++) { |
|---|
| 554 | 1015 | tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); |
|---|
| 555 | | - child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass]; |
|---|
| 1016 | + child_qdisc = &qdisc_state->tclass_qdiscs[tclass]; |
|---|
| 556 | 1017 | child_qdisc->prio_bitmap = 0; |
|---|
| 557 | 1018 | mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc); |
|---|
| 1019 | + mlxsw_sp_port_ets_set(mlxsw_sp_port, |
|---|
| 1020 | + MLXSW_REG_QEEC_HR_SUBGROUP, |
|---|
| 1021 | + tclass, 0, false, 0); |
|---|
| 558 | 1022 | } |
|---|
| 1023 | + |
|---|
| 1024 | + qdisc_state->future_handle = TC_H_UNSPEC; |
|---|
| 1025 | + memset(qdisc_state->future_fifos, 0, sizeof(qdisc_state->future_fifos)); |
|---|
| 559 | 1026 | return 0; |
|---|
| 1027 | +} |
|---|
| 1028 | + |
|---|
| 1029 | +static int |
|---|
| 1030 | +mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, |
|---|
| 1031 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 1032 | + void *params) |
|---|
| 1033 | +{ |
|---|
| 1034 | + struct tc_prio_qopt_offload_params *p = params; |
|---|
| 1035 | + unsigned int zeroes[TCQ_ETS_MAX_BANDS] = {0}; |
|---|
| 1036 | + |
|---|
| 1037 | + return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands, |
|---|
| 1038 | + zeroes, zeroes, p->priomap); |
|---|
| 1039 | +} |
|---|
| 1040 | + |
|---|
| 1041 | +static void |
|---|
| 1042 | +__mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 1043 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 1044 | + struct gnet_stats_queue *qstats) |
|---|
| 1045 | +{ |
|---|
| 1046 | + u64 backlog; |
|---|
| 1047 | + |
|---|
| 1048 | + backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, |
|---|
| 1049 | + mlxsw_sp_qdisc->stats_base.backlog); |
|---|
| 1050 | + qstats->backlog -= backlog; |
|---|
| 560 | 1051 | } |
|---|
| 561 | 1052 | |
|---|
| 562 | 1053 | static void |
|---|
| .. | .. |
|---|
| 565 | 1056 | void *params) |
|---|
| 566 | 1057 | { |
|---|
| 567 | 1058 | struct tc_prio_qopt_offload_params *p = params; |
|---|
| 568 | | - u64 backlog; |
|---|
| 569 | 1059 | |
|---|
| 570 | | - backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, |
|---|
| 571 | | - mlxsw_sp_qdisc->stats_base.backlog); |
|---|
| 572 | | - p->qstats->backlog -= backlog; |
|---|
| 1060 | + __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, |
|---|
| 1061 | + p->qstats); |
|---|
| 573 | 1062 | } |
|---|
| 574 | 1063 | |
|---|
| 575 | 1064 | static int |
|---|
| .. | .. |
|---|
| 577 | 1066 | struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 578 | 1067 | struct tc_qopt_offload_stats *stats_ptr) |
|---|
| 579 | 1068 | { |
|---|
| 580 | | - u64 tx_bytes, tx_packets, drops = 0, backlog = 0; |
|---|
| 581 | | - struct mlxsw_sp_qdisc_stats *stats_base; |
|---|
| 582 | | - struct mlxsw_sp_port_xstats *xstats; |
|---|
| 583 | | - struct rtnl_link_stats64 *stats; |
|---|
| 1069 | + struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; |
|---|
| 1070 | + struct mlxsw_sp_qdisc *tc_qdisc; |
|---|
| 1071 | + u64 tx_packets = 0; |
|---|
| 1072 | + u64 tx_bytes = 0; |
|---|
| 1073 | + u64 backlog = 0; |
|---|
| 1074 | + u64 drops = 0; |
|---|
| 584 | 1075 | int i; |
|---|
| 585 | 1076 | |
|---|
| 586 | | - xstats = &mlxsw_sp_port->periodic_hw_stats.xstats; |
|---|
| 587 | | - stats = &mlxsw_sp_port->periodic_hw_stats.stats; |
|---|
| 588 | | - stats_base = &mlxsw_sp_qdisc->stats_base; |
|---|
| 589 | | - |
|---|
| 590 | | - tx_bytes = stats->tx_bytes - stats_base->tx_bytes; |
|---|
| 591 | | - tx_packets = stats->tx_packets - stats_base->tx_packets; |
|---|
| 592 | | - |
|---|
| 593 | 1077 | for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { |
|---|
| 594 | | - drops += mlxsw_sp_xstats_tail_drop(xstats, i); |
|---|
| 595 | | - drops += xstats->wred_drop[i]; |
|---|
| 596 | | - backlog += mlxsw_sp_xstats_backlog(xstats, i); |
|---|
| 1078 | + tc_qdisc = &qdisc_state->tclass_qdiscs[i]; |
|---|
| 1079 | + mlxsw_sp_qdisc_collect_tc_stats(mlxsw_sp_port, tc_qdisc, |
|---|
| 1080 | + &tx_bytes, &tx_packets, |
|---|
| 1081 | + &drops, &backlog); |
|---|
| 597 | 1082 | } |
|---|
| 598 | | - drops = drops - stats_base->drops; |
|---|
| 599 | 1083 | |
|---|
| 600 | | - _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets); |
|---|
| 601 | | - stats_ptr->qstats->drops += drops; |
|---|
| 602 | | - stats_ptr->qstats->backlog += |
|---|
| 603 | | - mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, |
|---|
| 604 | | - backlog) - |
|---|
| 605 | | - mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp, |
|---|
| 606 | | - stats_base->backlog); |
|---|
| 607 | | - stats_base->backlog = backlog; |
|---|
| 608 | | - stats_base->drops += drops; |
|---|
| 609 | | - stats_base->tx_bytes += tx_bytes; |
|---|
| 610 | | - stats_base->tx_packets += tx_packets; |
|---|
| 1084 | + mlxsw_sp_qdisc_update_stats(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_qdisc, |
|---|
| 1085 | + tx_bytes, tx_packets, drops, backlog, |
|---|
| 1086 | + stats_ptr); |
|---|
| 611 | 1087 | return 0; |
|---|
| 612 | 1088 | } |
|---|
| 613 | 1089 | |
|---|
| .. | .. |
|---|
| 646 | 1122 | .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, |
|---|
| 647 | 1123 | }; |
|---|
| 648 | 1124 | |
|---|
| 649 | | -/* Grafting is not supported in mlxsw. It will result in un-offloading of the |
|---|
| 650 | | - * grafted qdisc as well as the qdisc in the qdisc new location. |
|---|
| 651 | | - * (However, if the graft is to the location where the qdisc is already at, it |
|---|
| 652 | | - * will be ignored completely and won't cause un-offloading). |
|---|
| 1125 | +static int |
|---|
| 1126 | +mlxsw_sp_qdisc_ets_check_params(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 1127 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 1128 | + void *params) |
|---|
| 1129 | +{ |
|---|
| 1130 | + struct tc_ets_qopt_offload_replace_params *p = params; |
|---|
| 1131 | + |
|---|
| 1132 | + return __mlxsw_sp_qdisc_ets_check_params(p->bands); |
|---|
| 1133 | +} |
|---|
| 1134 | + |
|---|
| 1135 | +static int |
|---|
| 1136 | +mlxsw_sp_qdisc_ets_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, |
|---|
| 1137 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 1138 | + void *params) |
|---|
| 1139 | +{ |
|---|
| 1140 | + struct tc_ets_qopt_offload_replace_params *p = params; |
|---|
| 1141 | + |
|---|
| 1142 | + return __mlxsw_sp_qdisc_ets_replace(mlxsw_sp_port, handle, p->bands, |
|---|
| 1143 | + p->quanta, p->weights, p->priomap); |
|---|
| 1144 | +} |
|---|
| 1145 | + |
|---|
| 1146 | +static void |
|---|
| 1147 | +mlxsw_sp_qdisc_ets_unoffload(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 1148 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 1149 | + void *params) |
|---|
| 1150 | +{ |
|---|
| 1151 | + struct tc_ets_qopt_offload_replace_params *p = params; |
|---|
| 1152 | + |
|---|
| 1153 | + __mlxsw_sp_qdisc_ets_unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, |
|---|
| 1154 | + p->qstats); |
|---|
| 1155 | +} |
|---|
| 1156 | + |
|---|
| 1157 | +static int |
|---|
| 1158 | +mlxsw_sp_qdisc_ets_destroy(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 1159 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc) |
|---|
| 1160 | +{ |
|---|
| 1161 | + return __mlxsw_sp_qdisc_ets_destroy(mlxsw_sp_port); |
|---|
| 1162 | +} |
|---|
| 1163 | + |
|---|
| 1164 | +static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_ets = { |
|---|
| 1165 | + .type = MLXSW_SP_QDISC_ETS, |
|---|
| 1166 | + .check_params = mlxsw_sp_qdisc_ets_check_params, |
|---|
| 1167 | + .replace = mlxsw_sp_qdisc_ets_replace, |
|---|
| 1168 | + .unoffload = mlxsw_sp_qdisc_ets_unoffload, |
|---|
| 1169 | + .destroy = mlxsw_sp_qdisc_ets_destroy, |
|---|
| 1170 | + .get_stats = mlxsw_sp_qdisc_get_prio_stats, |
|---|
| 1171 | + .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats, |
|---|
| 1172 | +}; |
|---|
| 1173 | + |
|---|
| 1174 | +/* Linux allows linking of Qdiscs to arbitrary classes (so long as the resulting |
|---|
| 1175 | + * graph is free of cycles). These operations do not change the parent handle |
|---|
| 1176 | + * though, which means it can be incomplete (if there is more than one class |
|---|
| 1177 | + * where the Qdisc in question is grafted) or outright wrong (if the Qdisc was |
|---|
| 1178 | + * linked to a different class and then removed from the original class). |
|---|
| 1179 | + * |
|---|
| 1180 | + * E.g. consider this sequence of operations: |
|---|
| 1181 | + * |
|---|
| 1182 | + * # tc qdisc add dev swp1 root handle 1: prio |
|---|
| 1183 | + * # tc qdisc add dev swp1 parent 1:3 handle 13: red limit 1000000 avpkt 10000 |
|---|
| 1184 | + * RED: set bandwidth to 10Mbit |
|---|
| 1185 | + * # tc qdisc link dev swp1 handle 13: parent 1:2 |
|---|
| 1186 | + * |
|---|
| 1187 | + * At this point, both 1:2 and 1:3 have the same RED Qdisc instance as their |
|---|
| 1188 | + * child. But RED will still only claim that 1:3 is its parent. If it's removed |
|---|
| 1189 | + * from that band, its only parent will be 1:2, but it will continue to claim |
|---|
| 1190 | + * that it is in fact 1:3. |
|---|
| 1191 | + * |
|---|
| 1192 | + * The notification for child Qdisc replace (e.g. TC_RED_REPLACE) comes before |
|---|
| 1193 | + * the notification for parent graft (e.g. TC_PRIO_GRAFT). We take the replace |
|---|
| 1194 | + * notification to offload the child Qdisc, based on its parent handle, and use |
|---|
| 1195 | + * the graft operation to validate that the class where the child is actually |
|---|
| 1196 | + * grafted corresponds to the parent handle. If the two don't match, we |
|---|
| 1197 | + * unoffload the child. |
|---|
| 653 | 1198 | */ |
|---|
| 654 | 1199 | static int |
|---|
| 655 | | -mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 656 | | - struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 657 | | - struct tc_prio_qopt_offload_graft_params *p) |
|---|
| 1200 | +__mlxsw_sp_qdisc_ets_graft(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 1201 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 1202 | + u8 band, u32 child_handle) |
|---|
| 658 | 1203 | { |
|---|
| 659 | | - int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(p->band); |
|---|
| 1204 | + struct mlxsw_sp_qdisc_state *qdisc_state = mlxsw_sp_port->qdisc; |
|---|
| 1205 | + int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(band); |
|---|
| 660 | 1206 | struct mlxsw_sp_qdisc *old_qdisc; |
|---|
| 661 | 1207 | |
|---|
| 662 | | - /* Check if the grafted qdisc is already in its "new" location. If so - |
|---|
| 663 | | - * nothing needs to be done. |
|---|
| 664 | | - */ |
|---|
| 665 | | - if (p->band < IEEE_8021QAZ_MAX_TCS && |
|---|
| 666 | | - mlxsw_sp_port->tclass_qdiscs[tclass_num].handle == p->child_handle) |
|---|
| 1208 | + if (band < IEEE_8021QAZ_MAX_TCS && |
|---|
| 1209 | + qdisc_state->tclass_qdiscs[tclass_num].handle == child_handle) |
|---|
| 667 | 1210 | return 0; |
|---|
| 668 | 1211 | |
|---|
| 669 | | - if (!p->child_handle) { |
|---|
| 1212 | + if (!child_handle) { |
|---|
| 670 | 1213 | /* This is an invisible FIFO replacing the original Qdisc. |
|---|
| 671 | 1214 | * Ignore it--the original Qdisc's destroy will follow. |
|---|
| 672 | 1215 | */ |
|---|
| .. | .. |
|---|
| 677 | 1220 | * unoffload it. |
|---|
| 678 | 1221 | */ |
|---|
| 679 | 1222 | old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, |
|---|
| 680 | | - p->child_handle); |
|---|
| 1223 | + child_handle); |
|---|
| 681 | 1224 | if (old_qdisc) |
|---|
| 682 | 1225 | mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc); |
|---|
| 683 | 1226 | |
|---|
| 684 | 1227 | mlxsw_sp_qdisc_destroy(mlxsw_sp_port, |
|---|
| 685 | | - &mlxsw_sp_port->tclass_qdiscs[tclass_num]); |
|---|
| 1228 | + &qdisc_state->tclass_qdiscs[tclass_num]); |
|---|
| 686 | 1229 | return -EOPNOTSUPP; |
|---|
| 1230 | +} |
|---|
| 1231 | + |
|---|
| 1232 | +static int |
|---|
| 1233 | +mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 1234 | + struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, |
|---|
| 1235 | + struct tc_prio_qopt_offload_graft_params *p) |
|---|
| 1236 | +{ |
|---|
| 1237 | + return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc, |
|---|
| 1238 | + p->band, p->child_handle); |
|---|
| 687 | 1239 | } |
|---|
| 688 | 1240 | |
|---|
| 689 | 1241 | int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| .. | .. |
|---|
| 719 | 1271 | } |
|---|
| 720 | 1272 | } |
|---|
| 721 | 1273 | |
|---|
| 722 | | -int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port) |
|---|
| 1274 | +int mlxsw_sp_setup_tc_ets(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 1275 | + struct tc_ets_qopt_offload *p) |
|---|
| 723 | 1276 | { |
|---|
| 724 | 1277 | struct mlxsw_sp_qdisc *mlxsw_sp_qdisc; |
|---|
| 725 | | - int i; |
|---|
| 726 | 1278 | |
|---|
| 727 | | - mlxsw_sp_qdisc = kzalloc(sizeof(*mlxsw_sp_qdisc), GFP_KERNEL); |
|---|
| 1279 | + mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true); |
|---|
| 728 | 1280 | if (!mlxsw_sp_qdisc) |
|---|
| 729 | | - goto err_root_qdisc_init; |
|---|
| 1281 | + return -EOPNOTSUPP; |
|---|
| 730 | 1282 | |
|---|
| 731 | | - mlxsw_sp_port->root_qdisc = mlxsw_sp_qdisc; |
|---|
| 732 | | - mlxsw_sp_port->root_qdisc->prio_bitmap = 0xff; |
|---|
| 733 | | - mlxsw_sp_port->root_qdisc->tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS; |
|---|
| 1283 | + if (p->command == TC_ETS_REPLACE) |
|---|
| 1284 | + return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle, |
|---|
| 1285 | + mlxsw_sp_qdisc, |
|---|
| 1286 | + &mlxsw_sp_qdisc_ops_ets, |
|---|
| 1287 | + &p->replace_params); |
|---|
| 734 | 1288 | |
|---|
| 735 | | - mlxsw_sp_qdisc = kcalloc(IEEE_8021QAZ_MAX_TCS, |
|---|
| 736 | | - sizeof(*mlxsw_sp_qdisc), |
|---|
| 737 | | - GFP_KERNEL); |
|---|
| 738 | | - if (!mlxsw_sp_qdisc) |
|---|
| 739 | | - goto err_tclass_qdiscs_init; |
|---|
| 1289 | + if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle, |
|---|
| 1290 | + MLXSW_SP_QDISC_ETS)) |
|---|
| 1291 | + return -EOPNOTSUPP; |
|---|
| 740 | 1292 | |
|---|
| 741 | | - mlxsw_sp_port->tclass_qdiscs = mlxsw_sp_qdisc; |
|---|
| 742 | | - for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) |
|---|
| 743 | | - mlxsw_sp_port->tclass_qdiscs[i].tclass_num = i; |
|---|
| 1293 | + switch (p->command) { |
|---|
| 1294 | + case TC_ETS_DESTROY: |
|---|
| 1295 | + return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc); |
|---|
| 1296 | + case TC_ETS_STATS: |
|---|
| 1297 | + return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc, |
|---|
| 1298 | + &p->stats); |
|---|
| 1299 | + case TC_ETS_GRAFT: |
|---|
| 1300 | + return __mlxsw_sp_qdisc_ets_graft(mlxsw_sp_port, mlxsw_sp_qdisc, |
|---|
| 1301 | + p->graft_params.band, |
|---|
| 1302 | + p->graft_params.child_handle); |
|---|
| 1303 | + default: |
|---|
| 1304 | + return -EOPNOTSUPP; |
|---|
| 1305 | + } |
|---|
| 1306 | +} |
|---|
| 1307 | + |
|---|
| 1308 | +struct mlxsw_sp_qevent_block { |
|---|
| 1309 | + struct list_head binding_list; |
|---|
| 1310 | + struct list_head mall_entry_list; |
|---|
| 1311 | + struct mlxsw_sp *mlxsw_sp; |
|---|
| 1312 | +}; |
|---|
| 1313 | + |
|---|
| 1314 | +struct mlxsw_sp_qevent_binding { |
|---|
| 1315 | + struct list_head list; |
|---|
| 1316 | + struct mlxsw_sp_port *mlxsw_sp_port; |
|---|
| 1317 | + u32 handle; |
|---|
| 1318 | + int tclass_num; |
|---|
| 1319 | + enum mlxsw_sp_span_trigger span_trigger; |
|---|
| 1320 | +}; |
|---|
| 1321 | + |
|---|
| 1322 | +static LIST_HEAD(mlxsw_sp_qevent_block_cb_list); |
|---|
| 1323 | + |
|---|
| 1324 | +static int mlxsw_sp_qevent_span_configure(struct mlxsw_sp *mlxsw_sp, |
|---|
| 1325 | + struct mlxsw_sp_mall_entry *mall_entry, |
|---|
| 1326 | + struct mlxsw_sp_qevent_binding *qevent_binding, |
|---|
| 1327 | + const struct mlxsw_sp_span_agent_parms *agent_parms, |
|---|
| 1328 | + int *p_span_id) |
|---|
| 1329 | +{ |
|---|
| 1330 | + struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port; |
|---|
| 1331 | + struct mlxsw_sp_span_trigger_parms trigger_parms = {}; |
|---|
| 1332 | + int span_id; |
|---|
| 1333 | + int err; |
|---|
| 1334 | + |
|---|
| 1335 | + err = mlxsw_sp_span_agent_get(mlxsw_sp, &span_id, agent_parms); |
|---|
| 1336 | + if (err) |
|---|
| 1337 | + return err; |
|---|
| 1338 | + |
|---|
| 1339 | + err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, true); |
|---|
| 1340 | + if (err) |
|---|
| 1341 | + goto err_analyzed_port_get; |
|---|
| 1342 | + |
|---|
| 1343 | + trigger_parms.span_id = span_id; |
|---|
| 1344 | + err = mlxsw_sp_span_agent_bind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port, |
|---|
| 1345 | + &trigger_parms); |
|---|
| 1346 | + if (err) |
|---|
| 1347 | + goto err_agent_bind; |
|---|
| 1348 | + |
|---|
| 1349 | + err = mlxsw_sp_span_trigger_enable(mlxsw_sp_port, qevent_binding->span_trigger, |
|---|
| 1350 | + qevent_binding->tclass_num); |
|---|
| 1351 | + if (err) |
|---|
| 1352 | + goto err_trigger_enable; |
|---|
| 1353 | + |
|---|
| 1354 | + *p_span_id = span_id; |
|---|
| 1355 | + return 0; |
|---|
| 1356 | + |
|---|
| 1357 | +err_trigger_enable: |
|---|
| 1358 | + mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port, |
|---|
| 1359 | + &trigger_parms); |
|---|
| 1360 | +err_agent_bind: |
|---|
| 1361 | + mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true); |
|---|
| 1362 | +err_analyzed_port_get: |
|---|
| 1363 | + mlxsw_sp_span_agent_put(mlxsw_sp, span_id); |
|---|
| 1364 | + return err; |
|---|
| 1365 | +} |
|---|
| 1366 | + |
|---|
| 1367 | +static void mlxsw_sp_qevent_span_deconfigure(struct mlxsw_sp *mlxsw_sp, |
|---|
| 1368 | + struct mlxsw_sp_qevent_binding *qevent_binding, |
|---|
| 1369 | + int span_id) |
|---|
| 1370 | +{ |
|---|
| 1371 | + struct mlxsw_sp_port *mlxsw_sp_port = qevent_binding->mlxsw_sp_port; |
|---|
| 1372 | + struct mlxsw_sp_span_trigger_parms trigger_parms = { |
|---|
| 1373 | + .span_id = span_id, |
|---|
| 1374 | + }; |
|---|
| 1375 | + |
|---|
| 1376 | + mlxsw_sp_span_trigger_disable(mlxsw_sp_port, qevent_binding->span_trigger, |
|---|
| 1377 | + qevent_binding->tclass_num); |
|---|
| 1378 | + mlxsw_sp_span_agent_unbind(mlxsw_sp, qevent_binding->span_trigger, mlxsw_sp_port, |
|---|
| 1379 | + &trigger_parms); |
|---|
| 1380 | + mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, true); |
|---|
| 1381 | + mlxsw_sp_span_agent_put(mlxsw_sp, span_id); |
|---|
| 1382 | +} |
|---|
| 1383 | + |
|---|
| 1384 | +static int mlxsw_sp_qevent_mirror_configure(struct mlxsw_sp *mlxsw_sp, |
|---|
| 1385 | + struct mlxsw_sp_mall_entry *mall_entry, |
|---|
| 1386 | + struct mlxsw_sp_qevent_binding *qevent_binding) |
|---|
| 1387 | +{ |
|---|
| 1388 | + struct mlxsw_sp_span_agent_parms agent_parms = { |
|---|
| 1389 | + .to_dev = mall_entry->mirror.to_dev, |
|---|
| 1390 | + }; |
|---|
| 1391 | + |
|---|
| 1392 | + return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding, |
|---|
| 1393 | + &agent_parms, &mall_entry->mirror.span_id); |
|---|
| 1394 | +} |
|---|
| 1395 | + |
|---|
| 1396 | +static void mlxsw_sp_qevent_mirror_deconfigure(struct mlxsw_sp *mlxsw_sp, |
|---|
| 1397 | + struct mlxsw_sp_mall_entry *mall_entry, |
|---|
| 1398 | + struct mlxsw_sp_qevent_binding *qevent_binding) |
|---|
| 1399 | +{ |
|---|
| 1400 | + mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->mirror.span_id); |
|---|
| 1401 | +} |
|---|
| 1402 | + |
|---|
| 1403 | +static int mlxsw_sp_qevent_trap_configure(struct mlxsw_sp *mlxsw_sp, |
|---|
| 1404 | + struct mlxsw_sp_mall_entry *mall_entry, |
|---|
| 1405 | + struct mlxsw_sp_qevent_binding *qevent_binding) |
|---|
| 1406 | +{ |
|---|
| 1407 | + struct mlxsw_sp_span_agent_parms agent_parms = {}; |
|---|
| 1408 | + int err; |
|---|
| 1409 | + |
|---|
| 1410 | + err = mlxsw_sp_trap_group_policer_hw_id_get(mlxsw_sp, |
|---|
| 1411 | + DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS, |
|---|
| 1412 | + &agent_parms.policer_enable, |
|---|
| 1413 | + &agent_parms.policer_id); |
|---|
| 1414 | + if (err) |
|---|
| 1415 | + return err; |
|---|
| 1416 | + |
|---|
| 1417 | + return mlxsw_sp_qevent_span_configure(mlxsw_sp, mall_entry, qevent_binding, |
|---|
| 1418 | + &agent_parms, &mall_entry->trap.span_id); |
|---|
| 1419 | +} |
|---|
| 1420 | + |
|---|
| 1421 | +static void mlxsw_sp_qevent_trap_deconfigure(struct mlxsw_sp *mlxsw_sp, |
|---|
| 1422 | + struct mlxsw_sp_mall_entry *mall_entry, |
|---|
| 1423 | + struct mlxsw_sp_qevent_binding *qevent_binding) |
|---|
| 1424 | +{ |
|---|
| 1425 | + mlxsw_sp_qevent_span_deconfigure(mlxsw_sp, qevent_binding, mall_entry->trap.span_id); |
|---|
| 1426 | +} |
|---|
| 1427 | + |
|---|
| 1428 | +static int mlxsw_sp_qevent_entry_configure(struct mlxsw_sp *mlxsw_sp, |
|---|
| 1429 | + struct mlxsw_sp_mall_entry *mall_entry, |
|---|
| 1430 | + struct mlxsw_sp_qevent_binding *qevent_binding) |
|---|
| 1431 | +{ |
|---|
| 1432 | + switch (mall_entry->type) { |
|---|
| 1433 | + case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: |
|---|
| 1434 | + return mlxsw_sp_qevent_mirror_configure(mlxsw_sp, mall_entry, qevent_binding); |
|---|
| 1435 | + case MLXSW_SP_MALL_ACTION_TYPE_TRAP: |
|---|
| 1436 | + return mlxsw_sp_qevent_trap_configure(mlxsw_sp, mall_entry, qevent_binding); |
|---|
| 1437 | + default: |
|---|
| 1438 | + /* This should have been validated away. */ |
|---|
| 1439 | + WARN_ON(1); |
|---|
| 1440 | + return -EOPNOTSUPP; |
|---|
| 1441 | + } |
|---|
| 1442 | +} |
|---|
| 1443 | + |
|---|
| 1444 | +static void mlxsw_sp_qevent_entry_deconfigure(struct mlxsw_sp *mlxsw_sp, |
|---|
| 1445 | + struct mlxsw_sp_mall_entry *mall_entry, |
|---|
| 1446 | + struct mlxsw_sp_qevent_binding *qevent_binding) |
|---|
| 1447 | +{ |
|---|
| 1448 | + switch (mall_entry->type) { |
|---|
| 1449 | + case MLXSW_SP_MALL_ACTION_TYPE_MIRROR: |
|---|
| 1450 | + return mlxsw_sp_qevent_mirror_deconfigure(mlxsw_sp, mall_entry, qevent_binding); |
|---|
| 1451 | + case MLXSW_SP_MALL_ACTION_TYPE_TRAP: |
|---|
| 1452 | + return mlxsw_sp_qevent_trap_deconfigure(mlxsw_sp, mall_entry, qevent_binding); |
|---|
| 1453 | + default: |
|---|
| 1454 | + WARN_ON(1); |
|---|
| 1455 | + return; |
|---|
| 1456 | + } |
|---|
| 1457 | +} |
|---|
| 1458 | + |
|---|
| 1459 | +static int mlxsw_sp_qevent_binding_configure(struct mlxsw_sp_qevent_block *qevent_block, |
|---|
| 1460 | + struct mlxsw_sp_qevent_binding *qevent_binding) |
|---|
| 1461 | +{ |
|---|
| 1462 | + struct mlxsw_sp_mall_entry *mall_entry; |
|---|
| 1463 | + int err; |
|---|
| 1464 | + |
|---|
| 1465 | + list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) { |
|---|
| 1466 | + err = mlxsw_sp_qevent_entry_configure(qevent_block->mlxsw_sp, mall_entry, |
|---|
| 1467 | + qevent_binding); |
|---|
| 1468 | + if (err) |
|---|
| 1469 | + goto err_entry_configure; |
|---|
| 1470 | + } |
|---|
| 744 | 1471 | |
|---|
| 745 | 1472 | return 0; |
|---|
| 746 | 1473 | |
|---|
| 747 | | -err_tclass_qdiscs_init: |
|---|
| 748 | | - kfree(mlxsw_sp_port->root_qdisc); |
|---|
| 749 | | -err_root_qdisc_init: |
|---|
| 750 | | - return -ENOMEM; |
|---|
| 1474 | +err_entry_configure: |
|---|
| 1475 | + list_for_each_entry_continue_reverse(mall_entry, &qevent_block->mall_entry_list, list) |
|---|
| 1476 | + mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry, |
|---|
| 1477 | + qevent_binding); |
|---|
| 1478 | + return err; |
|---|
| 1479 | +} |
|---|
| 1480 | + |
|---|
| 1481 | +static void mlxsw_sp_qevent_binding_deconfigure(struct mlxsw_sp_qevent_block *qevent_block, |
|---|
| 1482 | + struct mlxsw_sp_qevent_binding *qevent_binding) |
|---|
| 1483 | +{ |
|---|
| 1484 | + struct mlxsw_sp_mall_entry *mall_entry; |
|---|
| 1485 | + |
|---|
| 1486 | + list_for_each_entry(mall_entry, &qevent_block->mall_entry_list, list) |
|---|
| 1487 | + mlxsw_sp_qevent_entry_deconfigure(qevent_block->mlxsw_sp, mall_entry, |
|---|
| 1488 | + qevent_binding); |
|---|
| 1489 | +} |
|---|
| 1490 | + |
|---|
| 1491 | +static int mlxsw_sp_qevent_block_configure(struct mlxsw_sp_qevent_block *qevent_block) |
|---|
| 1492 | +{ |
|---|
| 1493 | + struct mlxsw_sp_qevent_binding *qevent_binding; |
|---|
| 1494 | + int err; |
|---|
| 1495 | + |
|---|
| 1496 | + list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) { |
|---|
| 1497 | + err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding); |
|---|
| 1498 | + if (err) |
|---|
| 1499 | + goto err_binding_configure; |
|---|
| 1500 | + } |
|---|
| 1501 | + |
|---|
| 1502 | + return 0; |
|---|
| 1503 | + |
|---|
| 1504 | +err_binding_configure: |
|---|
| 1505 | + list_for_each_entry_continue_reverse(qevent_binding, &qevent_block->binding_list, list) |
|---|
| 1506 | + mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); |
|---|
| 1507 | + return err; |
|---|
| 1508 | +} |
|---|
| 1509 | + |
|---|
| 1510 | +static void mlxsw_sp_qevent_block_deconfigure(struct mlxsw_sp_qevent_block *qevent_block) |
|---|
| 1511 | +{ |
|---|
| 1512 | + struct mlxsw_sp_qevent_binding *qevent_binding; |
|---|
| 1513 | + |
|---|
| 1514 | + list_for_each_entry(qevent_binding, &qevent_block->binding_list, list) |
|---|
| 1515 | + mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); |
|---|
| 1516 | +} |
|---|
| 1517 | + |
|---|
| 1518 | +static struct mlxsw_sp_mall_entry * |
|---|
| 1519 | +mlxsw_sp_qevent_mall_entry_find(struct mlxsw_sp_qevent_block *block, unsigned long cookie) |
|---|
| 1520 | +{ |
|---|
| 1521 | + struct mlxsw_sp_mall_entry *mall_entry; |
|---|
| 1522 | + |
|---|
| 1523 | + list_for_each_entry(mall_entry, &block->mall_entry_list, list) |
|---|
| 1524 | + if (mall_entry->cookie == cookie) |
|---|
| 1525 | + return mall_entry; |
|---|
| 1526 | + |
|---|
| 1527 | + return NULL; |
|---|
| 1528 | +} |
|---|
| 1529 | + |
|---|
| 1530 | +static int mlxsw_sp_qevent_mall_replace(struct mlxsw_sp *mlxsw_sp, |
|---|
| 1531 | + struct mlxsw_sp_qevent_block *qevent_block, |
|---|
| 1532 | + struct tc_cls_matchall_offload *f) |
|---|
| 1533 | +{ |
|---|
| 1534 | + struct mlxsw_sp_mall_entry *mall_entry; |
|---|
| 1535 | + struct flow_action_entry *act; |
|---|
| 1536 | + int err; |
|---|
| 1537 | + |
|---|
| 1538 | + /* It should not currently be possible to replace a matchall rule. So |
|---|
| 1539 | + * this must be a new rule. |
|---|
| 1540 | + */ |
|---|
| 1541 | + if (!list_empty(&qevent_block->mall_entry_list)) { |
|---|
| 1542 | + NL_SET_ERR_MSG(f->common.extack, "At most one filter supported"); |
|---|
| 1543 | + return -EOPNOTSUPP; |
|---|
| 1544 | + } |
|---|
| 1545 | + if (f->rule->action.num_entries != 1) { |
|---|
| 1546 | + NL_SET_ERR_MSG(f->common.extack, "Only singular actions supported"); |
|---|
| 1547 | + return -EOPNOTSUPP; |
|---|
| 1548 | + } |
|---|
| 1549 | + if (f->common.chain_index) { |
|---|
| 1550 | + NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported"); |
|---|
| 1551 | + return -EOPNOTSUPP; |
|---|
| 1552 | + } |
|---|
| 1553 | + if (f->common.protocol != htons(ETH_P_ALL)) { |
|---|
| 1554 | + NL_SET_ERR_MSG(f->common.extack, "Protocol matching not supported"); |
|---|
| 1555 | + return -EOPNOTSUPP; |
|---|
| 1556 | + } |
|---|
| 1557 | + |
|---|
| 1558 | + act = &f->rule->action.entries[0]; |
|---|
| 1559 | + if (!(act->hw_stats & FLOW_ACTION_HW_STATS_DISABLED)) { |
|---|
| 1560 | + NL_SET_ERR_MSG(f->common.extack, "HW counters not supported on qevents"); |
|---|
| 1561 | + return -EOPNOTSUPP; |
|---|
| 1562 | + } |
|---|
| 1563 | + |
|---|
| 1564 | + mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL); |
|---|
| 1565 | + if (!mall_entry) |
|---|
| 1566 | + return -ENOMEM; |
|---|
| 1567 | + mall_entry->cookie = f->cookie; |
|---|
| 1568 | + |
|---|
| 1569 | + if (act->id == FLOW_ACTION_MIRRED) { |
|---|
| 1570 | + mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR; |
|---|
| 1571 | + mall_entry->mirror.to_dev = act->dev; |
|---|
| 1572 | + } else if (act->id == FLOW_ACTION_TRAP) { |
|---|
| 1573 | + mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_TRAP; |
|---|
| 1574 | + } else { |
|---|
| 1575 | + NL_SET_ERR_MSG(f->common.extack, "Unsupported action"); |
|---|
| 1576 | + err = -EOPNOTSUPP; |
|---|
| 1577 | + goto err_unsupported_action; |
|---|
| 1578 | + } |
|---|
| 1579 | + |
|---|
| 1580 | + list_add_tail(&mall_entry->list, &qevent_block->mall_entry_list); |
|---|
| 1581 | + |
|---|
| 1582 | + err = mlxsw_sp_qevent_block_configure(qevent_block); |
|---|
| 1583 | + if (err) |
|---|
| 1584 | + goto err_block_configure; |
|---|
| 1585 | + |
|---|
| 1586 | + return 0; |
|---|
| 1587 | + |
|---|
| 1588 | +err_block_configure: |
|---|
| 1589 | + list_del(&mall_entry->list); |
|---|
| 1590 | +err_unsupported_action: |
|---|
| 1591 | + kfree(mall_entry); |
|---|
| 1592 | + return err; |
|---|
| 1593 | +} |
|---|
| 1594 | + |
|---|
| 1595 | +static void mlxsw_sp_qevent_mall_destroy(struct mlxsw_sp_qevent_block *qevent_block, |
|---|
| 1596 | + struct tc_cls_matchall_offload *f) |
|---|
| 1597 | +{ |
|---|
| 1598 | + struct mlxsw_sp_mall_entry *mall_entry; |
|---|
| 1599 | + |
|---|
| 1600 | + mall_entry = mlxsw_sp_qevent_mall_entry_find(qevent_block, f->cookie); |
|---|
| 1601 | + if (!mall_entry) |
|---|
| 1602 | + return; |
|---|
| 1603 | + |
|---|
| 1604 | + mlxsw_sp_qevent_block_deconfigure(qevent_block); |
|---|
| 1605 | + |
|---|
| 1606 | + list_del(&mall_entry->list); |
|---|
| 1607 | + kfree(mall_entry); |
|---|
| 1608 | +} |
|---|
| 1609 | + |
|---|
| 1610 | +static int mlxsw_sp_qevent_block_mall_cb(struct mlxsw_sp_qevent_block *qevent_block, |
|---|
| 1611 | + struct tc_cls_matchall_offload *f) |
|---|
| 1612 | +{ |
|---|
| 1613 | + struct mlxsw_sp *mlxsw_sp = qevent_block->mlxsw_sp; |
|---|
| 1614 | + |
|---|
| 1615 | + switch (f->command) { |
|---|
| 1616 | + case TC_CLSMATCHALL_REPLACE: |
|---|
| 1617 | + return mlxsw_sp_qevent_mall_replace(mlxsw_sp, qevent_block, f); |
|---|
| 1618 | + case TC_CLSMATCHALL_DESTROY: |
|---|
| 1619 | + mlxsw_sp_qevent_mall_destroy(qevent_block, f); |
|---|
| 1620 | + return 0; |
|---|
| 1621 | + default: |
|---|
| 1622 | + return -EOPNOTSUPP; |
|---|
| 1623 | + } |
|---|
| 1624 | +} |
|---|
| 1625 | + |
|---|
| 1626 | +static int mlxsw_sp_qevent_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) |
|---|
| 1627 | +{ |
|---|
| 1628 | + struct mlxsw_sp_qevent_block *qevent_block = cb_priv; |
|---|
| 1629 | + |
|---|
| 1630 | + switch (type) { |
|---|
| 1631 | + case TC_SETUP_CLSMATCHALL: |
|---|
| 1632 | + return mlxsw_sp_qevent_block_mall_cb(qevent_block, type_data); |
|---|
| 1633 | + default: |
|---|
| 1634 | + return -EOPNOTSUPP; |
|---|
| 1635 | + } |
|---|
| 1636 | +} |
|---|
| 1637 | + |
|---|
| 1638 | +static struct mlxsw_sp_qevent_block *mlxsw_sp_qevent_block_create(struct mlxsw_sp *mlxsw_sp, |
|---|
| 1639 | + struct net *net) |
|---|
| 1640 | +{ |
|---|
| 1641 | + struct mlxsw_sp_qevent_block *qevent_block; |
|---|
| 1642 | + |
|---|
| 1643 | + qevent_block = kzalloc(sizeof(*qevent_block), GFP_KERNEL); |
|---|
| 1644 | + if (!qevent_block) |
|---|
| 1645 | + return NULL; |
|---|
| 1646 | + |
|---|
| 1647 | + INIT_LIST_HEAD(&qevent_block->binding_list); |
|---|
| 1648 | + INIT_LIST_HEAD(&qevent_block->mall_entry_list); |
|---|
| 1649 | + qevent_block->mlxsw_sp = mlxsw_sp; |
|---|
| 1650 | + return qevent_block; |
|---|
| 1651 | +} |
|---|
| 1652 | + |
|---|
| 1653 | +static void |
|---|
| 1654 | +mlxsw_sp_qevent_block_destroy(struct mlxsw_sp_qevent_block *qevent_block) |
|---|
| 1655 | +{ |
|---|
| 1656 | + WARN_ON(!list_empty(&qevent_block->binding_list)); |
|---|
| 1657 | + WARN_ON(!list_empty(&qevent_block->mall_entry_list)); |
|---|
| 1658 | + kfree(qevent_block); |
|---|
| 1659 | +} |
|---|
| 1660 | + |
|---|
| 1661 | +static void mlxsw_sp_qevent_block_release(void *cb_priv) |
|---|
| 1662 | +{ |
|---|
| 1663 | + struct mlxsw_sp_qevent_block *qevent_block = cb_priv; |
|---|
| 1664 | + |
|---|
| 1665 | + mlxsw_sp_qevent_block_destroy(qevent_block); |
|---|
| 1666 | +} |
|---|
| 1667 | + |
|---|
| 1668 | +static struct mlxsw_sp_qevent_binding * |
|---|
| 1669 | +mlxsw_sp_qevent_binding_create(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle, int tclass_num, |
|---|
| 1670 | + enum mlxsw_sp_span_trigger span_trigger) |
|---|
| 1671 | +{ |
|---|
| 1672 | + struct mlxsw_sp_qevent_binding *binding; |
|---|
| 1673 | + |
|---|
| 1674 | + binding = kzalloc(sizeof(*binding), GFP_KERNEL); |
|---|
| 1675 | + if (!binding) |
|---|
| 1676 | + return ERR_PTR(-ENOMEM); |
|---|
| 1677 | + |
|---|
| 1678 | + binding->mlxsw_sp_port = mlxsw_sp_port; |
|---|
| 1679 | + binding->handle = handle; |
|---|
| 1680 | + binding->tclass_num = tclass_num; |
|---|
| 1681 | + binding->span_trigger = span_trigger; |
|---|
| 1682 | + return binding; |
|---|
| 1683 | +} |
|---|
| 1684 | + |
|---|
| 1685 | +static void |
|---|
| 1686 | +mlxsw_sp_qevent_binding_destroy(struct mlxsw_sp_qevent_binding *binding) |
|---|
| 1687 | +{ |
|---|
| 1688 | + kfree(binding); |
|---|
| 1689 | +} |
|---|
| 1690 | + |
|---|
| 1691 | +static struct mlxsw_sp_qevent_binding * |
|---|
| 1692 | +mlxsw_sp_qevent_binding_lookup(struct mlxsw_sp_qevent_block *block, |
|---|
| 1693 | + struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 1694 | + u32 handle, |
|---|
| 1695 | + enum mlxsw_sp_span_trigger span_trigger) |
|---|
| 1696 | +{ |
|---|
| 1697 | + struct mlxsw_sp_qevent_binding *qevent_binding; |
|---|
| 1698 | + |
|---|
| 1699 | + list_for_each_entry(qevent_binding, &block->binding_list, list) |
|---|
| 1700 | + if (qevent_binding->mlxsw_sp_port == mlxsw_sp_port && |
|---|
| 1701 | + qevent_binding->handle == handle && |
|---|
| 1702 | + qevent_binding->span_trigger == span_trigger) |
|---|
| 1703 | + return qevent_binding; |
|---|
| 1704 | + return NULL; |
|---|
| 1705 | +} |
|---|
| 1706 | + |
|---|
| 1707 | +static int mlxsw_sp_setup_tc_block_qevent_bind(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 1708 | + struct flow_block_offload *f, |
|---|
| 1709 | + enum mlxsw_sp_span_trigger span_trigger) |
|---|
| 1710 | +{ |
|---|
| 1711 | + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
|---|
| 1712 | + struct mlxsw_sp_qevent_binding *qevent_binding; |
|---|
| 1713 | + struct mlxsw_sp_qevent_block *qevent_block; |
|---|
| 1714 | + struct flow_block_cb *block_cb; |
|---|
| 1715 | + struct mlxsw_sp_qdisc *qdisc; |
|---|
| 1716 | + bool register_block = false; |
|---|
| 1717 | + int err; |
|---|
| 1718 | + |
|---|
| 1719 | + block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp); |
|---|
| 1720 | + if (!block_cb) { |
|---|
| 1721 | + qevent_block = mlxsw_sp_qevent_block_create(mlxsw_sp, f->net); |
|---|
| 1722 | + if (!qevent_block) |
|---|
| 1723 | + return -ENOMEM; |
|---|
| 1724 | + block_cb = flow_block_cb_alloc(mlxsw_sp_qevent_block_cb, mlxsw_sp, qevent_block, |
|---|
| 1725 | + mlxsw_sp_qevent_block_release); |
|---|
| 1726 | + if (IS_ERR(block_cb)) { |
|---|
| 1727 | + mlxsw_sp_qevent_block_destroy(qevent_block); |
|---|
| 1728 | + return PTR_ERR(block_cb); |
|---|
| 1729 | + } |
|---|
| 1730 | + register_block = true; |
|---|
| 1731 | + } else { |
|---|
| 1732 | + qevent_block = flow_block_cb_priv(block_cb); |
|---|
| 1733 | + } |
|---|
| 1734 | + flow_block_cb_incref(block_cb); |
|---|
| 1735 | + |
|---|
| 1736 | + qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port, f->sch->handle); |
|---|
| 1737 | + if (!qdisc) { |
|---|
| 1738 | + NL_SET_ERR_MSG(f->extack, "Qdisc not offloaded"); |
|---|
| 1739 | + err = -ENOENT; |
|---|
| 1740 | + goto err_find_qdisc; |
|---|
| 1741 | + } |
|---|
| 1742 | + |
|---|
| 1743 | + if (WARN_ON(mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle, |
|---|
| 1744 | + span_trigger))) { |
|---|
| 1745 | + err = -EEXIST; |
|---|
| 1746 | + goto err_binding_exists; |
|---|
| 1747 | + } |
|---|
| 1748 | + |
|---|
| 1749 | + qevent_binding = mlxsw_sp_qevent_binding_create(mlxsw_sp_port, f->sch->handle, |
|---|
| 1750 | + qdisc->tclass_num, span_trigger); |
|---|
| 1751 | + if (IS_ERR(qevent_binding)) { |
|---|
| 1752 | + err = PTR_ERR(qevent_binding); |
|---|
| 1753 | + goto err_binding_create; |
|---|
| 1754 | + } |
|---|
| 1755 | + |
|---|
| 1756 | + err = mlxsw_sp_qevent_binding_configure(qevent_block, qevent_binding); |
|---|
| 1757 | + if (err) |
|---|
| 1758 | + goto err_binding_configure; |
|---|
| 1759 | + |
|---|
| 1760 | + list_add(&qevent_binding->list, &qevent_block->binding_list); |
|---|
| 1761 | + |
|---|
| 1762 | + if (register_block) { |
|---|
| 1763 | + flow_block_cb_add(block_cb, f); |
|---|
| 1764 | + list_add_tail(&block_cb->driver_list, &mlxsw_sp_qevent_block_cb_list); |
|---|
| 1765 | + } |
|---|
| 1766 | + |
|---|
| 1767 | + return 0; |
|---|
| 1768 | + |
|---|
| 1769 | +err_binding_configure: |
|---|
| 1770 | + mlxsw_sp_qevent_binding_destroy(qevent_binding); |
|---|
| 1771 | +err_binding_create: |
|---|
| 1772 | +err_binding_exists: |
|---|
| 1773 | +err_find_qdisc: |
|---|
| 1774 | + if (!flow_block_cb_decref(block_cb)) |
|---|
| 1775 | + flow_block_cb_free(block_cb); |
|---|
| 1776 | + return err; |
|---|
| 1777 | +} |
|---|
| 1778 | + |
|---|
| 1779 | +static void mlxsw_sp_setup_tc_block_qevent_unbind(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 1780 | + struct flow_block_offload *f, |
|---|
| 1781 | + enum mlxsw_sp_span_trigger span_trigger) |
|---|
| 1782 | +{ |
|---|
| 1783 | + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
|---|
| 1784 | + struct mlxsw_sp_qevent_binding *qevent_binding; |
|---|
| 1785 | + struct mlxsw_sp_qevent_block *qevent_block; |
|---|
| 1786 | + struct flow_block_cb *block_cb; |
|---|
| 1787 | + |
|---|
| 1788 | + block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_qevent_block_cb, mlxsw_sp); |
|---|
| 1789 | + if (!block_cb) |
|---|
| 1790 | + return; |
|---|
| 1791 | + qevent_block = flow_block_cb_priv(block_cb); |
|---|
| 1792 | + |
|---|
| 1793 | + qevent_binding = mlxsw_sp_qevent_binding_lookup(qevent_block, mlxsw_sp_port, f->sch->handle, |
|---|
| 1794 | + span_trigger); |
|---|
| 1795 | + if (!qevent_binding) |
|---|
| 1796 | + return; |
|---|
| 1797 | + |
|---|
| 1798 | + list_del(&qevent_binding->list); |
|---|
| 1799 | + mlxsw_sp_qevent_binding_deconfigure(qevent_block, qevent_binding); |
|---|
| 1800 | + mlxsw_sp_qevent_binding_destroy(qevent_binding); |
|---|
| 1801 | + |
|---|
| 1802 | + if (!flow_block_cb_decref(block_cb)) { |
|---|
| 1803 | + flow_block_cb_remove(block_cb, f); |
|---|
| 1804 | + list_del(&block_cb->driver_list); |
|---|
| 1805 | + } |
|---|
| 1806 | +} |
|---|
| 1807 | + |
|---|
| 1808 | +static int mlxsw_sp_setup_tc_block_qevent(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 1809 | + struct flow_block_offload *f, |
|---|
| 1810 | + enum mlxsw_sp_span_trigger span_trigger) |
|---|
| 1811 | +{ |
|---|
| 1812 | + f->driver_block_list = &mlxsw_sp_qevent_block_cb_list; |
|---|
| 1813 | + |
|---|
| 1814 | + switch (f->command) { |
|---|
| 1815 | + case FLOW_BLOCK_BIND: |
|---|
| 1816 | + return mlxsw_sp_setup_tc_block_qevent_bind(mlxsw_sp_port, f, span_trigger); |
|---|
| 1817 | + case FLOW_BLOCK_UNBIND: |
|---|
| 1818 | + mlxsw_sp_setup_tc_block_qevent_unbind(mlxsw_sp_port, f, span_trigger); |
|---|
| 1819 | + return 0; |
|---|
| 1820 | + default: |
|---|
| 1821 | + return -EOPNOTSUPP; |
|---|
| 1822 | + } |
|---|
| 1823 | +} |
|---|
| 1824 | + |
|---|
| 1825 | +int mlxsw_sp_setup_tc_block_qevent_early_drop(struct mlxsw_sp_port *mlxsw_sp_port, |
|---|
| 1826 | + struct flow_block_offload *f) |
|---|
| 1827 | +{ |
|---|
| 1828 | + return mlxsw_sp_setup_tc_block_qevent(mlxsw_sp_port, f, MLXSW_SP_SPAN_TRIGGER_EARLY_DROP); |
|---|
| 1829 | +} |
|---|
| 1830 | + |
|---|
| 1831 | +int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port) |
|---|
| 1832 | +{ |
|---|
| 1833 | + struct mlxsw_sp_qdisc_state *qdisc_state; |
|---|
| 1834 | + int i; |
|---|
| 1835 | + |
|---|
| 1836 | + qdisc_state = kzalloc(sizeof(*qdisc_state), GFP_KERNEL); |
|---|
| 1837 | + if (!qdisc_state) |
|---|
| 1838 | + return -ENOMEM; |
|---|
| 1839 | + |
|---|
| 1840 | + qdisc_state->root_qdisc.prio_bitmap = 0xff; |
|---|
| 1841 | + qdisc_state->root_qdisc.tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS; |
|---|
| 1842 | + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) |
|---|
| 1843 | + qdisc_state->tclass_qdiscs[i].tclass_num = i; |
|---|
| 1844 | + |
|---|
| 1845 | + mlxsw_sp_port->qdisc = qdisc_state; |
|---|
| 1846 | + return 0; |
|---|
| 751 | 1847 | } |
|---|
| 752 | 1848 | |
|---|
| 753 | 1849 | void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port) |
|---|
| 754 | 1850 | { |
|---|
| 755 | | - kfree(mlxsw_sp_port->tclass_qdiscs); |
|---|
| 756 | | - kfree(mlxsw_sp_port->root_qdisc); |
|---|
| 1851 | + kfree(mlxsw_sp_port->qdisc); |
|---|
| 757 | 1852 | } |
|---|