1/*- 2 * Copyright (c) 2018-2019 Mellanox Technologies. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 * 25 * $FreeBSD: stable/11/sys/dev/mlx5/mlx5_en/mlx5_en_port_buffer.c 369094 2021-01-22 12:49:45Z hselasky $ 26 */ 27 28#include "port_buffer.h" 29 30#define MLX5E_MAX_PORT_MTU 9216 31 32int mlx5e_port_query_buffer(struct mlx5e_priv *priv, 33 struct mlx5e_port_buffer *port_buffer) 34{ 35 struct mlx5_core_dev *mdev = priv->mdev; 36 int sz = MLX5_ST_SZ_BYTES(pbmc_reg); 37 u32 total_used = 0; 38 void *buffer; 39 void *out; 40 int err; 41 int i; 42 43 out = kzalloc(sz, GFP_KERNEL); 44 if (!out) 45 return -ENOMEM; 46 47 err = mlx5e_port_query_pbmc(mdev, out); 48 if (err) 49 goto out; 50 51 for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 52 buffer = MLX5_ADDR_OF(pbmc_reg, out, buffer[i]); 53 port_buffer->buffer[i].lossy = 54 MLX5_GET(bufferx_reg, buffer, lossy); 55 port_buffer->buffer[i].epsb = 56 MLX5_GET(bufferx_reg, buffer, epsb); 57 port_buffer->buffer[i].size = 58 MLX5_GET(bufferx_reg, buffer, size) << MLX5E_BUFFER_CELL_SHIFT; 59 port_buffer->buffer[i].xon = 60 MLX5_GET(bufferx_reg, buffer, xon_threshold) << MLX5E_BUFFER_CELL_SHIFT; 61 port_buffer->buffer[i].xoff = 62 MLX5_GET(bufferx_reg, buffer, xoff_threshold) << MLX5E_BUFFER_CELL_SHIFT; 63 total_used += port_buffer->buffer[i].size; 64 65 mlx5e_dbg(HW, priv, "buffer %d: size=%d, xon=%d, xoff=%d, epsb=%d, lossy=%d\n", i, 66 port_buffer->buffer[i].size, 67 port_buffer->buffer[i].xon, 68 port_buffer->buffer[i].xoff, 69 port_buffer->buffer[i].epsb, 70 port_buffer->buffer[i].lossy); 71 } 72 73 port_buffer->port_buffer_size = 74 MLX5_GET(pbmc_reg, out, port_buffer_size) << MLX5E_BUFFER_CELL_SHIFT; 75 port_buffer->spare_buffer_size = 76 port_buffer->port_buffer_size - total_used; 77 78 mlx5e_dbg(HW, priv, "total buffer size=%d, spare buffer size=%d\n", 79 port_buffer->port_buffer_size, 80 port_buffer->spare_buffer_size); 81out: 82 kfree(out); 83 return err; 84} 85 86static int port_set_buffer(struct mlx5e_priv *priv, 87 struct mlx5e_port_buffer *port_buffer) 88{ 89 struct mlx5_core_dev *mdev = priv->mdev; 90 int sz = MLX5_ST_SZ_BYTES(pbmc_reg); 91 void *buffer; 92 void *in; 93 int err; 94 int i; 95 96 in = kzalloc(sz, GFP_KERNEL); 97 if (!in) 98 return -ENOMEM; 99 100 err = mlx5e_port_query_pbmc(mdev, in); 101 if (err) 102 goto out; 103 104 for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 105 buffer = MLX5_ADDR_OF(pbmc_reg, in, buffer[i]); 106 107 MLX5_SET(bufferx_reg, buffer, size, 108 port_buffer->buffer[i].size >> MLX5E_BUFFER_CELL_SHIFT); 109 MLX5_SET(bufferx_reg, buffer, lossy, 110 port_buffer->buffer[i].lossy); 111 MLX5_SET(bufferx_reg, buffer, xoff_threshold, 112 port_buffer->buffer[i].xoff >> MLX5E_BUFFER_CELL_SHIFT); 113 MLX5_SET(bufferx_reg, buffer, xon_threshold, 114 port_buffer->buffer[i].xon >> MLX5E_BUFFER_CELL_SHIFT); 115 } 116 117 err = mlx5e_port_set_pbmc(mdev, in); 118out: 119 kfree(in); 120 return err; 121} 122 123/* xoff = ((301+2.16 * len [m]) * speed [Gbps] + 2.72 MTU [B]) */ 124static u32 calculate_xoff(struct mlx5e_priv *priv, unsigned int mtu) 125{ 126 u32 speed; 127 u32 xoff; 128 int err; 129 130 err = mlx5e_port_linkspeed(priv->mdev, &speed); 131 if (err) { 132 mlx5_core_warn(priv->mdev, "cannot get port speed\n"); 133 speed = SPEED_40000; 134 } 135 speed = max_t(u32, speed, SPEED_40000); 136 xoff = (301 + 216 * priv->dcbx.cable_len / 100) * speed / 1000 + 272 * mtu / 100; 137 138 mlx5e_dbg(HW, priv, "%s: xoff=%d\n", __func__, xoff); 139 return xoff; 140} 141 142static int update_xoff_threshold(struct mlx5e_priv *priv, 143 struct mlx5e_port_buffer *port_buffer, u32 xoff) 144{ 145 int i; 146 147 for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 148 if (port_buffer->buffer[i].lossy) { 149 port_buffer->buffer[i].xoff = 0; 150 port_buffer->buffer[i].xon = 0; 151 continue; 152 } 153 154 if (port_buffer->buffer[i].size < 155 (xoff + MLX5E_MAX_PORT_MTU + (1 << MLX5E_BUFFER_CELL_SHIFT))) { 156 mlx5_en_info(priv->ifp, 157 "non-lossy buffer %d size %d less than xoff threshold %d\n", 158 i, port_buffer->buffer[i].size, 159 xoff + MLX5E_MAX_PORT_MTU + 160 (1 << MLX5E_BUFFER_CELL_SHIFT)); 161 return -ENOMEM; 162 } 163 164 port_buffer->buffer[i].xoff = port_buffer->buffer[i].size - xoff; 165 port_buffer->buffer[i].xon = 166 port_buffer->buffer[i].xoff - MLX5E_MAX_PORT_MTU; 167 } 168 169 return 0; 170} 171 172/** 173 * update_buffer_lossy() 174 * mtu: device's MTU 175 * pfc_en: <input> current pfc configuration 176 * buffer: <input> current prio to buffer mapping 177 * xoff: <input> xoff value 178 * port_buffer: <output> port receive buffer configuration 179 * change: <output> 180 * 181 * Update buffer configuration based on pfc configuraiton and priority 182 * to buffer mapping. 183 * Buffer's lossy bit is changed to: 184 * lossless if there is at least one PFC enabled priority mapped to this buffer 185 * lossy if all priorities mapped to this buffer are PFC disabled 186 * 187 * Return: 188 * Return 0 if no error. 189 * Set change to true if buffer configuration is modified. 190 */ 191static int update_buffer_lossy(struct mlx5e_priv *priv, unsigned int mtu, 192 u8 pfc_en, u8 *buffer, u32 xoff, 193 struct mlx5e_port_buffer *port_buffer, 194 bool *change) 195{ 196 bool changed = false; 197 u8 lossy_count; 198 u8 prio_count; 199 u8 lossy; 200 int prio; 201 int err; 202 int i; 203 204 for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 205 prio_count = 0; 206 lossy_count = 0; 207 208 for (prio = 0; prio < MLX5E_MAX_PRIORITY; prio++) { 209 if (buffer[prio] != i) 210 continue; 211 212 prio_count++; 213 lossy_count += !(pfc_en & (1 << prio)); 214 } 215 216 if (lossy_count == prio_count) 217 lossy = 1; 218 else /* lossy_count < prio_count */ 219 lossy = 0; 220 221 if (lossy != port_buffer->buffer[i].lossy) { 222 port_buffer->buffer[i].lossy = lossy; 223 changed = true; 224 } 225 } 226 227 if (changed) { 228 err = update_xoff_threshold(priv, port_buffer, xoff); 229 if (err) 230 return err; 231 232 *change = true; 233 } 234 235 return 0; 236} 237 238int mlx5e_port_manual_buffer_config(struct mlx5e_priv *priv, 239 u32 change, unsigned int mtu, 240 struct ieee_pfc *pfc, 241 u32 *buffer_size, 242 u8 *prio2buffer) 243{ 244 struct mlx5e_port_buffer port_buffer; 245 u32 xoff = calculate_xoff(priv, mtu); 246 bool update_prio2buffer = false; 247 u8 buffer[MLX5E_MAX_PRIORITY]; 248 bool update_buffer = false; 249 u32 total_used = 0; 250 u8 curr_pfc_en; 251 int err; 252 int i; 253 254 mlx5e_dbg(HW, priv, "%s: change=%x\n", __func__, change); 255 256 err = mlx5e_port_query_buffer(priv, &port_buffer); 257 if (err) 258 return err; 259 260 if (change & MLX5E_PORT_BUFFER_CABLE_LEN) { 261 update_buffer = true; 262 err = update_xoff_threshold(priv, &port_buffer, xoff); 263 if (err) 264 return err; 265 } 266 267 if (change & MLX5E_PORT_BUFFER_PFC) { 268 err = mlx5e_port_query_priority2buffer(priv->mdev, buffer); 269 if (err) 270 return err; 271 272 priv->sw_is_port_buf_owner = true; 273 err = update_buffer_lossy(priv, mtu, pfc->pfc_en, buffer, xoff, 274 &port_buffer, &update_buffer); 275 if (err) 276 return err; 277 } 278 279 if (change & MLX5E_PORT_BUFFER_PRIO2BUFFER) { 280 update_prio2buffer = true; 281 err = mlx5_query_port_pfc(priv->mdev, &curr_pfc_en, NULL); 282 if (err) 283 return err; 284 285 err = update_buffer_lossy(priv, mtu, curr_pfc_en, prio2buffer, xoff, 286 &port_buffer, &update_buffer); 287 if (err) 288 return err; 289 } 290 291 if (change & MLX5E_PORT_BUFFER_SIZE) { 292 for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 293 mlx5e_dbg(HW, priv, "%s: buffer[%d]=%d\n", __func__, i, buffer_size[i]); 294 if (!port_buffer.buffer[i].lossy && !buffer_size[i]) { 295 mlx5e_dbg(HW, priv, "%s: lossless buffer[%d] size cannot be zero\n", 296 __func__, i); 297 return -EINVAL; 298 } 299 300 port_buffer.buffer[i].size = buffer_size[i]; 301 total_used += buffer_size[i]; 302 } 303 304 mlx5e_dbg(HW, priv, "%s: total buffer requested=%d\n", __func__, total_used); 305 306 if (total_used > port_buffer.port_buffer_size) 307 return -EINVAL; 308 309 update_buffer = true; 310 err = update_xoff_threshold(priv, &port_buffer, xoff); 311 if (err) 312 return err; 313 } 314 315 /* Need to update buffer configuration if xoff value is changed */ 316 if (!update_buffer && xoff != priv->dcbx.xoff) { 317 update_buffer = true; 318 err = update_xoff_threshold(priv, &port_buffer, xoff); 319 if (err) 320 return err; 321 } 322 priv->dcbx.xoff = xoff; 323 324 /* Apply the settings */ 325 if (update_buffer) { 326 priv->sw_is_port_buf_owner = true; 327 err = port_set_buffer(priv, &port_buffer); 328 if (err) 329 return err; 330 } 331 332 if (update_prio2buffer) { 333 priv->sw_is_port_buf_owner = true; 334 err = mlx5e_port_set_priority2buffer(priv->mdev, prio2buffer); 335 } 336 337 return err; 338} 339