mlx5_en_port_buffer.c revision 353262
1/*- 2 * Copyright (c) 2018 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 353262 2019-10-07 10:28:51Z 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_port_buffer *port_buffer, 143 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 return -ENOMEM; 157 158 port_buffer->buffer[i].xoff = port_buffer->buffer[i].size - xoff; 159 port_buffer->buffer[i].xon = 160 port_buffer->buffer[i].xoff - MLX5E_MAX_PORT_MTU; 161 } 162 163 return 0; 164} 165 166/** 167 * update_buffer_lossy() 168 * mtu: device's MTU 169 * pfc_en: <input> current pfc configuration 170 * buffer: <input> current prio to buffer mapping 171 * xoff: <input> xoff value 172 * port_buffer: <output> port receive buffer configuration 173 * change: <output> 174 * 175 * Update buffer configuration based on pfc configuraiton and priority 176 * to buffer mapping. 177 * Buffer's lossy bit is changed to: 178 * lossless if there is at least one PFC enabled priority mapped to this buffer 179 * lossy if all priorities mapped to this buffer are PFC disabled 180 * 181 * Return: 182 * Return 0 if no error. 183 * Set change to true if buffer configuration is modified. 184 */ 185static int update_buffer_lossy(unsigned int mtu, 186 u8 pfc_en, u8 *buffer, u32 xoff, 187 struct mlx5e_port_buffer *port_buffer, 188 bool *change) 189{ 190 bool changed = false; 191 u8 lossy_count; 192 u8 prio_count; 193 u8 lossy; 194 int prio; 195 int err; 196 int i; 197 198 for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 199 prio_count = 0; 200 lossy_count = 0; 201 202 for (prio = 0; prio < MLX5E_MAX_PRIORITY; prio++) { 203 if (buffer[prio] != i) 204 continue; 205 206 prio_count++; 207 lossy_count += !(pfc_en & (1 << prio)); 208 } 209 210 if (lossy_count == prio_count) 211 lossy = 1; 212 else /* lossy_count < prio_count */ 213 lossy = 0; 214 215 if (lossy != port_buffer->buffer[i].lossy) { 216 port_buffer->buffer[i].lossy = lossy; 217 changed = true; 218 } 219 } 220 221 if (changed) { 222 err = update_xoff_threshold(port_buffer, xoff); 223 if (err) 224 return err; 225 226 *change = true; 227 } 228 229 return 0; 230} 231 232int mlx5e_port_manual_buffer_config(struct mlx5e_priv *priv, 233 u32 change, unsigned int mtu, 234 struct ieee_pfc *pfc, 235 u32 *buffer_size, 236 u8 *prio2buffer) 237{ 238 struct mlx5e_port_buffer port_buffer; 239 u32 xoff = calculate_xoff(priv, mtu); 240 bool update_prio2buffer = false; 241 u8 buffer[MLX5E_MAX_PRIORITY]; 242 bool update_buffer = false; 243 u32 total_used = 0; 244 u8 curr_pfc_en; 245 int err; 246 int i; 247 248 mlx5e_dbg(HW, priv, "%s: change=%x\n", __func__, change); 249 250 err = mlx5e_port_query_buffer(priv, &port_buffer); 251 if (err) 252 return err; 253 254 if (change & MLX5E_PORT_BUFFER_CABLE_LEN) { 255 update_buffer = true; 256 err = update_xoff_threshold(&port_buffer, xoff); 257 if (err) 258 return err; 259 } 260 261 if (change & MLX5E_PORT_BUFFER_PFC) { 262 err = mlx5e_port_query_priority2buffer(priv->mdev, buffer); 263 if (err) 264 return err; 265 266 priv->sw_is_port_buf_owner = true; 267 err = update_buffer_lossy(mtu, pfc->pfc_en, buffer, xoff, 268 &port_buffer, &update_buffer); 269 if (err) 270 return err; 271 } 272 273 if (change & MLX5E_PORT_BUFFER_PRIO2BUFFER) { 274 update_prio2buffer = true; 275 err = mlx5_query_port_pfc(priv->mdev, &curr_pfc_en, NULL); 276 if (err) 277 return err; 278 279 err = update_buffer_lossy(mtu, curr_pfc_en, prio2buffer, xoff, 280 &port_buffer, &update_buffer); 281 if (err) 282 return err; 283 } 284 285 if (change & MLX5E_PORT_BUFFER_SIZE) { 286 for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 287 mlx5e_dbg(HW, priv, "%s: buffer[%d]=%d\n", __func__, i, buffer_size[i]); 288 if (!port_buffer.buffer[i].lossy && !buffer_size[i]) { 289 mlx5e_dbg(HW, priv, "%s: lossless buffer[%d] size cannot be zero\n", 290 __func__, i); 291 return -EINVAL; 292 } 293 294 port_buffer.buffer[i].size = buffer_size[i]; 295 total_used += buffer_size[i]; 296 } 297 298 mlx5e_dbg(HW, priv, "%s: total buffer requested=%d\n", __func__, total_used); 299 300 if (total_used > port_buffer.port_buffer_size) 301 return -EINVAL; 302 303 update_buffer = true; 304 err = update_xoff_threshold(&port_buffer, xoff); 305 if (err) 306 return err; 307 } 308 309 /* Need to update buffer configuration if xoff value is changed */ 310 if (!update_buffer && xoff != priv->dcbx.xoff) { 311 update_buffer = true; 312 err = update_xoff_threshold(&port_buffer, xoff); 313 if (err) 314 return err; 315 } 316 priv->dcbx.xoff = xoff; 317 318 /* Apply the settings */ 319 if (update_buffer) { 320 priv->sw_is_port_buf_owner = true; 321 err = port_set_buffer(priv, &port_buffer); 322 if (err) 323 return err; 324 } 325 326 if (update_prio2buffer) { 327 priv->sw_is_port_buf_owner = true; 328 err = mlx5e_port_set_priority2buffer(priv->mdev, prio2buffer); 329 } 330 331 return err; 332} 333