linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c
<<
>>
Prefs
   1/*
   2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_qdisc.c
   3 * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
   4 * Copyright (c) 2017 Nogah Frankel <nogahf@mellanox.com>
   5 *
   6 * Redistribution and use in source and binary forms, with or without
   7 * modification, are permitted provided that the following conditions are met:
   8 *
   9 * 1. Redistributions of source code must retain the above copyright
  10 *    notice, this list of conditions and the following disclaimer.
  11 * 2. Redistributions in binary form must reproduce the above copyright
  12 *    notice, this list of conditions and the following disclaimer in the
  13 *    documentation and/or other materials provided with the distribution.
  14 * 3. Neither the names of the copyright holders nor the names of its
  15 *    contributors may be used to endorse or promote products derived from
  16 *    this software without specific prior written permission.
  17 *
  18 * Alternatively, this software may be distributed under the terms of the
  19 * GNU General Public License ("GPL") version 2 as published by the Free
  20 * Software Foundation.
  21 *
  22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32 * POSSIBILITY OF SUCH DAMAGE.
  33 */
  34
  35#include <linux/kernel.h>
  36#include <linux/errno.h>
  37#include <linux/netdevice.h>
  38#include <net/pkt_cls.h>
  39#include <net/red.h>
  40
  41#include "spectrum.h"
  42#include "reg.h"
  43
  44static int
  45mlxsw_sp_tclass_congestion_enable(struct mlxsw_sp_port *mlxsw_sp_port,
  46                                  int tclass_num, u32 min, u32 max,
  47                                  u32 probability, bool is_ecn)
  48{
  49        char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
  50        char cwtp_cmd[MLXSW_REG_CWTP_LEN];
  51        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
  52        int err;
  53
  54        mlxsw_reg_cwtp_pack(cwtp_cmd, mlxsw_sp_port->local_port, tclass_num);
  55        mlxsw_reg_cwtp_profile_pack(cwtp_cmd, MLXSW_REG_CWTP_DEFAULT_PROFILE,
  56                                    roundup(min, MLXSW_REG_CWTP_MIN_VALUE),
  57                                    roundup(max, MLXSW_REG_CWTP_MIN_VALUE),
  58                                    probability);
  59
  60        err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtp), cwtp_cmd);
  61        if (err)
  62                return err;
  63
  64        mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
  65                             MLXSW_REG_CWTP_DEFAULT_PROFILE, true, is_ecn);
  66
  67        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
  68}
  69
  70static int
  71mlxsw_sp_tclass_congestion_disable(struct mlxsw_sp_port *mlxsw_sp_port,
  72                                   int tclass_num)
  73{
  74        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
  75        char cwtpm_cmd[MLXSW_REG_CWTPM_LEN];
  76
  77        mlxsw_reg_cwtpm_pack(cwtpm_cmd, mlxsw_sp_port->local_port, tclass_num,
  78                             MLXSW_REG_CWTPM_RESET_PROFILE, false, false);
  79        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(cwtpm), cwtpm_cmd);
  80}
  81
  82static void
  83mlxsw_sp_setup_tc_qdisc_clean_stats(struct mlxsw_sp_port *mlxsw_sp_port,
  84                                    struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
  85                                    int tclass_num)
  86{
  87        struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base;
  88        struct mlxsw_sp_port_xstats *xstats;
  89        struct rtnl_link_stats64 *stats;
  90
  91        xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
  92        stats = &mlxsw_sp_port->periodic_hw_stats.stats;
  93
  94        mlxsw_sp_qdisc->tx_packets = stats->tx_packets;
  95        mlxsw_sp_qdisc->tx_bytes = stats->tx_bytes;
  96
  97        switch (mlxsw_sp_qdisc->type) {
  98        case MLXSW_SP_QDISC_RED:
  99                xstats_base->prob_mark = xstats->ecn;
 100                xstats_base->prob_drop = xstats->wred_drop[tclass_num];
 101                xstats_base->pdrop = xstats->tail_drop[tclass_num];
 102
 103                mlxsw_sp_qdisc->overlimits = xstats_base->prob_drop +
 104                                             xstats_base->prob_mark;
 105                mlxsw_sp_qdisc->drops = xstats_base->prob_drop +
 106                                        xstats_base->pdrop;
 107                break;
 108        default:
 109                break;
 110        }
 111}
 112
 113static int
 114mlxsw_sp_qdisc_red_destroy(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
 115                           struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
 116                           int tclass_num)
 117{
 118        int err;
 119
 120        if (mlxsw_sp_qdisc->handle != handle)
 121                return 0;
 122
 123        err = mlxsw_sp_tclass_congestion_disable(mlxsw_sp_port, tclass_num);
 124        mlxsw_sp_qdisc->handle = TC_H_UNSPEC;
 125        mlxsw_sp_qdisc->type = MLXSW_SP_QDISC_NO_QDISC;
 126
 127        return err;
 128}
 129
 130static int
 131mlxsw_sp_qdisc_red_replace(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
 132                           struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
 133                           int tclass_num,
 134                           struct tc_red_qopt_offload_params *p)
 135{
 136        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 137        u32 min, max;
 138        u64 prob;
 139        int err = 0;
 140
 141        if (p->min > p->max) {
 142                dev_err(mlxsw_sp->bus_info->dev,
 143                        "spectrum: RED: min %u is bigger then max %u\n", p->min,
 144                        p->max);
 145                goto err_bad_param;
 146        }
 147        if (p->max > MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_BUFFER_SIZE)) {
 148                dev_err(mlxsw_sp->bus_info->dev,
 149                        "spectrum: RED: max value %u is too big\n", p->max);
 150                goto err_bad_param;
 151        }
 152        if (p->min == 0 || p->max == 0) {
 153                dev_err(mlxsw_sp->bus_info->dev,
 154                        "spectrum: RED: 0 value is illegal for min and max\n");
 155                goto err_bad_param;
 156        }
 157
 158        /* calculate probability in percentage */
 159        prob = p->probability;
 160        prob *= 100;
 161        prob = DIV_ROUND_UP(prob, 1 << 16);
 162        prob = DIV_ROUND_UP(prob, 1 << 16);
 163        min = mlxsw_sp_bytes_cells(mlxsw_sp, p->min);
 164        max = mlxsw_sp_bytes_cells(mlxsw_sp, p->max);
 165        err = mlxsw_sp_tclass_congestion_enable(mlxsw_sp_port, tclass_num, min,
 166                                                max, prob, p->is_ecn);
 167        if (err)
 168                goto err_config;
 169
 170        mlxsw_sp_qdisc->type = MLXSW_SP_QDISC_RED;
 171        if (mlxsw_sp_qdisc->handle != handle)
 172                mlxsw_sp_setup_tc_qdisc_clean_stats(mlxsw_sp_port,
 173                                                    mlxsw_sp_qdisc,
 174                                                    tclass_num);
 175
 176        mlxsw_sp_qdisc->handle = handle;
 177        return 0;
 178
 179err_bad_param:
 180        err = -EINVAL;
 181err_config:
 182        mlxsw_sp_qdisc_red_destroy(mlxsw_sp_port, mlxsw_sp_qdisc->handle,
 183                                   mlxsw_sp_qdisc, tclass_num);
 184        return err;
 185}
 186
 187static int
 188mlxsw_sp_qdisc_get_red_xstats(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
 189                              struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
 190                              int tclass_num, struct red_stats *res)
 191{
 192        struct red_stats *xstats_base = &mlxsw_sp_qdisc->xstats_base;
 193        struct mlxsw_sp_port_xstats *xstats;
 194
 195        if (mlxsw_sp_qdisc->handle != handle ||
 196            mlxsw_sp_qdisc->type != MLXSW_SP_QDISC_RED)
 197                return -EOPNOTSUPP;
 198
 199        xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
 200
 201        res->prob_drop = xstats->wred_drop[tclass_num] - xstats_base->prob_drop;
 202        res->prob_mark = xstats->ecn - xstats_base->prob_mark;
 203        res->pdrop = xstats->tail_drop[tclass_num] - xstats_base->pdrop;
 204        return 0;
 205}
 206
 207static int
 208mlxsw_sp_qdisc_get_red_stats(struct mlxsw_sp_port *mlxsw_sp_port, u32 handle,
 209                             struct mlxsw_sp_qdisc *mlxsw_sp_qdisc,
 210                             int tclass_num,
 211                             struct tc_red_qopt_offload_stats *res)
 212{
 213        u64 tx_bytes, tx_packets, overlimits, drops;
 214        struct mlxsw_sp_port_xstats *xstats;
 215        struct rtnl_link_stats64 *stats;
 216
 217        if (mlxsw_sp_qdisc->handle != handle ||
 218            mlxsw_sp_qdisc->type != MLXSW_SP_QDISC_RED)
 219                return -EOPNOTSUPP;
 220
 221        xstats = &mlxsw_sp_port->periodic_hw_stats.xstats;
 222        stats = &mlxsw_sp_port->periodic_hw_stats.stats;
 223
 224        tx_bytes = stats->tx_bytes - mlxsw_sp_qdisc->tx_bytes;
 225        tx_packets = stats->tx_packets - mlxsw_sp_qdisc->tx_packets;
 226        overlimits = xstats->wred_drop[tclass_num] + xstats->ecn -
 227                     mlxsw_sp_qdisc->overlimits;
 228        drops = xstats->wred_drop[tclass_num] + xstats->tail_drop[tclass_num] -
 229                mlxsw_sp_qdisc->drops;
 230
 231        _bstats_update(res->bstats, tx_bytes, tx_packets);
 232        res->qstats->overlimits += overlimits;
 233        res->qstats->drops += drops;
 234        res->qstats->backlog += mlxsw_sp_cells_bytes(mlxsw_sp_port->mlxsw_sp,
 235                                                xstats->backlog[tclass_num]);
 236
 237        mlxsw_sp_qdisc->drops +=  drops;
 238        mlxsw_sp_qdisc->overlimits += overlimits;
 239        mlxsw_sp_qdisc->tx_bytes += tx_bytes;
 240        mlxsw_sp_qdisc->tx_packets += tx_packets;
 241        return 0;
 242}
 243
 244#define MLXSW_SP_PORT_DEFAULT_TCLASS 0
 245
 246int mlxsw_sp_setup_tc_red(struct mlxsw_sp_port *mlxsw_sp_port,
 247                          struct tc_red_qopt_offload *p)
 248{
 249        struct mlxsw_sp_qdisc *mlxsw_sp_qdisc;
 250        int tclass_num;
 251
 252        if (p->parent != TC_H_ROOT)
 253                return -EOPNOTSUPP;
 254
 255        mlxsw_sp_qdisc = &mlxsw_sp_port->root_qdisc;
 256        tclass_num = MLXSW_SP_PORT_DEFAULT_TCLASS;
 257
 258        switch (p->command) {
 259        case TC_RED_REPLACE:
 260                return mlxsw_sp_qdisc_red_replace(mlxsw_sp_port, p->handle,
 261                                                  mlxsw_sp_qdisc, tclass_num,
 262                                                  &p->set);
 263        case TC_RED_DESTROY:
 264                return mlxsw_sp_qdisc_red_destroy(mlxsw_sp_port, p->handle,
 265                                                  mlxsw_sp_qdisc, tclass_num);
 266        case TC_RED_XSTATS:
 267                return mlxsw_sp_qdisc_get_red_xstats(mlxsw_sp_port, p->handle,
 268                                                     mlxsw_sp_qdisc, tclass_num,
 269                                                     p->xstats);
 270        case TC_RED_STATS:
 271                return mlxsw_sp_qdisc_get_red_stats(mlxsw_sp_port, p->handle,
 272                                                    mlxsw_sp_qdisc, tclass_num,
 273                                                    &p->stats);
 274        default:
 275                return -EOPNOTSUPP;
 276        }
 277}
 278