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 26 #include "opt_rss.h" 27 #include "opt_ratelimit.h" 28 29 #include <dev/mlx5/mlx5_en/port_buffer.h> 30 31 #define MLX5E_MAX_PORT_MTU 9216 32 33 int mlx5e_port_query_buffer(struct mlx5e_priv *priv, 34 struct mlx5e_port_buffer *port_buffer) 35 { 36 struct mlx5_core_dev *mdev = priv->mdev; 37 int sz = MLX5_ST_SZ_BYTES(pbmc_reg); 38 u32 total_used = 0; 39 void *buffer; 40 void *out; 41 int err; 42 int i; 43 44 out = kzalloc(sz, GFP_KERNEL); 45 if (!out) 46 return -ENOMEM; 47 48 err = mlx5e_port_query_pbmc(mdev, out); 49 if (err) 50 goto out; 51 52 for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 53 buffer = MLX5_ADDR_OF(pbmc_reg, out, buffer[i]); 54 port_buffer->buffer[i].lossy = 55 MLX5_GET(bufferx_reg, buffer, lossy); 56 port_buffer->buffer[i].epsb = 57 MLX5_GET(bufferx_reg, buffer, epsb); 58 port_buffer->buffer[i].size = 59 MLX5_GET(bufferx_reg, buffer, size) << MLX5E_BUFFER_CELL_SHIFT; 60 port_buffer->buffer[i].xon = 61 MLX5_GET(bufferx_reg, buffer, xon_threshold) << MLX5E_BUFFER_CELL_SHIFT; 62 port_buffer->buffer[i].xoff = 63 MLX5_GET(bufferx_reg, buffer, xoff_threshold) << MLX5E_BUFFER_CELL_SHIFT; 64 total_used += port_buffer->buffer[i].size; 65 66 mlx5e_dbg(HW, priv, "buffer %d: size=%d, xon=%d, xoff=%d, epsb=%d, lossy=%d\n", i, 67 port_buffer->buffer[i].size, 68 port_buffer->buffer[i].xon, 69 port_buffer->buffer[i].xoff, 70 port_buffer->buffer[i].epsb, 71 port_buffer->buffer[i].lossy); 72 } 73 74 port_buffer->port_buffer_size = 75 MLX5_GET(pbmc_reg, out, port_buffer_size) << MLX5E_BUFFER_CELL_SHIFT; 76 port_buffer->spare_buffer_size = 77 port_buffer->port_buffer_size - total_used; 78 79 mlx5e_dbg(HW, priv, "total buffer size=%d, spare buffer size=%d\n", 80 port_buffer->port_buffer_size, 81 port_buffer->spare_buffer_size); 82 out: 83 kfree(out); 84 return err; 85 } 86 87 static int port_set_buffer(struct mlx5e_priv *priv, 88 struct mlx5e_port_buffer *port_buffer) 89 { 90 struct mlx5_core_dev *mdev = priv->mdev; 91 int sz = MLX5_ST_SZ_BYTES(pbmc_reg); 92 void *buffer; 93 void *in; 94 int err; 95 int i; 96 97 in = kzalloc(sz, GFP_KERNEL); 98 if (!in) 99 return -ENOMEM; 100 101 err = mlx5e_port_query_pbmc(mdev, in); 102 if (err) 103 goto out; 104 105 for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 106 buffer = MLX5_ADDR_OF(pbmc_reg, in, buffer[i]); 107 108 MLX5_SET(bufferx_reg, buffer, size, 109 port_buffer->buffer[i].size >> MLX5E_BUFFER_CELL_SHIFT); 110 MLX5_SET(bufferx_reg, buffer, lossy, 111 port_buffer->buffer[i].lossy); 112 MLX5_SET(bufferx_reg, buffer, xoff_threshold, 113 port_buffer->buffer[i].xoff >> MLX5E_BUFFER_CELL_SHIFT); 114 MLX5_SET(bufferx_reg, buffer, xon_threshold, 115 port_buffer->buffer[i].xon >> MLX5E_BUFFER_CELL_SHIFT); 116 } 117 118 err = mlx5e_port_set_pbmc(mdev, in); 119 out: 120 kfree(in); 121 return err; 122 } 123 124 /* xoff = ((301+2.16 * len [m]) * speed [Gbps] + 2.72 MTU [B]) */ 125 static u32 calculate_xoff(struct mlx5e_priv *priv, unsigned int mtu) 126 { 127 u32 speed; 128 u32 xoff; 129 int err; 130 131 err = mlx5e_port_linkspeed(priv->mdev, &speed); 132 if (err) { 133 mlx5_core_warn(priv->mdev, "cannot get port speed\n"); 134 speed = SPEED_40000; 135 } 136 speed = max_t(u32, speed, SPEED_40000); 137 xoff = (301 + 216 * priv->dcbx.cable_len / 100) * speed / 1000 + 272 * mtu / 100; 138 139 mlx5e_dbg(HW, priv, "%s: xoff=%d\n", __func__, xoff); 140 return xoff; 141 } 142 143 static int update_xoff_threshold(struct mlx5e_priv *priv, 144 struct mlx5e_port_buffer *port_buffer, u32 xoff) 145 { 146 int i; 147 148 for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 149 if (port_buffer->buffer[i].lossy) { 150 port_buffer->buffer[i].xoff = 0; 151 port_buffer->buffer[i].xon = 0; 152 continue; 153 } 154 155 if (port_buffer->buffer[i].size < 156 (xoff + MLX5E_MAX_PORT_MTU + (1 << MLX5E_BUFFER_CELL_SHIFT))) { 157 mlx5_en_info(priv->ifp, 158 "non-lossy buffer %d size %d less than xoff threshold %d\n", 159 i, port_buffer->buffer[i].size, 160 xoff + MLX5E_MAX_PORT_MTU + 161 (1 << MLX5E_BUFFER_CELL_SHIFT)); 162 return -ENOMEM; 163 } 164 165 port_buffer->buffer[i].xoff = port_buffer->buffer[i].size - xoff; 166 port_buffer->buffer[i].xon = 167 port_buffer->buffer[i].xoff - MLX5E_MAX_PORT_MTU; 168 } 169 170 return 0; 171 } 172 173 /** 174 * update_buffer_lossy() 175 * mtu: device's MTU 176 * pfc_en: <input> current pfc configuration 177 * buffer: <input> current prio to buffer mapping 178 * xoff: <input> xoff value 179 * port_buffer: <output> port receive buffer configuration 180 * change: <output> 181 * 182 * Update buffer configuration based on pfc configuration and priority 183 * to buffer mapping. 184 * Buffer's lossy bit is changed to: 185 * lossless if there is at least one PFC enabled priority mapped to this buffer 186 * lossy if all priorities mapped to this buffer are PFC disabled 187 * 188 * Return: 189 * Return 0 if no error. 190 * Set change to true if buffer configuration is modified. 191 */ 192 static int update_buffer_lossy(struct mlx5e_priv *priv, unsigned int mtu, 193 u8 pfc_en, u8 *buffer, u32 xoff, 194 struct mlx5e_port_buffer *port_buffer, 195 bool *change) 196 { 197 bool changed = false; 198 u8 lossy_count; 199 u8 prio_count; 200 u8 lossy; 201 int prio; 202 int err; 203 int i; 204 205 for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 206 prio_count = 0; 207 lossy_count = 0; 208 209 for (prio = 0; prio < MLX5E_MAX_PRIORITY; prio++) { 210 if (buffer[prio] != i) 211 continue; 212 213 prio_count++; 214 lossy_count += !(pfc_en & (1 << prio)); 215 } 216 217 if (lossy_count == prio_count) 218 lossy = 1; 219 else /* lossy_count < prio_count */ 220 lossy = 0; 221 222 if (lossy != port_buffer->buffer[i].lossy) { 223 port_buffer->buffer[i].lossy = lossy; 224 changed = true; 225 } 226 } 227 228 if (changed) { 229 err = update_xoff_threshold(priv, port_buffer, xoff); 230 if (err) 231 return err; 232 233 *change = true; 234 } 235 236 return 0; 237 } 238 239 int mlx5e_port_manual_buffer_config(struct mlx5e_priv *priv, 240 u32 change, unsigned int mtu, 241 struct ieee_pfc *pfc, 242 u32 *buffer_size, 243 u8 *prio2buffer) 244 { 245 struct mlx5e_port_buffer port_buffer; 246 u32 xoff = calculate_xoff(priv, mtu); 247 bool update_prio2buffer = false; 248 u8 buffer[MLX5E_MAX_PRIORITY]; 249 bool update_buffer = false; 250 u32 total_used = 0; 251 u8 curr_pfc_en; 252 int err; 253 int i; 254 255 mlx5e_dbg(HW, priv, "%s: change=%x\n", __func__, change); 256 257 err = mlx5e_port_query_buffer(priv, &port_buffer); 258 if (err) 259 return err; 260 261 if (change & MLX5E_PORT_BUFFER_CABLE_LEN) { 262 update_buffer = true; 263 err = update_xoff_threshold(priv, &port_buffer, xoff); 264 if (err) 265 return err; 266 } 267 268 if (change & MLX5E_PORT_BUFFER_PFC) { 269 err = mlx5e_port_query_priority2buffer(priv->mdev, buffer); 270 if (err) 271 return err; 272 273 priv->sw_is_port_buf_owner = true; 274 err = update_buffer_lossy(priv, mtu, pfc->pfc_en, buffer, xoff, 275 &port_buffer, &update_buffer); 276 if (err) 277 return err; 278 } 279 280 if (change & MLX5E_PORT_BUFFER_PRIO2BUFFER) { 281 update_prio2buffer = true; 282 err = mlx5_query_port_pfc(priv->mdev, &curr_pfc_en, NULL); 283 if (err) 284 return err; 285 286 err = update_buffer_lossy(priv, mtu, curr_pfc_en, prio2buffer, xoff, 287 &port_buffer, &update_buffer); 288 if (err) 289 return err; 290 } 291 292 if (change & MLX5E_PORT_BUFFER_SIZE) { 293 for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 294 mlx5e_dbg(HW, priv, "%s: buffer[%d]=%d\n", __func__, i, buffer_size[i]); 295 if (!port_buffer.buffer[i].lossy && !buffer_size[i]) { 296 mlx5e_dbg(HW, priv, "%s: lossless buffer[%d] size cannot be zero\n", 297 __func__, i); 298 return -EINVAL; 299 } 300 301 port_buffer.buffer[i].size = buffer_size[i]; 302 total_used += buffer_size[i]; 303 } 304 305 mlx5e_dbg(HW, priv, "%s: total buffer requested=%d\n", __func__, total_used); 306 307 if (total_used > port_buffer.port_buffer_size) 308 return -EINVAL; 309 310 update_buffer = true; 311 err = update_xoff_threshold(priv, &port_buffer, xoff); 312 if (err) 313 return err; 314 } 315 316 /* Need to update buffer configuration if xoff value is changed */ 317 if (!update_buffer && xoff != priv->dcbx.xoff) { 318 update_buffer = true; 319 err = update_xoff_threshold(priv, &port_buffer, xoff); 320 if (err) 321 return err; 322 } 323 priv->dcbx.xoff = xoff; 324 325 /* Apply the settings */ 326 if (update_buffer) { 327 priv->sw_is_port_buf_owner = true; 328 err = port_set_buffer(priv, &port_buffer); 329 if (err) 330 return err; 331 } 332 333 if (update_prio2buffer) { 334 priv->sw_is_port_buf_owner = true; 335 err = mlx5e_port_set_priority2buffer(priv->mdev, prio2buffer); 336 } 337 338 return err; 339 } 340