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