10696d608SHuy Nguyen /* 20696d608SHuy Nguyen * Copyright (c) 2018, Mellanox Technologies. All rights reserved. 30696d608SHuy Nguyen * 40696d608SHuy Nguyen * This software is available to you under a choice of one of two 50696d608SHuy Nguyen * licenses. You may choose to be licensed under the terms of the GNU 60696d608SHuy Nguyen * General Public License (GPL) Version 2, available from the file 70696d608SHuy Nguyen * COPYING in the main directory of this source tree, or the 80696d608SHuy Nguyen * OpenIB.org BSD license below: 90696d608SHuy Nguyen * 100696d608SHuy Nguyen * Redistribution and use in source and binary forms, with or 110696d608SHuy Nguyen * without modification, are permitted provided that the following 120696d608SHuy Nguyen * conditions are met: 130696d608SHuy Nguyen * 140696d608SHuy Nguyen * - Redistributions of source code must retain the above 150696d608SHuy Nguyen * copyright notice, this list of conditions and the following 160696d608SHuy Nguyen * disclaimer. 170696d608SHuy Nguyen * 180696d608SHuy Nguyen * - Redistributions in binary form must reproduce the above 190696d608SHuy Nguyen * copyright notice, this list of conditions and the following 200696d608SHuy Nguyen * disclaimer in the documentation and/or other materials 210696d608SHuy Nguyen * provided with the distribution. 220696d608SHuy Nguyen * 230696d608SHuy Nguyen * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 240696d608SHuy Nguyen * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 250696d608SHuy Nguyen * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 260696d608SHuy Nguyen * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 270696d608SHuy Nguyen * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 280696d608SHuy Nguyen * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 290696d608SHuy Nguyen * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 300696d608SHuy Nguyen * SOFTWARE. 310696d608SHuy Nguyen */ 320696d608SHuy Nguyen #include "port_buffer.h" 330696d608SHuy Nguyen 340696d608SHuy Nguyen int mlx5e_port_query_buffer(struct mlx5e_priv *priv, 350696d608SHuy Nguyen struct mlx5e_port_buffer *port_buffer) 360696d608SHuy Nguyen { 370696d608SHuy Nguyen struct mlx5_core_dev *mdev = priv->mdev; 380696d608SHuy Nguyen int sz = MLX5_ST_SZ_BYTES(pbmc_reg); 390696d608SHuy Nguyen u32 total_used = 0; 400696d608SHuy Nguyen void *buffer; 410696d608SHuy Nguyen void *out; 420696d608SHuy Nguyen int err; 430696d608SHuy Nguyen int i; 440696d608SHuy Nguyen 450696d608SHuy Nguyen out = kzalloc(sz, GFP_KERNEL); 460696d608SHuy Nguyen if (!out) 470696d608SHuy Nguyen return -ENOMEM; 480696d608SHuy Nguyen 490696d608SHuy Nguyen err = mlx5e_port_query_pbmc(mdev, out); 500696d608SHuy Nguyen if (err) 510696d608SHuy Nguyen goto out; 520696d608SHuy Nguyen 530696d608SHuy Nguyen for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 540696d608SHuy Nguyen buffer = MLX5_ADDR_OF(pbmc_reg, out, buffer[i]); 550696d608SHuy Nguyen port_buffer->buffer[i].lossy = 560696d608SHuy Nguyen MLX5_GET(bufferx_reg, buffer, lossy); 570696d608SHuy Nguyen port_buffer->buffer[i].epsb = 580696d608SHuy Nguyen MLX5_GET(bufferx_reg, buffer, epsb); 590696d608SHuy Nguyen port_buffer->buffer[i].size = 600696d608SHuy Nguyen MLX5_GET(bufferx_reg, buffer, size) << MLX5E_BUFFER_CELL_SHIFT; 610696d608SHuy Nguyen port_buffer->buffer[i].xon = 620696d608SHuy Nguyen MLX5_GET(bufferx_reg, buffer, xon_threshold) << MLX5E_BUFFER_CELL_SHIFT; 630696d608SHuy Nguyen port_buffer->buffer[i].xoff = 640696d608SHuy Nguyen MLX5_GET(bufferx_reg, buffer, xoff_threshold) << MLX5E_BUFFER_CELL_SHIFT; 650696d608SHuy Nguyen total_used += port_buffer->buffer[i].size; 660696d608SHuy Nguyen 670696d608SHuy Nguyen mlx5e_dbg(HW, priv, "buffer %d: size=%d, xon=%d, xoff=%d, epsb=%d, lossy=%d\n", i, 680696d608SHuy Nguyen port_buffer->buffer[i].size, 690696d608SHuy Nguyen port_buffer->buffer[i].xon, 700696d608SHuy Nguyen port_buffer->buffer[i].xoff, 710696d608SHuy Nguyen port_buffer->buffer[i].epsb, 720696d608SHuy Nguyen port_buffer->buffer[i].lossy); 730696d608SHuy Nguyen } 740696d608SHuy Nguyen 750696d608SHuy Nguyen port_buffer->port_buffer_size = 760696d608SHuy Nguyen MLX5_GET(pbmc_reg, out, port_buffer_size) << MLX5E_BUFFER_CELL_SHIFT; 770696d608SHuy Nguyen port_buffer->spare_buffer_size = 780696d608SHuy Nguyen port_buffer->port_buffer_size - total_used; 790696d608SHuy Nguyen 800696d608SHuy Nguyen mlx5e_dbg(HW, priv, "total buffer size=%d, spare buffer size=%d\n", 810696d608SHuy Nguyen port_buffer->port_buffer_size, 820696d608SHuy Nguyen port_buffer->spare_buffer_size); 830696d608SHuy Nguyen out: 840696d608SHuy Nguyen kfree(out); 850696d608SHuy Nguyen return err; 860696d608SHuy Nguyen } 870696d608SHuy Nguyen 880696d608SHuy Nguyen static int port_set_buffer(struct mlx5e_priv *priv, 890696d608SHuy Nguyen struct mlx5e_port_buffer *port_buffer) 900696d608SHuy Nguyen { 910696d608SHuy Nguyen struct mlx5_core_dev *mdev = priv->mdev; 920696d608SHuy Nguyen int sz = MLX5_ST_SZ_BYTES(pbmc_reg); 930696d608SHuy Nguyen void *buffer; 940696d608SHuy Nguyen void *in; 950696d608SHuy Nguyen int err; 960696d608SHuy Nguyen int i; 970696d608SHuy Nguyen 980696d608SHuy Nguyen in = kzalloc(sz, GFP_KERNEL); 990696d608SHuy Nguyen if (!in) 1000696d608SHuy Nguyen return -ENOMEM; 1010696d608SHuy Nguyen 1020696d608SHuy Nguyen err = mlx5e_port_query_pbmc(mdev, in); 1030696d608SHuy Nguyen if (err) 1040696d608SHuy Nguyen goto out; 1050696d608SHuy Nguyen 1060696d608SHuy Nguyen for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 1070696d608SHuy Nguyen buffer = MLX5_ADDR_OF(pbmc_reg, in, buffer[i]); 1080696d608SHuy Nguyen 1090696d608SHuy Nguyen MLX5_SET(bufferx_reg, buffer, size, 1100696d608SHuy Nguyen port_buffer->buffer[i].size >> MLX5E_BUFFER_CELL_SHIFT); 1110696d608SHuy Nguyen MLX5_SET(bufferx_reg, buffer, lossy, 1120696d608SHuy Nguyen port_buffer->buffer[i].lossy); 1130696d608SHuy Nguyen MLX5_SET(bufferx_reg, buffer, xoff_threshold, 1140696d608SHuy Nguyen port_buffer->buffer[i].xoff >> MLX5E_BUFFER_CELL_SHIFT); 1150696d608SHuy Nguyen MLX5_SET(bufferx_reg, buffer, xon_threshold, 1160696d608SHuy Nguyen port_buffer->buffer[i].xon >> MLX5E_BUFFER_CELL_SHIFT); 1170696d608SHuy Nguyen } 1180696d608SHuy Nguyen 1190696d608SHuy Nguyen err = mlx5e_port_set_pbmc(mdev, in); 1200696d608SHuy Nguyen out: 1210696d608SHuy Nguyen kfree(in); 1220696d608SHuy Nguyen return err; 1230696d608SHuy Nguyen } 1240696d608SHuy Nguyen 1255ec983e9SHuy Nguyen /* xoff = ((301+2.16 * len [m]) * speed [Gbps] + 2.72 MTU [B]) 1265ec983e9SHuy Nguyen * minimum speed value is 40Gbps 1275ec983e9SHuy Nguyen */ 1280696d608SHuy Nguyen static u32 calculate_xoff(struct mlx5e_priv *priv, unsigned int mtu) 1290696d608SHuy Nguyen { 1300696d608SHuy Nguyen u32 speed; 1310696d608SHuy Nguyen u32 xoff; 1320696d608SHuy Nguyen int err; 1330696d608SHuy Nguyen 1340696d608SHuy Nguyen err = mlx5e_port_linkspeed(priv->mdev, &speed); 1355ec983e9SHuy Nguyen if (err) 1365ec983e9SHuy Nguyen speed = SPEED_40000; 1375ec983e9SHuy Nguyen speed = max_t(u32, speed, SPEED_40000); 1380696d608SHuy Nguyen 1390696d608SHuy Nguyen xoff = (301 + 216 * priv->dcbx.cable_len / 100) * speed / 1000 + 272 * mtu / 100; 1400696d608SHuy Nguyen 1410696d608SHuy Nguyen mlx5e_dbg(HW, priv, "%s: xoff=%d\n", __func__, xoff); 1420696d608SHuy Nguyen return xoff; 1430696d608SHuy Nguyen } 1440696d608SHuy Nguyen 1450696d608SHuy Nguyen static int update_xoff_threshold(struct mlx5e_port_buffer *port_buffer, 146e28408e9SHuy Nguyen u32 xoff, unsigned int max_mtu) 1470696d608SHuy Nguyen { 1480696d608SHuy Nguyen int i; 1490696d608SHuy Nguyen 1500696d608SHuy Nguyen for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 1510696d608SHuy Nguyen if (port_buffer->buffer[i].lossy) { 1520696d608SHuy Nguyen port_buffer->buffer[i].xoff = 0; 1530696d608SHuy Nguyen port_buffer->buffer[i].xon = 0; 1540696d608SHuy Nguyen continue; 1550696d608SHuy Nguyen } 1560696d608SHuy Nguyen 1570696d608SHuy Nguyen if (port_buffer->buffer[i].size < 158e28408e9SHuy Nguyen (xoff + max_mtu + (1 << MLX5E_BUFFER_CELL_SHIFT))) 1590696d608SHuy Nguyen return -ENOMEM; 1600696d608SHuy Nguyen 1610696d608SHuy Nguyen port_buffer->buffer[i].xoff = port_buffer->buffer[i].size - xoff; 162e28408e9SHuy Nguyen port_buffer->buffer[i].xon = 163e28408e9SHuy Nguyen port_buffer->buffer[i].xoff - max_mtu; 1640696d608SHuy Nguyen } 1650696d608SHuy Nguyen 1660696d608SHuy Nguyen return 0; 1670696d608SHuy Nguyen } 1680696d608SHuy Nguyen 1690696d608SHuy Nguyen /** 1700696d608SHuy Nguyen * update_buffer_lossy() 171e28408e9SHuy Nguyen * max_mtu: netdev's max_mtu 1720696d608SHuy Nguyen * pfc_en: <input> current pfc configuration 1730696d608SHuy Nguyen * buffer: <input> current prio to buffer mapping 1740696d608SHuy Nguyen * xoff: <input> xoff value 1750696d608SHuy Nguyen * port_buffer: <output> port receive buffer configuration 1760696d608SHuy Nguyen * change: <output> 1770696d608SHuy Nguyen * 1780696d608SHuy Nguyen * Update buffer configuration based on pfc configuraiton and priority 1790696d608SHuy Nguyen * to buffer mapping. 1800696d608SHuy Nguyen * Buffer's lossy bit is changed to: 1810696d608SHuy Nguyen * lossless if there is at least one PFC enabled priority mapped to this buffer 1820696d608SHuy Nguyen * lossy if all priorities mapped to this buffer are PFC disabled 1830696d608SHuy Nguyen * 1840696d608SHuy Nguyen * Return: 1850696d608SHuy Nguyen * Return 0 if no error. 1860696d608SHuy Nguyen * Set change to true if buffer configuration is modified. 1870696d608SHuy Nguyen */ 188e28408e9SHuy Nguyen static int update_buffer_lossy(unsigned int max_mtu, 1890696d608SHuy Nguyen u8 pfc_en, u8 *buffer, u32 xoff, 1900696d608SHuy Nguyen struct mlx5e_port_buffer *port_buffer, 1910696d608SHuy Nguyen bool *change) 1920696d608SHuy Nguyen { 1930696d608SHuy Nguyen bool changed = false; 1940696d608SHuy Nguyen u8 lossy_count; 1950696d608SHuy Nguyen u8 prio_count; 1960696d608SHuy Nguyen u8 lossy; 1970696d608SHuy Nguyen int prio; 1980696d608SHuy Nguyen int err; 1990696d608SHuy Nguyen int i; 2000696d608SHuy Nguyen 2010696d608SHuy Nguyen for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 2020696d608SHuy Nguyen prio_count = 0; 2030696d608SHuy Nguyen lossy_count = 0; 2040696d608SHuy Nguyen 2050696d608SHuy Nguyen for (prio = 0; prio < MLX5E_MAX_PRIORITY; prio++) { 2060696d608SHuy Nguyen if (buffer[prio] != i) 2070696d608SHuy Nguyen continue; 2080696d608SHuy Nguyen 2090696d608SHuy Nguyen prio_count++; 2100696d608SHuy Nguyen lossy_count += !(pfc_en & (1 << prio)); 2110696d608SHuy Nguyen } 2120696d608SHuy Nguyen 2130696d608SHuy Nguyen if (lossy_count == prio_count) 2140696d608SHuy Nguyen lossy = 1; 2150696d608SHuy Nguyen else /* lossy_count < prio_count */ 2160696d608SHuy Nguyen lossy = 0; 2170696d608SHuy Nguyen 2180696d608SHuy Nguyen if (lossy != port_buffer->buffer[i].lossy) { 2190696d608SHuy Nguyen port_buffer->buffer[i].lossy = lossy; 2200696d608SHuy Nguyen changed = true; 2210696d608SHuy Nguyen } 2220696d608SHuy Nguyen } 2230696d608SHuy Nguyen 2240696d608SHuy Nguyen if (changed) { 225e28408e9SHuy Nguyen err = update_xoff_threshold(port_buffer, xoff, max_mtu); 2260696d608SHuy Nguyen if (err) 2270696d608SHuy Nguyen return err; 2280696d608SHuy Nguyen 2290696d608SHuy Nguyen *change = true; 2300696d608SHuy Nguyen } 2310696d608SHuy Nguyen 2320696d608SHuy Nguyen return 0; 2330696d608SHuy Nguyen } 2340696d608SHuy Nguyen 235e28408e9SHuy Nguyen #define MINIMUM_MAX_MTU 9216 2360696d608SHuy Nguyen int mlx5e_port_manual_buffer_config(struct mlx5e_priv *priv, 2370696d608SHuy Nguyen u32 change, unsigned int mtu, 2380696d608SHuy Nguyen struct ieee_pfc *pfc, 2390696d608SHuy Nguyen u32 *buffer_size, 2400696d608SHuy Nguyen u8 *prio2buffer) 2410696d608SHuy Nguyen { 2420696d608SHuy Nguyen struct mlx5e_port_buffer port_buffer; 2430696d608SHuy Nguyen u32 xoff = calculate_xoff(priv, mtu); 2440696d608SHuy Nguyen bool update_prio2buffer = false; 2450696d608SHuy Nguyen u8 buffer[MLX5E_MAX_PRIORITY]; 2460696d608SHuy Nguyen bool update_buffer = false; 247e28408e9SHuy Nguyen unsigned int max_mtu; 2480696d608SHuy Nguyen u32 total_used = 0; 2490696d608SHuy Nguyen u8 curr_pfc_en; 2500696d608SHuy Nguyen int err; 2510696d608SHuy Nguyen int i; 2520696d608SHuy Nguyen 2530696d608SHuy Nguyen mlx5e_dbg(HW, priv, "%s: change=%x\n", __func__, change); 254e28408e9SHuy Nguyen max_mtu = max_t(unsigned int, priv->netdev->max_mtu, MINIMUM_MAX_MTU); 2550696d608SHuy Nguyen 2560696d608SHuy Nguyen err = mlx5e_port_query_buffer(priv, &port_buffer); 2570696d608SHuy Nguyen if (err) 2580696d608SHuy Nguyen return err; 2590696d608SHuy Nguyen 2600696d608SHuy Nguyen if (change & MLX5E_PORT_BUFFER_CABLE_LEN) { 2610696d608SHuy Nguyen update_buffer = true; 262e28408e9SHuy Nguyen err = update_xoff_threshold(&port_buffer, xoff, max_mtu); 2630696d608SHuy Nguyen if (err) 2640696d608SHuy Nguyen return err; 2650696d608SHuy Nguyen } 2660696d608SHuy Nguyen 2670696d608SHuy Nguyen if (change & MLX5E_PORT_BUFFER_PFC) { 2680696d608SHuy Nguyen err = mlx5e_port_query_priority2buffer(priv->mdev, buffer); 2690696d608SHuy Nguyen if (err) 2700696d608SHuy Nguyen return err; 2710696d608SHuy Nguyen 272e28408e9SHuy Nguyen err = update_buffer_lossy(max_mtu, pfc->pfc_en, buffer, xoff, 2730696d608SHuy Nguyen &port_buffer, &update_buffer); 2740696d608SHuy Nguyen if (err) 2750696d608SHuy Nguyen return err; 2760696d608SHuy Nguyen } 2770696d608SHuy Nguyen 2780696d608SHuy Nguyen if (change & MLX5E_PORT_BUFFER_PRIO2BUFFER) { 2790696d608SHuy Nguyen update_prio2buffer = true; 2800696d608SHuy Nguyen err = mlx5_query_port_pfc(priv->mdev, &curr_pfc_en, NULL); 2810696d608SHuy Nguyen if (err) 2820696d608SHuy Nguyen return err; 2830696d608SHuy Nguyen 284e28408e9SHuy Nguyen err = update_buffer_lossy(max_mtu, curr_pfc_en, prio2buffer, 285e28408e9SHuy Nguyen xoff, &port_buffer, &update_buffer); 2860696d608SHuy Nguyen if (err) 2870696d608SHuy Nguyen return err; 2880696d608SHuy Nguyen } 2890696d608SHuy Nguyen 2900696d608SHuy Nguyen if (change & MLX5E_PORT_BUFFER_SIZE) { 2910696d608SHuy Nguyen for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 2920696d608SHuy Nguyen mlx5e_dbg(HW, priv, "%s: buffer[%d]=%d\n", __func__, i, buffer_size[i]); 2930696d608SHuy Nguyen if (!port_buffer.buffer[i].lossy && !buffer_size[i]) { 2940696d608SHuy Nguyen mlx5e_dbg(HW, priv, "%s: lossless buffer[%d] size cannot be zero\n", 2950696d608SHuy Nguyen __func__, i); 2960696d608SHuy Nguyen return -EINVAL; 2970696d608SHuy Nguyen } 2980696d608SHuy Nguyen 2990696d608SHuy Nguyen port_buffer.buffer[i].size = buffer_size[i]; 3000696d608SHuy Nguyen total_used += buffer_size[i]; 3010696d608SHuy Nguyen } 3020696d608SHuy Nguyen 3030696d608SHuy Nguyen mlx5e_dbg(HW, priv, "%s: total buffer requested=%d\n", __func__, total_used); 3040696d608SHuy Nguyen 3050696d608SHuy Nguyen if (total_used > port_buffer.port_buffer_size) 3060696d608SHuy Nguyen return -EINVAL; 3070696d608SHuy Nguyen 3080696d608SHuy Nguyen update_buffer = true; 309e28408e9SHuy Nguyen err = update_xoff_threshold(&port_buffer, xoff, max_mtu); 3100696d608SHuy Nguyen if (err) 3110696d608SHuy Nguyen return err; 3120696d608SHuy Nguyen } 3130696d608SHuy Nguyen 3140696d608SHuy Nguyen /* Need to update buffer configuration if xoff value is changed */ 3150696d608SHuy Nguyen if (!update_buffer && xoff != priv->dcbx.xoff) { 3160696d608SHuy Nguyen update_buffer = true; 317e28408e9SHuy Nguyen err = update_xoff_threshold(&port_buffer, xoff, max_mtu); 3180696d608SHuy Nguyen if (err) 3190696d608SHuy Nguyen return err; 3200696d608SHuy Nguyen } 3210696d608SHuy Nguyen priv->dcbx.xoff = xoff; 3220696d608SHuy Nguyen 3230696d608SHuy Nguyen /* Apply the settings */ 3240696d608SHuy Nguyen if (update_buffer) { 3250696d608SHuy Nguyen err = port_set_buffer(priv, &port_buffer); 3260696d608SHuy Nguyen if (err) 3270696d608SHuy Nguyen return err; 3280696d608SHuy Nguyen } 3290696d608SHuy Nguyen 3300696d608SHuy Nguyen if (update_prio2buffer) 3310696d608SHuy Nguyen err = mlx5e_port_set_priority2buffer(priv->mdev, prio2buffer); 3320696d608SHuy Nguyen 3330696d608SHuy Nguyen return err; 3340696d608SHuy Nguyen } 335