linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
   2/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
   3
   4#include <linux/kernel.h>
   5#include <linux/errno.h>
   6#include <linux/netdevice.h>
   7#include <net/pkt_cls.h>
   8#include <net/red.h>
   9
  10#include "spectrum.h"
  11#include "reg.h"
  12
  13#define MLXSW_SP_PRIO_BAND_TO_TCLASS(band) (IEEE_8021QAZ_MAX_TCS - band - 1)
  14#define MLXSW_SP_PRIO_CHILD_TO_TCLASS(child) \
  15        MLXSW_SP_PRIO_BAND_TO_TCLASS((child - 1))
  16
  17enum mlxsw_sp_qdisc_type {
  18        MLXSW_SP_QDISC_NO_QDISC,
  19        MLXSW_SP_QDISC_RED,
  20        MLXSW_SP_QDISC_PRIO,
  21};
  22
  23struct mlxsw_sp_qdisc_ops {
  24        enum mlxsw_sp_qdisc_type type;
  25        int (*check_params)(struct mlxsw_sp_port *mlxsw_sp_port,
  26                            struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
  27                            void *params);
  28        int (*replace)(struct mlxsw_sp_port *mlxsw_sp_port,
  29                       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
  30        int (*destroy)(struct mlxsw_sp_port *mlxsw_sp_port,
  31                       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
  32        int (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
  33                         struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
  34                         struct tc_qopt_offload_stats *stats_ptr);
  35        int (*get_xstats)(struct mlxsw_sp_port *mlxsw_sp_port,
  36                          struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
  37                          void *xstats_ptr);
  38        void (*clean_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
  39                            struct mlxsw_sp_qdisc *mlxsw_sp_qdisc);
  40        /* unoffload - to be used for a qdisc that stops being offloaded without
  41         * being destroyed.
  42         */
  43        void (*unoffload)(struct mlxsw_sp_port *mlxsw_sp_port,
  44                          struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, void *params);
  45};
  46
  47struct mlxsw_sp_qdisc {
  48        u32 handle;
  49        u8 tclass_num;
  50        u8 prio_bitmap;
  51        union {
  52                struct red_stats red;
  53        } xstats_base;
  54        struct mlxsw_sp_qdisc_stats {
  55                u64 tx_bytes;
  56                u64 tx_packets;
  57                u64 drops;
  58                u64 overlimits;
  59                u64 backlog;
  60        } stats_base;
  61
  62        struct mlxsw_sp_qdisc_ops *ops;
  63};
  64
  65static bool
  66mlxsw_sp_qdisc_compare(struct mlxsw_sp_qdisc *mlxsw_sp_qdisc, u32 handle,
  67                       enum mlxsw_sp_qdisc_type type)
  68{
  69        return mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
  70               mlxsw_sp_qdisc->ops->type == type &&
  71               mlxsw_sp_qdisc->handle == handle;
  72}
  73
  74static struct mlxsw_sp_qdisc *
  75mlxsw_sp_qdisc_find(struct mlxsw_sp_port *mlxsw_sp_port, u32 parent,
  76                    bool root_only)
  77{
  78        int tclass, child_index;
  79
  80        if (parent == TC_H_ROOT)
  81                return mlxsw_sp_port->root_qdisc;
  82
  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 ||
  86            TC_H_MIN(parent) > IEEE_8021QAZ_MAX_TCS)
  87                return NULL;
  88
  89        child_index = TC_H_MIN(parent);
  90        tclass = MLXSW_SP_PRIO_CHILD_TO_TCLASS(child_index);
  91        return &mlxsw_sp_port->tclass_qdiscs[tclass];
  92}
  93
  94static struct mlxsw_sp_qdisc *
  95mlxsw_sp_qdisc_find_by_handle(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle)
  96{
  97        int i;
  98
  99        if (mlxsw_sp_port->root_qdisc->handle == handle)
 100                return mlxsw_sp_port->root_qdisc;
 101
 102        if (mlxsw_sp_port->root_qdisc->handle == TC_H_UNSPEC)
 103                return NULL;
 104
 105        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];
 108
 109        return NULL;
 110}
 111
 112static int
 113mlxsw_sp_qdisc_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
 114                       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
 115{
 116        int err = 0;
 117
 118        if (!mlxsw_sp_qdisc)
 119                return 0;
 120
 121        if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->destroy)
 122                err = mlxsw_sp_qdisc->ops->destroy(mlxsw_sp_port,
 123                                                   mlxsw_sp_qdisc);
 124
 125        mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
 126        mlxsw_sp_qdisc->ops = NULL;
 127        return err;
 128}
 129
 130static int
 131mlxsw_sp_qdisc_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
 132                       struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
 133                       struct mlxsw_sp_qdisc_ops *ops, void *params)
 134{
 135        int err;
 136
 137        if (mlxsw_sp_qdisc->ops && mlxsw_sp_qdisc->ops->type != ops->type)
 138                /* In case this location contained a different qdisc of the
 139                 * same type we can override the old qdisc configuration.
 140                 * Otherwise, we need to remove the old qdisc before setting the
 141                 * new one.
 142                 */
 143                mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
 144        err = ops->check_params(mlxsw_sp_port, mlxsw_sp_qdisc, params);
 145        if (err)
 146                goto err_bad_param;
 147
 148        err = ops->replace(mlxsw_sp_port, mlxsw_sp_qdisc, params);
 149        if (err)
 150                goto err_config;
 151
 152        if (mlxsw_sp_qdisc->handle != handle) {
 153                mlxsw_sp_qdisc->ops = ops;
 154                if (ops->clean_stats)
 155                        ops->clean_stats(mlxsw_sp_port, mlxsw_sp_qdisc);
 156        }
 157
 158        mlxsw_sp_qdisc->handle = handle;
 159        return 0;
 160
 161err_bad_param:
 162err_config:
 163        if (mlxsw_sp_qdisc->handle == handle && ops->unoffload)
 164                ops->unoffload(mlxsw_sp_port, mlxsw_sp_qdisc, params);
 165
 166        mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
 167        return err;
 168}
 169
 170static int
 171mlxsw_sp_qdisc_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
 172                         struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
 173                         struct tc_qopt_offload_stats *stats_ptr)
 174{
 175        if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
 176            mlxsw_sp_qdisc->ops->get_stats)
 177                return mlxsw_sp_qdisc->ops->get_stats(mlxsw_sp_port,
 178                                                      mlxsw_sp_qdisc,
 179                                                      stats_ptr);
 180
 181        return -EOPNOTSUPP;
 182}
 183
 184static int
 185mlxsw_sp_qdisc_get_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
 186                          struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
 187                          void *xstats_ptr)
 188{
 189        if (mlxsw_sp_qdisc && mlxsw_sp_qdisc->ops &&
 190            mlxsw_sp_qdisc->ops->get_xstats)
 191                return mlxsw_sp_qdisc->ops->get_xstats(mlxsw_sp_port,
 192                                                      mlxsw_sp_qdisc,
 193                                                      xstats_ptr);
 194
 195        return -EOPNOTSUPP;
 196}
 197
 198static void
 199mlxsw_sp_qdisc_bstats_per_priority_get(struct mlxsw_sp_port_xstats *xstats,
 200                                       u8 prio_bitmap, u64 *tx_packets,
 201                                       u64 *tx_bytes)
 202{
 203        int i;
 204
 205        *tx_packets = 0;
 206        *tx_bytes = 0;
 207        for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
 208                if (prio_bitmap & BIT(i)) {
 209                        *tx_packets += xstats->tx_packets[i];
 210                        *tx_bytes += xstats->tx_bytes[i];
 211                }
 212        }
 213}
 214
 215static int
 216mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port,
 217                                  int tclass_num, u32 min, u32 max,
 218                                  u32 probability, bool is_ecn)
 219{
 220        char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
 221        char cwtp_cmd[MLXSW_REG_CWTP_LEN];
 222        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 223        int err;
 224
 225        mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num);
 226        mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE,
 227                                    roundup(min, MLXSW_REG_CWTP_MIN_VALUE),
 228                                    roundup(max, MLXSW_REG_CWTP_MIN_VALUE),
 229                                    probability);
 230
 231        err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd);
 232        if (err)
 233                return err;
 234
 235        mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
 236                             MLXSW_REG_CWTP_DEFAULT_PROFILE, true, is_ecn);
 237
 238        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
 239}
 240
 241static int
 242mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port,
 243                                   int tclass_num)
 244{
 245        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 246        char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
 247
 248        mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
 249                             MLXSW_REG_CWTPM_RESET_PROFILE, false, false);
 250        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
 251}
 252
 253static void
 254mlxsw_sp_setup_tc_qdisc_red_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
 255                                        struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
 256{
 257        u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
 258        struct mlxsw_sp_qdisc_stats *stats_base;
 259        struct mlxsw_sp_port_xstats *xstats;
 260        struct red_stats *red_base;
 261
 262        xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
 263        stats_base = &mlxsw_sp_qdisc->stats_base;
 264        red_base = &mlxsw_sp_qdisc->xstats_base.red;
 265
 266        mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
 267                                               mlxsw_sp_qdisc->prio_bitmap,
 268                                               &stats_base->tx_packets,
 269                                               &stats_base->tx_bytes);
 270        red_base->prob_mark = xstats->ecn;
 271        red_base->prob_drop = xstats->wred_drop[tclass_num];
 272        red_base->pdrop = xstats->tail_drop[tclass_num];
 273
 274        stats_base->overlimits = red_base->prob_drop + red_base->prob_mark;
 275        stats_base->drops = red_base->prob_drop + red_base->pdrop;
 276
 277        stats_base->backlog = 0;
 278}
 279
 280static int
 281mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
 282                           struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
 283{
 284        struct mlxsw_sp_qdisc *root_qdisc = mlxsw_sp_port->root_qdisc;
 285
 286        if (root_qdisc != mlxsw_sp_qdisc)
 287                root_qdisc->stats_base.backlog -=
 288                                        mlxsw_sp_qdisc->stats_base.backlog;
 289
 290        return mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port,
 291                                                  mlxsw_sp_qdisc->tclass_num);
 292}
 293
 294static int
 295mlxsw_sp_qdisc_red_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
 296                                struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
 297                                void *params)
 298{
 299        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 300        struct tc_red_qopt_offload_params *p = params;
 301
 302        if (p->min > p->max) {
 303                dev_err(mlxsw_sp->bus_info->dev,
 304                        "spectrum: RED: min %u is bigger then max %u\n", p->min,
 305                        p->max);
 306                return -EINVAL;
 307        }
 308        if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_BUFFER_SIZE)) {
 309                dev_err(mlxsw_sp->bus_info->dev,
 310                        "spectrum: RED: max value %u is too big\n", p->max);
 311                return -EINVAL;
 312        }
 313        if (p->min == 0 || p->max == 0) {
 314                dev_err(mlxsw_sp->bus_info->dev,
 315                        "spectrum: RED: 0 value is illegal for min and max\n");
 316                return -EINVAL;
 317        }
 318        return 0;
 319}
 320
 321static int
 322mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port,
 323                           struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
 324                           void *params)
 325{
 326        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 327        struct tc_red_qopt_offload_params *p = params;
 328        u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
 329        u32 min, max;
 330        u64 prob;
 331
 332        /* calculate probability in percentage */
 333        prob = p->probability;
 334        prob *= 100;
 335        prob = DIV_ROUND_UP(prob, 1 << 16);
 336        prob = DIV_ROUND_UP(prob, 1 << 16);
 337        min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min);
 338        max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max);
 339        return mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num, min,
 340                                                 max, prob, p->is_ecn);
 341}
 342
 343static void
 344mlxsw_sp_qdisc_red_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
 345                             struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
 346                             void *params)
 347{
 348        struct tc_red_qopt_offload_params *p = params;
 349        u64 backlog;
 350
 351        backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
 352                                       mlxsw_sp_qdisc->stats_base.backlog);
 353        p->qstats->backlog -= backlog;
 354        mlxsw_sp_qdisc->stats_base.backlog = 0;
 355}
 356
 357static int
 358mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port,
 359                              struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
 360                              void *xstats_ptr)
 361{
 362        struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base.red;
 363        u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
 364        struct mlxsw_sp_port_xstats *xstats;
 365        struct red_stats *res = xstats_ptr;
 366        int early_drops, marks, pdrops;
 367
 368        xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
 369
 370        early_drops = xstats->wred_drop[tclass_num] - xstats_base->prob_drop;
 371        marks = xstats->ecn - xstats_base->prob_mark;
 372        pdrops = xstats->tail_drop[tclass_num] - xstats_base->pdrop;
 373
 374        res->pdrop += pdrops;
 375        res->prob_drop += early_drops;
 376        res->prob_mark += marks;
 377
 378        xstats_base->pdrop += pdrops;
 379        xstats_base->prob_drop += early_drops;
 380        xstats_base->prob_mark += marks;
 381        return 0;
 382}
 383
 384static int
 385mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port,
 386                             struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
 387                             struct tc_qopt_offload_stats *stats_ptr)
 388{
 389        u64 tx_bytes, tx_packets, overlimits, drops, backlog;
 390        u8 tclass_num = mlxsw_sp_qdisc->tclass_num;
 391        struct mlxsw_sp_qdisc_stats *stats_base;
 392        struct mlxsw_sp_port_xstats *xstats;
 393
 394        xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
 395        stats_base = &mlxsw_sp_qdisc->stats_base;
 396
 397        mlxsw_sp_qdisc_bstats_per_priority_get(xstats,
 398                                               mlxsw_sp_qdisc->prio_bitmap,
 399                                               &tx_packets, &tx_bytes);
 400        tx_bytes = tx_bytes - stats_base->tx_bytes;
 401        tx_packets = tx_packets - stats_base->tx_packets;
 402
 403        overlimits = xstats->wred_drop[tclass_num] + xstats->ecn -
 404                     stats_base->overlimits;
 405        drops = xstats->wred_drop[tclass_num] + xstats->tail_drop[tclass_num] -
 406                stats_base->drops;
 407        backlog = xstats->backlog[tclass_num];
 408
 409        _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);
 410        stats_ptr->qstats->overlimits += overlimits;
 411        stats_ptr->qstats->drops += drops;
 412        stats_ptr->qstats->backlog +=
 413                                mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
 414                                                     backlog) -
 415                                mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
 416                                                     stats_base->backlog);
 417
 418        stats_base->backlog = backlog;
 419        stats_base->drops +=  drops;
 420        stats_base->overlimits += overlimits;
 421        stats_base->tx_bytes += tx_bytes;
 422        stats_base->tx_packets += tx_packets;
 423        return 0;
 424}
 425
 426#define MLXSW_SP_PORT_DEFAULT_TCLASS 0
 427
 428static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_red = {
 429        .type = MLXSW_SP_QDISC_RED,
 430        .check_params = mlxsw_sp_qdisc_red_check_params,
 431        .replace = mlxsw_sp_qdisc_red_replace,
 432        .unoffload = mlxsw_sp_qdisc_red_unoffload,
 433        .destroy = mlxsw_sp_qdisc_red_destroy,
 434        .get_stats = mlxsw_sp_qdisc_get_red_stats,
 435        .get_xstats = mlxsw_sp_qdisc_get_red_xstats,
 436        .clean_stats = mlxsw_sp_setup_tc_qdisc_red_clean_stats,
 437};
 438
 439int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
 440                          struct tc_red_qopt_offload *p)
 441{
 442        struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
 443
 444        mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, false);
 445        if (!mlxsw_sp_qdisc)
 446                return -EOPNOTSUPP;
 447
 448        if (p->command == TC_RED_REPLACE)
 449                return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
 450                                              mlxsw_sp_qdisc,
 451                                              &mlxsw_sp_qdisc_ops_red,
 452                                              &p->set);
 453
 454        if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
 455                                    MLXSW_SP_QDISC_RED))
 456                return -EOPNOTSUPP;
 457
 458        switch (p->command) {
 459        case TC_RED_DESTROY:
 460                return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
 461        case TC_RED_XSTATS:
 462                return mlxsw_sp_qdisc_get_xstats(mlxsw_sp_port, mlxsw_sp_qdisc,
 463                                                 p->xstats);
 464        case TC_RED_STATS:
 465                return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
 466                                                &p->stats);
 467        default:
 468                return -EOPNOTSUPP;
 469        }
 470}
 471
 472static int
 473mlxsw_sp_qdisc_prio_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
 474                            struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
 475{
 476        int i;
 477
 478        for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
 479                mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i,
 480                                          MLXSW_SP_PORT_DEFAULT_TCLASS);
 481                mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
 482                                       &mlxsw_sp_port->tclass_qdiscs[i]);
 483                mlxsw_sp_port->tclass_qdiscs[i].prio_bitmap = 0;
 484        }
 485
 486        return 0;
 487}
 488
 489static int
 490mlxsw_sp_qdisc_prio_check_params(struct mlxsw_sp_port *mlxsw_sp_port,
 491                                 struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
 492                                 void *params)
 493{
 494        struct tc_prio_qopt_offload_params *p = params;
 495
 496        if (p->bands > IEEE_8021QAZ_MAX_TCS)
 497                return -EOPNOTSUPP;
 498
 499        return 0;
 500}
 501
 502static int
 503mlxsw_sp_qdisc_prio_replace(struct mlxsw_sp_port *mlxsw_sp_port,
 504                            struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
 505                            void *params)
 506{
 507        struct tc_prio_qopt_offload_params *p = params;
 508        struct mlxsw_sp_qdisc *child_qdisc;
 509        int tclass, i, band, backlog;
 510        u8 old_priomap;
 511        int err;
 512
 513        for (band = 0; band < p->bands; band++) {
 514                tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
 515                child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass];
 516                old_priomap = child_qdisc->prio_bitmap;
 517                child_qdisc->prio_bitmap = 0;
 518                for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
 519                        if (p->priomap[i] == band) {
 520                                child_qdisc->prio_bitmap |= BIT(i);
 521                                if (BIT(i) & old_priomap)
 522                                        continue;
 523                                err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port,
 524                                                                i, tclass);
 525                                if (err)
 526                                        return err;
 527                        }
 528                }
 529                if (old_priomap != child_qdisc->prio_bitmap &&
 530                    child_qdisc->ops && child_qdisc->ops->clean_stats) {
 531                        backlog = child_qdisc->stats_base.backlog;
 532                        child_qdisc->ops->clean_stats(mlxsw_sp_port,
 533                                                      child_qdisc);
 534                        child_qdisc->stats_base.backlog = backlog;
 535                }
 536        }
 537        for (; band < IEEE_8021QAZ_MAX_TCS; band++) {
 538                tclass = MLXSW_SP_PRIO_BAND_TO_TCLASS(band);
 539                child_qdisc = &mlxsw_sp_port->tclass_qdiscs[tclass];
 540                child_qdisc->prio_bitmap = 0;
 541                mlxsw_sp_qdisc_destroy(mlxsw_sp_port, child_qdisc);
 542        }
 543        return 0;
 544}
 545
 546static void
 547mlxsw_sp_qdisc_prio_unoffload(struct mlxsw_sp_port *mlxsw_sp_port,
 548                              struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
 549                              void *params)
 550{
 551        struct tc_prio_qopt_offload_params *p = params;
 552        u64 backlog;
 553
 554        backlog = mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
 555                                       mlxsw_sp_qdisc->stats_base.backlog);
 556        p->qstats->backlog -= backlog;
 557}
 558
 559static int
 560mlxsw_sp_qdisc_get_prio_stats(struct mlxsw_sp_port *mlxsw_sp_port,
 561                              struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
 562                              struct tc_qopt_offload_stats *stats_ptr)
 563{
 564        u64 tx_bytes, tx_packets, drops = 0, backlog = 0;
 565        struct mlxsw_sp_qdisc_stats *stats_base;
 566        struct mlxsw_sp_port_xstats *xstats;
 567        struct rtnl_link_stats64 *stats;
 568        int i;
 569
 570        xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
 571        stats = &mlxsw_sp_port->periodic_hw_stats.stats;
 572        stats_base = &mlxsw_sp_qdisc->stats_base;
 573
 574        tx_bytes = stats->tx_bytes - stats_base->tx_bytes;
 575        tx_packets = stats->tx_packets - stats_base->tx_packets;
 576
 577        for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
 578                drops += xstats->tail_drop[i];
 579                drops += xstats->wred_drop[i];
 580                backlog += xstats->backlog[i];
 581        }
 582        drops = drops - stats_base->drops;
 583
 584        _bstats_update(stats_ptr->bstats, tx_bytes, tx_packets);
 585        stats_ptr->qstats->drops += drops;
 586        stats_ptr->qstats->backlog +=
 587                                mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
 588                                                     backlog) -
 589                                mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
 590                                                     stats_base->backlog);
 591        stats_base->backlog = backlog;
 592        stats_base->drops += drops;
 593        stats_base->tx_bytes += tx_bytes;
 594        stats_base->tx_packets += tx_packets;
 595        return 0;
 596}
 597
 598static void
 599mlxsw_sp_setup_tc_qdisc_prio_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
 600                                         struct mlxsw_sp_qdisc *mlxsw_sp_qdisc)
 601{
 602        struct mlxsw_sp_qdisc_stats *stats_base;
 603        struct mlxsw_sp_port_xstats *xstats;
 604        struct rtnl_link_stats64 *stats;
 605        int i;
 606
 607        xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
 608        stats = &mlxsw_sp_port->periodic_hw_stats.stats;
 609        stats_base = &mlxsw_sp_qdisc->stats_base;
 610
 611        stats_base->tx_packets = stats->tx_packets;
 612        stats_base->tx_bytes = stats->tx_bytes;
 613
 614        stats_base->drops = 0;
 615        for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
 616                stats_base->drops += xstats->tail_drop[i];
 617                stats_base->drops += xstats->wred_drop[i];
 618        }
 619
 620        mlxsw_sp_qdisc->stats_base.backlog = 0;
 621}
 622
 623static struct mlxsw_sp_qdisc_ops mlxsw_sp_qdisc_ops_prio = {
 624        .type = MLXSW_SP_QDISC_PRIO,
 625        .check_params = mlxsw_sp_qdisc_prio_check_params,
 626        .replace = mlxsw_sp_qdisc_prio_replace,
 627        .unoffload = mlxsw_sp_qdisc_prio_unoffload,
 628        .destroy = mlxsw_sp_qdisc_prio_destroy,
 629        .get_stats = mlxsw_sp_qdisc_get_prio_stats,
 630        .clean_stats = mlxsw_sp_setup_tc_qdisc_prio_clean_stats,
 631};
 632
 633/* Grafting is not supported in mlxsw. It will result in un-offloading of the
 634 * grafted qdisc as well as the qdisc in the qdisc new location.
 635 * (However, if the graft is to the location where the qdisc is already at, it
 636 * will be ignored completely and won't cause un-offloading).
 637 */
 638static int
 639mlxsw_sp_qdisc_prio_graft(struct mlxsw_sp_port *mlxsw_sp_port,
 640                          struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
 641                          struct tc_prio_qopt_offload_graft_params *p)
 642{
 643        int tclass_num = MLXSW_SP_PRIO_BAND_TO_TCLASS(p->band);
 644        struct mlxsw_sp_qdisc *old_qdisc;
 645
 646        /* Check if the grafted qdisc is already in its "new" location. If so -
 647         * nothing needs to be done.
 648         */
 649        if (p->band < IEEE_8021QAZ_MAX_TCS &&
 650            mlxsw_sp_port->tclass_qdiscs[tclass_num].handle == p->child_handle)
 651                return 0;
 652
 653        /* See if the grafted qdisc is already offloaded on any tclass. If so,
 654         * unoffload it.
 655         */
 656        old_qdisc = mlxsw_sp_qdisc_find_by_handle(mlxsw_sp_port,
 657                                                  p->child_handle);
 658        if (old_qdisc)
 659                mlxsw_sp_qdisc_destroy(mlxsw_sp_port, old_qdisc);
 660
 661        mlxsw_sp_qdisc_destroy(mlxsw_sp_port,
 662                               &mlxsw_sp_port->tclass_qdiscs[tclass_num]);
 663        return -EOPNOTSUPP;
 664}
 665
 666int mlxsw_sp_setup_tc_prio(struct mlxsw_sp_port *mlxsw_sp_port,
 667                           struct tc_prio_qopt_offload *p)
 668{
 669        struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
 670
 671        mlxsw_sp_qdisc = mlxsw_sp_qdisc_find(mlxsw_sp_port, p->parent, true);
 672        if (!mlxsw_sp_qdisc)
 673                return -EOPNOTSUPP;
 674
 675        if (p->command == TC_PRIO_REPLACE)
 676                return mlxsw_sp_qdisc_replace(mlxsw_sp_port, p->handle,
 677                                              mlxsw_sp_qdisc,
 678                                              &mlxsw_sp_qdisc_ops_prio,
 679                                              &p->replace_params);
 680
 681        if (!mlxsw_sp_qdisc_compare(mlxsw_sp_qdisc, p->handle,
 682                                    MLXSW_SP_QDISC_PRIO))
 683                return -EOPNOTSUPP;
 684
 685        switch (p->command) {
 686        case TC_PRIO_DESTROY:
 687                return mlxsw_sp_qdisc_destroy(mlxsw_sp_port, mlxsw_sp_qdisc);
 688        case TC_PRIO_STATS:
 689                return mlxsw_sp_qdisc_get_stats(mlxsw_sp_port, mlxsw_sp_qdisc,
 690                                                &p->stats);
 691        case TC_PRIO_GRAFT:
 692                return mlxsw_sp_qdisc_prio_graft(mlxsw_sp_port, mlxsw_sp_qdisc,
 693                                                 &p->graft_params);
 694        default:
 695                return -EOPNOTSUPP;
 696        }
 697}
 698
 699int mlxsw_sp_tc_qdisc_init(struct mlxsw_sp_port *mlxsw_sp_port)
 700{
 701        struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
 702        int i;
 703
 704        mlxsw_sp_qdisc = kzalloc(sizeof(*mlxsw_sp_qdisc), GFP_KERNEL);
 705        if (!mlxsw_sp_qdisc)
 706                goto err_root_qdisc_init;
 707
 708        mlxsw_sp_port->root_qdisc = mlxsw_sp_qdisc;
 709        mlxsw_sp_port->root_qdisc->prio_bitmap = 0xff;
 710        mlxsw_sp_port->root_qdisc->tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS;
 711
 712        mlxsw_sp_qdisc = kcalloc(IEEE_8021QAZ_MAX_TCS,
 713                                 sizeof(*mlxsw_sp_qdisc),
 714                                 GFP_KERNEL);
 715        if (!mlxsw_sp_qdisc)
 716                goto err_tclass_qdiscs_init;
 717
 718        mlxsw_sp_port->tclass_qdiscs = mlxsw_sp_qdisc;
 719        for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
 720                mlxsw_sp_port->tclass_qdiscs[i].tclass_num = i;
 721
 722        return 0;
 723
 724err_tclass_qdiscs_init:
 725        kfree(mlxsw_sp_port->root_qdisc);
 726err_root_qdisc_init:
 727        return -ENOMEM;
 728}
 729
 730void mlxsw_sp_tc_qdisc_fini(struct mlxsw_sp_port *mlxsw_sp_port)
 731{
 732        kfree(mlxsw_sp_port->tclass_qdiscs);
 733        kfree(mlxsw_sp_port->root_qdisc);
 734}
 735