linux/drivers/net/ethernet/mellanox/mlx5/core/en/port_buffer.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2018, Mellanox Technologies. All rights reserved.
   3 *
   4 * This software is available to you under a choice of one of two
   5 * licenses.  You may choose to be licensed under the terms of the GNU
   6 * General Public License (GPL) Version 2, available from the file
   7 * COPYING in the main directory of this source tree, or the
   8 * OpenIB.org BSD license below:
   9 *
  10 *     Redistribution and use in source and binary forms, with or
  11 *     without modification, are permitted provided that the following
  12 *     conditions are met:
  13 *
  14 *      - Redistributions of source code must retain the above
  15 *        copyright notice, this list of conditions and the following
  16 *        disclaimer.
  17 *
  18 *      - Redistributions in binary form must reproduce the above
  19 *        copyright notice, this list of conditions and the following
  20 *        disclaimer in the documentation and/or other materials
  21 *        provided with the distribution.
  22 *
  23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  30 * SOFTWARE.
  31 */
  32#include "port_buffer.h"
  33
  34int mlx5e_port_query_buffer(struct mlx5e_priv *priv,
  35                            struct mlx5e_port_buffer *port_buffer)
  36{
  37        u16 port_buff_cell_sz = priv->dcbx.port_buff_cell_sz;
  38        struct mlx5_core_dev *mdev = priv->mdev;
  39        int sz = MLX5_ST_SZ_BYTES(pbmc_reg);
  40        u32 total_used = 0;
  41        void *buffer;
  42        void *out;
  43        int err;
  44        int i;
  45
  46        out = kzalloc(sz, GFP_KERNEL);
  47        if (!out)
  48                return -ENOMEM;
  49
  50        err = mlx5e_port_query_pbmc(mdev, out);
  51        if (err)
  52                goto out;
  53
  54        for (i = 0; i < MLX5E_MAX_BUFFER; i++) {
  55                buffer = MLX5_ADDR_OF(pbmc_reg, out, buffer[i]);
  56                port_buffer->buffer[i].lossy =
  57                        MLX5_GET(bufferx_reg, buffer, lossy);
  58                port_buffer->buffer[i].epsb =
  59                        MLX5_GET(bufferx_reg, buffer, epsb);
  60                port_buffer->buffer[i].size =
  61                        MLX5_GET(bufferx_reg, buffer, size) * port_buff_cell_sz;
  62                port_buffer->buffer[i].xon =
  63                        MLX5_GET(bufferx_reg, buffer, xon_threshold) * port_buff_cell_sz;
  64                port_buffer->buffer[i].xoff =
  65                        MLX5_GET(bufferx_reg, buffer, xoff_threshold) * port_buff_cell_sz;
  66                total_used += port_buffer->buffer[i].size;
  67
  68                mlx5e_dbg(HW, priv, "buffer %d: size=%d, xon=%d, xoff=%d, epsb=%d, lossy=%d\n", i,
  69                          port_buffer->buffer[i].size,
  70                          port_buffer->buffer[i].xon,
  71                          port_buffer->buffer[i].xoff,
  72                          port_buffer->buffer[i].epsb,
  73                          port_buffer->buffer[i].lossy);
  74        }
  75
  76        port_buffer->port_buffer_size =
  77                MLX5_GET(pbmc_reg, out, port_buffer_size) * port_buff_cell_sz;
  78        port_buffer->spare_buffer_size =
  79                port_buffer->port_buffer_size - total_used;
  80
  81        mlx5e_dbg(HW, priv, "total buffer size=%d, spare buffer size=%d\n",
  82                  port_buffer->port_buffer_size,
  83                  port_buffer->spare_buffer_size);
  84out:
  85        kfree(out);
  86        return err;
  87}
  88
  89static int port_set_buffer(struct mlx5e_priv *priv,
  90                           struct mlx5e_port_buffer *port_buffer)
  91{
  92        u16 port_buff_cell_sz = priv->dcbx.port_buff_cell_sz;
  93        struct mlx5_core_dev *mdev = priv->mdev;
  94        int sz = MLX5_ST_SZ_BYTES(pbmc_reg);
  95        void *in;
  96        int err;
  97        int i;
  98
  99        in = kzalloc(sz, GFP_KERNEL);
 100        if (!in)
 101                return -ENOMEM;
 102
 103        err = mlx5e_port_query_pbmc(mdev, in);
 104        if (err)
 105                goto out;
 106
 107        for (i = 0; i < MLX5E_MAX_BUFFER; i++) {
 108                void *buffer = MLX5_ADDR_OF(pbmc_reg, in, buffer[i]);
 109                u64 size = port_buffer->buffer[i].size;
 110                u64 xoff = port_buffer->buffer[i].xoff;
 111                u64 xon = port_buffer->buffer[i].xon;
 112
 113                do_div(size, port_buff_cell_sz);
 114                do_div(xoff, port_buff_cell_sz);
 115                do_div(xon, port_buff_cell_sz);
 116                MLX5_SET(bufferx_reg, buffer, size, size);
 117                MLX5_SET(bufferx_reg, buffer, lossy, port_buffer->buffer[i].lossy);
 118                MLX5_SET(bufferx_reg, buffer, xoff_threshold, xoff);
 119                MLX5_SET(bufferx_reg, buffer, xon_threshold, xon);
 120        }
 121
 122        err = mlx5e_port_set_pbmc(mdev, in);
 123out:
 124        kfree(in);
 125        return err;
 126}
 127
 128/* xoff = ((301+2.16 * len [m]) * speed [Gbps] + 2.72 MTU [B])
 129 * minimum speed value is 40Gbps
 130 */
 131static u32 calculate_xoff(struct mlx5e_priv *priv, unsigned int mtu)
 132{
 133        u32 speed;
 134        u32 xoff;
 135        int err;
 136
 137        err = mlx5e_port_linkspeed(priv->mdev, &speed);
 138        if (err)
 139                speed = SPEED_40000;
 140        speed = max_t(u32, speed, SPEED_40000);
 141
 142        xoff = (301 + 216 * priv->dcbx.cable_len / 100) * speed / 1000 + 272 * mtu / 100;
 143
 144        mlx5e_dbg(HW, priv, "%s: xoff=%d\n", __func__, xoff);
 145        return xoff;
 146}
 147
 148static int update_xoff_threshold(struct mlx5e_port_buffer *port_buffer,
 149                                 u32 xoff, unsigned int max_mtu, u16 port_buff_cell_sz)
 150{
 151        int i;
 152
 153        for (i = 0; i < MLX5E_MAX_BUFFER; i++) {
 154                if (port_buffer->buffer[i].lossy) {
 155                        port_buffer->buffer[i].xoff = 0;
 156                        port_buffer->buffer[i].xon  = 0;
 157                        continue;
 158                }
 159
 160                if (port_buffer->buffer[i].size <
 161                    (xoff + max_mtu + port_buff_cell_sz)) {
 162                        pr_err("buffer_size[%d]=%d is not enough for lossless buffer\n",
 163                               i, port_buffer->buffer[i].size);
 164                        return -ENOMEM;
 165                }
 166
 167                port_buffer->buffer[i].xoff = port_buffer->buffer[i].size - xoff;
 168                port_buffer->buffer[i].xon  =
 169                        port_buffer->buffer[i].xoff - max_mtu;
 170        }
 171
 172        return 0;
 173}
 174
 175/**
 176 *      update_buffer_lossy     - Update buffer configuration based on pfc
 177 *      @max_mtu: netdev's max_mtu
 178 *      @pfc_en: <input> current pfc configuration
 179 *      @buffer: <input> current prio to buffer mapping
 180 *      @xoff:   <input> xoff value
 181 *      @port_buff_cell_sz: <input> port buffer cell_size
 182 *      @port_buffer: <output> port receive buffer configuration
 183 *      @change: <output>
 184 *
 185 *      Update buffer configuration based on pfc configuration and
 186 *      priority to buffer mapping.
 187 *      Buffer's lossy bit is changed to:
 188 *              lossless if there is at least one PFC enabled priority
 189 *              mapped to this buffer lossy if all priorities mapped to
 190 *              this buffer are PFC disabled
 191 *
 192 *      @return: 0 if no error,
 193 *      sets change to true if buffer configuration was modified.
 194 */
 195static int update_buffer_lossy(unsigned int max_mtu,
 196                               u8 pfc_en, u8 *buffer, u32 xoff, u16 port_buff_cell_sz,
 197                               struct mlx5e_port_buffer *port_buffer,
 198                               bool *change)
 199{
 200        bool changed = false;
 201        u8 lossy_count;
 202        u8 prio_count;
 203        u8 lossy;
 204        int prio;
 205        int err;
 206        int i;
 207
 208        for (i = 0; i < MLX5E_MAX_BUFFER; i++) {
 209                prio_count = 0;
 210                lossy_count = 0;
 211
 212                for (prio = 0; prio < MLX5E_MAX_PRIORITY; prio++) {
 213                        if (buffer[prio] != i)
 214                                continue;
 215
 216                        prio_count++;
 217                        lossy_count += !(pfc_en & (1 << prio));
 218                }
 219
 220                if (lossy_count == prio_count)
 221                        lossy = 1;
 222                else /* lossy_count < prio_count */
 223                        lossy = 0;
 224
 225                if (lossy != port_buffer->buffer[i].lossy) {
 226                        port_buffer->buffer[i].lossy = lossy;
 227                        changed = true;
 228                }
 229        }
 230
 231        if (changed) {
 232                err = update_xoff_threshold(port_buffer, xoff, max_mtu, port_buff_cell_sz);
 233                if (err)
 234                        return err;
 235
 236                *change = true;
 237        }
 238
 239        return 0;
 240}
 241
 242static int fill_pfc_en(struct mlx5_core_dev *mdev, u8 *pfc_en)
 243{
 244        u32 g_rx_pause, g_tx_pause;
 245        int err;
 246
 247        err = mlx5_query_port_pause(mdev, &g_rx_pause, &g_tx_pause);
 248        if (err)
 249                return err;
 250
 251        /* If global pause enabled, set all active buffers to lossless.
 252         * Otherwise, check PFC setting.
 253         */
 254        if (g_rx_pause || g_tx_pause)
 255                *pfc_en = 0xff;
 256        else
 257                err = mlx5_query_port_pfc(mdev, pfc_en, NULL);
 258
 259        return err;
 260}
 261
 262#define MINIMUM_MAX_MTU 9216
 263int mlx5e_port_manual_buffer_config(struct mlx5e_priv *priv,
 264                                    u32 change, unsigned int mtu,
 265                                    struct ieee_pfc *pfc,
 266                                    u32 *buffer_size,
 267                                    u8 *prio2buffer)
 268{
 269        u16 port_buff_cell_sz = priv->dcbx.port_buff_cell_sz;
 270        struct mlx5e_port_buffer port_buffer;
 271        u32 xoff = calculate_xoff(priv, mtu);
 272        bool update_prio2buffer = false;
 273        u8 buffer[MLX5E_MAX_PRIORITY];
 274        bool update_buffer = false;
 275        unsigned int max_mtu;
 276        u32 total_used = 0;
 277        u8 curr_pfc_en;
 278        int err;
 279        int i;
 280
 281        mlx5e_dbg(HW, priv, "%s: change=%x\n", __func__, change);
 282        max_mtu = max_t(unsigned int, priv->netdev->max_mtu, MINIMUM_MAX_MTU);
 283
 284        err = mlx5e_port_query_buffer(priv, &port_buffer);
 285        if (err)
 286                return err;
 287
 288        if (change & MLX5E_PORT_BUFFER_CABLE_LEN) {
 289                update_buffer = true;
 290                err = update_xoff_threshold(&port_buffer, xoff, max_mtu, port_buff_cell_sz);
 291                if (err)
 292                        return err;
 293        }
 294
 295        if (change & MLX5E_PORT_BUFFER_PFC) {
 296                err = mlx5e_port_query_priority2buffer(priv->mdev, buffer);
 297                if (err)
 298                        return err;
 299
 300                err = update_buffer_lossy(max_mtu, pfc->pfc_en, buffer, xoff, port_buff_cell_sz,
 301                                          &port_buffer, &update_buffer);
 302                if (err)
 303                        return err;
 304        }
 305
 306        if (change & MLX5E_PORT_BUFFER_PRIO2BUFFER) {
 307                update_prio2buffer = true;
 308                err = fill_pfc_en(priv->mdev, &curr_pfc_en);
 309                if (err)
 310                        return err;
 311
 312                err = update_buffer_lossy(max_mtu, curr_pfc_en, prio2buffer, port_buff_cell_sz,
 313                                          xoff, &port_buffer, &update_buffer);
 314                if (err)
 315                        return err;
 316        }
 317
 318        if (change & MLX5E_PORT_BUFFER_SIZE) {
 319                for (i = 0; i < MLX5E_MAX_BUFFER; i++) {
 320                        mlx5e_dbg(HW, priv, "%s: buffer[%d]=%d\n", __func__, i, buffer_size[i]);
 321                        if (!port_buffer.buffer[i].lossy && !buffer_size[i]) {
 322                                mlx5e_dbg(HW, priv, "%s: lossless buffer[%d] size cannot be zero\n",
 323                                          __func__, i);
 324                                return -EINVAL;
 325                        }
 326
 327                        port_buffer.buffer[i].size = buffer_size[i];
 328                        total_used += buffer_size[i];
 329                }
 330
 331                mlx5e_dbg(HW, priv, "%s: total buffer requested=%d\n", __func__, total_used);
 332
 333                if (total_used > port_buffer.port_buffer_size)
 334                        return -EINVAL;
 335
 336                update_buffer = true;
 337                err = update_xoff_threshold(&port_buffer, xoff, max_mtu, port_buff_cell_sz);
 338                if (err)
 339                        return err;
 340        }
 341
 342        /* Need to update buffer configuration if xoff value is changed */
 343        if (!update_buffer && xoff != priv->dcbx.xoff) {
 344                update_buffer = true;
 345                err = update_xoff_threshold(&port_buffer, xoff, max_mtu, port_buff_cell_sz);
 346                if (err)
 347                        return err;
 348        }
 349        priv->dcbx.xoff = xoff;
 350
 351        /* Apply the settings */
 352        if (update_buffer) {
 353                err = port_set_buffer(priv, &port_buffer);
 354                if (err)
 355                        return err;
 356        }
 357
 358        if (update_prio2buffer)
 359                err = mlx5e_port_set_priority2buffer(priv->mdev, prio2buffer);
 360
 361        return err;
 362}
 363