1352981Shselasky/*- 2369094Shselasky * Copyright (c) 2018-2019 Mellanox Technologies. All rights reserved. 3352981Shselasky * 4352981Shselasky * Redistribution and use in source and binary forms, with or without 5352981Shselasky * modification, are permitted provided that the following conditions 6352981Shselasky * are met: 7352981Shselasky * 1. Redistributions of source code must retain the above copyright 8352981Shselasky * notice, this list of conditions and the following disclaimer. 9352981Shselasky * 2. Redistributions in binary form must reproduce the above copyright 10352981Shselasky * notice, this list of conditions and the following disclaimer in the 11352981Shselasky * documentation and/or other materials provided with the distribution. 12352981Shselasky * 13352981Shselasky * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND 14352981Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15352981Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16352981Shselasky * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17352981Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18352981Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19352981Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20352981Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21352981Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22352981Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23352981Shselasky * SUCH DAMAGE. 24352981Shselasky * 25352981Shselasky * $FreeBSD: stable/11/sys/dev/mlx5/mlx5_en/mlx5_en_port_buffer.c 369094 2021-01-22 12:49:45Z hselasky $ 26352981Shselasky */ 27352981Shselasky 28352981Shselasky#include "port_buffer.h" 29352981Shselasky 30352981Shselasky#define MLX5E_MAX_PORT_MTU 9216 31352981Shselasky 32352981Shselaskyint mlx5e_port_query_buffer(struct mlx5e_priv *priv, 33352981Shselasky struct mlx5e_port_buffer *port_buffer) 34352981Shselasky{ 35352981Shselasky struct mlx5_core_dev *mdev = priv->mdev; 36352981Shselasky int sz = MLX5_ST_SZ_BYTES(pbmc_reg); 37352981Shselasky u32 total_used = 0; 38352981Shselasky void *buffer; 39352981Shselasky void *out; 40352981Shselasky int err; 41352981Shselasky int i; 42352981Shselasky 43352981Shselasky out = kzalloc(sz, GFP_KERNEL); 44352981Shselasky if (!out) 45352981Shselasky return -ENOMEM; 46352981Shselasky 47352981Shselasky err = mlx5e_port_query_pbmc(mdev, out); 48352981Shselasky if (err) 49352981Shselasky goto out; 50352981Shselasky 51352981Shselasky for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 52352981Shselasky buffer = MLX5_ADDR_OF(pbmc_reg, out, buffer[i]); 53352981Shselasky port_buffer->buffer[i].lossy = 54352981Shselasky MLX5_GET(bufferx_reg, buffer, lossy); 55352981Shselasky port_buffer->buffer[i].epsb = 56352981Shselasky MLX5_GET(bufferx_reg, buffer, epsb); 57352981Shselasky port_buffer->buffer[i].size = 58352981Shselasky MLX5_GET(bufferx_reg, buffer, size) << MLX5E_BUFFER_CELL_SHIFT; 59352981Shselasky port_buffer->buffer[i].xon = 60352981Shselasky MLX5_GET(bufferx_reg, buffer, xon_threshold) << MLX5E_BUFFER_CELL_SHIFT; 61352981Shselasky port_buffer->buffer[i].xoff = 62352981Shselasky MLX5_GET(bufferx_reg, buffer, xoff_threshold) << MLX5E_BUFFER_CELL_SHIFT; 63352981Shselasky total_used += port_buffer->buffer[i].size; 64352981Shselasky 65352981Shselasky mlx5e_dbg(HW, priv, "buffer %d: size=%d, xon=%d, xoff=%d, epsb=%d, lossy=%d\n", i, 66352981Shselasky port_buffer->buffer[i].size, 67352981Shselasky port_buffer->buffer[i].xon, 68352981Shselasky port_buffer->buffer[i].xoff, 69352981Shselasky port_buffer->buffer[i].epsb, 70352981Shselasky port_buffer->buffer[i].lossy); 71352981Shselasky } 72352981Shselasky 73352981Shselasky port_buffer->port_buffer_size = 74352981Shselasky MLX5_GET(pbmc_reg, out, port_buffer_size) << MLX5E_BUFFER_CELL_SHIFT; 75352981Shselasky port_buffer->spare_buffer_size = 76352981Shselasky port_buffer->port_buffer_size - total_used; 77352981Shselasky 78352981Shselasky mlx5e_dbg(HW, priv, "total buffer size=%d, spare buffer size=%d\n", 79352981Shselasky port_buffer->port_buffer_size, 80352981Shselasky port_buffer->spare_buffer_size); 81352981Shselaskyout: 82352981Shselasky kfree(out); 83352981Shselasky return err; 84352981Shselasky} 85352981Shselasky 86352981Shselaskystatic int port_set_buffer(struct mlx5e_priv *priv, 87352981Shselasky struct mlx5e_port_buffer *port_buffer) 88352981Shselasky{ 89352981Shselasky struct mlx5_core_dev *mdev = priv->mdev; 90352981Shselasky int sz = MLX5_ST_SZ_BYTES(pbmc_reg); 91352981Shselasky void *buffer; 92352981Shselasky void *in; 93352981Shselasky int err; 94352981Shselasky int i; 95352981Shselasky 96352981Shselasky in = kzalloc(sz, GFP_KERNEL); 97352981Shselasky if (!in) 98352981Shselasky return -ENOMEM; 99352981Shselasky 100352981Shselasky err = mlx5e_port_query_pbmc(mdev, in); 101352981Shselasky if (err) 102352981Shselasky goto out; 103352981Shselasky 104352981Shselasky for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 105352981Shselasky buffer = MLX5_ADDR_OF(pbmc_reg, in, buffer[i]); 106352981Shselasky 107352981Shselasky MLX5_SET(bufferx_reg, buffer, size, 108352981Shselasky port_buffer->buffer[i].size >> MLX5E_BUFFER_CELL_SHIFT); 109352981Shselasky MLX5_SET(bufferx_reg, buffer, lossy, 110352981Shselasky port_buffer->buffer[i].lossy); 111352981Shselasky MLX5_SET(bufferx_reg, buffer, xoff_threshold, 112352981Shselasky port_buffer->buffer[i].xoff >> MLX5E_BUFFER_CELL_SHIFT); 113352981Shselasky MLX5_SET(bufferx_reg, buffer, xon_threshold, 114352981Shselasky port_buffer->buffer[i].xon >> MLX5E_BUFFER_CELL_SHIFT); 115352981Shselasky } 116352981Shselasky 117352981Shselasky err = mlx5e_port_set_pbmc(mdev, in); 118352981Shselaskyout: 119352981Shselasky kfree(in); 120352981Shselasky return err; 121352981Shselasky} 122352981Shselasky 123352981Shselasky/* xoff = ((301+2.16 * len [m]) * speed [Gbps] + 2.72 MTU [B]) */ 124352981Shselaskystatic u32 calculate_xoff(struct mlx5e_priv *priv, unsigned int mtu) 125352981Shselasky{ 126352981Shselasky u32 speed; 127352981Shselasky u32 xoff; 128352981Shselasky int err; 129352981Shselasky 130352981Shselasky err = mlx5e_port_linkspeed(priv->mdev, &speed); 131352981Shselasky if (err) { 132352981Shselasky mlx5_core_warn(priv->mdev, "cannot get port speed\n"); 133352981Shselasky speed = SPEED_40000; 134352981Shselasky } 135352981Shselasky speed = max_t(u32, speed, SPEED_40000); 136352981Shselasky xoff = (301 + 216 * priv->dcbx.cable_len / 100) * speed / 1000 + 272 * mtu / 100; 137352981Shselasky 138352981Shselasky mlx5e_dbg(HW, priv, "%s: xoff=%d\n", __func__, xoff); 139352981Shselasky return xoff; 140352981Shselasky} 141352981Shselasky 142353264Shselaskystatic int update_xoff_threshold(struct mlx5e_priv *priv, 143353264Shselasky struct mlx5e_port_buffer *port_buffer, u32 xoff) 144352981Shselasky{ 145352981Shselasky int i; 146352981Shselasky 147352981Shselasky for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 148352981Shselasky if (port_buffer->buffer[i].lossy) { 149352981Shselasky port_buffer->buffer[i].xoff = 0; 150352981Shselasky port_buffer->buffer[i].xon = 0; 151352981Shselasky continue; 152352981Shselasky } 153352981Shselasky 154352981Shselasky if (port_buffer->buffer[i].size < 155353264Shselasky (xoff + MLX5E_MAX_PORT_MTU + (1 << MLX5E_BUFFER_CELL_SHIFT))) { 156353264Shselasky mlx5_en_info(priv->ifp, 157353264Shselasky "non-lossy buffer %d size %d less than xoff threshold %d\n", 158353264Shselasky i, port_buffer->buffer[i].size, 159353264Shselasky xoff + MLX5E_MAX_PORT_MTU + 160353264Shselasky (1 << MLX5E_BUFFER_CELL_SHIFT)); 161352981Shselasky return -ENOMEM; 162353264Shselasky } 163352981Shselasky 164352981Shselasky port_buffer->buffer[i].xoff = port_buffer->buffer[i].size - xoff; 165352981Shselasky port_buffer->buffer[i].xon = 166352981Shselasky port_buffer->buffer[i].xoff - MLX5E_MAX_PORT_MTU; 167352981Shselasky } 168352981Shselasky 169352981Shselasky return 0; 170352981Shselasky} 171352981Shselasky 172352981Shselasky/** 173352981Shselasky * update_buffer_lossy() 174352981Shselasky * mtu: device's MTU 175352981Shselasky * pfc_en: <input> current pfc configuration 176352981Shselasky * buffer: <input> current prio to buffer mapping 177352981Shselasky * xoff: <input> xoff value 178352981Shselasky * port_buffer: <output> port receive buffer configuration 179352981Shselasky * change: <output> 180352981Shselasky * 181352981Shselasky * Update buffer configuration based on pfc configuraiton and priority 182352981Shselasky * to buffer mapping. 183352981Shselasky * Buffer's lossy bit is changed to: 184352981Shselasky * lossless if there is at least one PFC enabled priority mapped to this buffer 185352981Shselasky * lossy if all priorities mapped to this buffer are PFC disabled 186352981Shselasky * 187352981Shselasky * Return: 188352981Shselasky * Return 0 if no error. 189352981Shselasky * Set change to true if buffer configuration is modified. 190352981Shselasky */ 191353264Shselaskystatic int update_buffer_lossy(struct mlx5e_priv *priv, unsigned int mtu, 192352981Shselasky u8 pfc_en, u8 *buffer, u32 xoff, 193352981Shselasky struct mlx5e_port_buffer *port_buffer, 194352981Shselasky bool *change) 195352981Shselasky{ 196352981Shselasky bool changed = false; 197352981Shselasky u8 lossy_count; 198352981Shselasky u8 prio_count; 199352981Shselasky u8 lossy; 200352981Shselasky int prio; 201352981Shselasky int err; 202352981Shselasky int i; 203352981Shselasky 204352981Shselasky for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 205352981Shselasky prio_count = 0; 206352981Shselasky lossy_count = 0; 207352981Shselasky 208352981Shselasky for (prio = 0; prio < MLX5E_MAX_PRIORITY; prio++) { 209352981Shselasky if (buffer[prio] != i) 210352981Shselasky continue; 211352981Shselasky 212352981Shselasky prio_count++; 213352981Shselasky lossy_count += !(pfc_en & (1 << prio)); 214352981Shselasky } 215352981Shselasky 216352981Shselasky if (lossy_count == prio_count) 217352981Shselasky lossy = 1; 218352981Shselasky else /* lossy_count < prio_count */ 219352981Shselasky lossy = 0; 220352981Shselasky 221352981Shselasky if (lossy != port_buffer->buffer[i].lossy) { 222352981Shselasky port_buffer->buffer[i].lossy = lossy; 223352981Shselasky changed = true; 224352981Shselasky } 225352981Shselasky } 226352981Shselasky 227352981Shselasky if (changed) { 228353264Shselasky err = update_xoff_threshold(priv, port_buffer, xoff); 229352981Shselasky if (err) 230352981Shselasky return err; 231352981Shselasky 232352981Shselasky *change = true; 233352981Shselasky } 234352981Shselasky 235352981Shselasky return 0; 236352981Shselasky} 237352981Shselasky 238352981Shselaskyint mlx5e_port_manual_buffer_config(struct mlx5e_priv *priv, 239352981Shselasky u32 change, unsigned int mtu, 240352981Shselasky struct ieee_pfc *pfc, 241352981Shselasky u32 *buffer_size, 242352981Shselasky u8 *prio2buffer) 243352981Shselasky{ 244352981Shselasky struct mlx5e_port_buffer port_buffer; 245352981Shselasky u32 xoff = calculate_xoff(priv, mtu); 246352981Shselasky bool update_prio2buffer = false; 247352981Shselasky u8 buffer[MLX5E_MAX_PRIORITY]; 248352981Shselasky bool update_buffer = false; 249352981Shselasky u32 total_used = 0; 250352981Shselasky u8 curr_pfc_en; 251352981Shselasky int err; 252352981Shselasky int i; 253352981Shselasky 254352981Shselasky mlx5e_dbg(HW, priv, "%s: change=%x\n", __func__, change); 255352981Shselasky 256352981Shselasky err = mlx5e_port_query_buffer(priv, &port_buffer); 257352981Shselasky if (err) 258352981Shselasky return err; 259352981Shselasky 260352981Shselasky if (change & MLX5E_PORT_BUFFER_CABLE_LEN) { 261352981Shselasky update_buffer = true; 262353264Shselasky err = update_xoff_threshold(priv, &port_buffer, xoff); 263352981Shselasky if (err) 264352981Shselasky return err; 265352981Shselasky } 266352981Shselasky 267352981Shselasky if (change & MLX5E_PORT_BUFFER_PFC) { 268352981Shselasky err = mlx5e_port_query_priority2buffer(priv->mdev, buffer); 269352981Shselasky if (err) 270352981Shselasky return err; 271352981Shselasky 272353262Shselasky priv->sw_is_port_buf_owner = true; 273353264Shselasky err = update_buffer_lossy(priv, mtu, pfc->pfc_en, buffer, xoff, 274352981Shselasky &port_buffer, &update_buffer); 275352981Shselasky if (err) 276352981Shselasky return err; 277352981Shselasky } 278352981Shselasky 279352981Shselasky if (change & MLX5E_PORT_BUFFER_PRIO2BUFFER) { 280352981Shselasky update_prio2buffer = true; 281352981Shselasky err = mlx5_query_port_pfc(priv->mdev, &curr_pfc_en, NULL); 282352981Shselasky if (err) 283352981Shselasky return err; 284352981Shselasky 285353264Shselasky err = update_buffer_lossy(priv, mtu, curr_pfc_en, prio2buffer, xoff, 286352981Shselasky &port_buffer, &update_buffer); 287352981Shselasky if (err) 288352981Shselasky return err; 289352981Shselasky } 290352981Shselasky 291352981Shselasky if (change & MLX5E_PORT_BUFFER_SIZE) { 292352981Shselasky for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 293352981Shselasky mlx5e_dbg(HW, priv, "%s: buffer[%d]=%d\n", __func__, i, buffer_size[i]); 294352981Shselasky if (!port_buffer.buffer[i].lossy && !buffer_size[i]) { 295352981Shselasky mlx5e_dbg(HW, priv, "%s: lossless buffer[%d] size cannot be zero\n", 296352981Shselasky __func__, i); 297352981Shselasky return -EINVAL; 298352981Shselasky } 299352981Shselasky 300352981Shselasky port_buffer.buffer[i].size = buffer_size[i]; 301352981Shselasky total_used += buffer_size[i]; 302352981Shselasky } 303352981Shselasky 304352981Shselasky mlx5e_dbg(HW, priv, "%s: total buffer requested=%d\n", __func__, total_used); 305352981Shselasky 306352981Shselasky if (total_used > port_buffer.port_buffer_size) 307352981Shselasky return -EINVAL; 308352981Shselasky 309352981Shselasky update_buffer = true; 310353264Shselasky err = update_xoff_threshold(priv, &port_buffer, xoff); 311352981Shselasky if (err) 312352981Shselasky return err; 313352981Shselasky } 314352981Shselasky 315352981Shselasky /* Need to update buffer configuration if xoff value is changed */ 316352981Shselasky if (!update_buffer && xoff != priv->dcbx.xoff) { 317352981Shselasky update_buffer = true; 318353264Shselasky err = update_xoff_threshold(priv, &port_buffer, xoff); 319352981Shselasky if (err) 320352981Shselasky return err; 321352981Shselasky } 322352981Shselasky priv->dcbx.xoff = xoff; 323352981Shselasky 324352981Shselasky /* Apply the settings */ 325352981Shselasky if (update_buffer) { 326353262Shselasky priv->sw_is_port_buf_owner = true; 327352981Shselasky err = port_set_buffer(priv, &port_buffer); 328352981Shselasky if (err) 329352981Shselasky return err; 330352981Shselasky } 331352981Shselasky 332353262Shselasky if (update_prio2buffer) { 333353262Shselasky priv->sw_is_port_buf_owner = true; 334352981Shselasky err = mlx5e_port_set_priority2buffer(priv->mdev, prio2buffer); 335353262Shselasky } 336352981Shselasky 337352981Shselasky return err; 338352981Shselasky} 339