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