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 1250696d608SHuy Nguyen /* xoff = ((301+2.16 * len [m]) * speed [Gbps] + 2.72 MTU [B]) */ 1260696d608SHuy Nguyen static u32 calculate_xoff(struct mlx5e_priv *priv, unsigned int mtu) 1270696d608SHuy Nguyen { 1280696d608SHuy Nguyen u32 speed; 1290696d608SHuy Nguyen u32 xoff; 1300696d608SHuy Nguyen int err; 1310696d608SHuy Nguyen 1320696d608SHuy Nguyen err = mlx5e_port_linkspeed(priv->mdev, &speed); 13364e28334SShay Agroskin if (err) { 13464e28334SShay Agroskin mlx5_core_warn(priv->mdev, "cannot get port speed\n"); 1350696d608SHuy Nguyen return 0; 13664e28334SShay Agroskin } 1370696d608SHuy Nguyen 1380696d608SHuy Nguyen xoff = (301 + 216 * priv->dcbx.cable_len / 100) * speed / 1000 + 272 * mtu / 100; 1390696d608SHuy Nguyen 1400696d608SHuy Nguyen mlx5e_dbg(HW, priv, "%s: xoff=%d\n", __func__, xoff); 1410696d608SHuy Nguyen return xoff; 1420696d608SHuy Nguyen } 1430696d608SHuy Nguyen 1440696d608SHuy Nguyen static int update_xoff_threshold(struct mlx5e_port_buffer *port_buffer, 1450696d608SHuy Nguyen u32 xoff, unsigned int mtu) 1460696d608SHuy Nguyen { 1470696d608SHuy Nguyen int i; 1480696d608SHuy Nguyen 1490696d608SHuy Nguyen for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 1500696d608SHuy Nguyen if (port_buffer->buffer[i].lossy) { 1510696d608SHuy Nguyen port_buffer->buffer[i].xoff = 0; 1520696d608SHuy Nguyen port_buffer->buffer[i].xon = 0; 1530696d608SHuy Nguyen continue; 1540696d608SHuy Nguyen } 1550696d608SHuy Nguyen 1560696d608SHuy Nguyen if (port_buffer->buffer[i].size < 1570696d608SHuy Nguyen (xoff + mtu + (1 << MLX5E_BUFFER_CELL_SHIFT))) 1580696d608SHuy Nguyen return -ENOMEM; 1590696d608SHuy Nguyen 1600696d608SHuy Nguyen port_buffer->buffer[i].xoff = port_buffer->buffer[i].size - xoff; 1610696d608SHuy Nguyen port_buffer->buffer[i].xon = port_buffer->buffer[i].xoff - mtu; 1620696d608SHuy Nguyen } 1630696d608SHuy Nguyen 1640696d608SHuy Nguyen return 0; 1650696d608SHuy Nguyen } 1660696d608SHuy Nguyen 1670696d608SHuy Nguyen /** 168d3669ca9SSaeed Mahameed * update_buffer_lossy - Update buffer configuration based on pfc 169d3669ca9SSaeed Mahameed * @mtu: device's MTU 170d3669ca9SSaeed Mahameed * @pfc_en: <input> current pfc configuration 171d3669ca9SSaeed Mahameed * @buffer: <input> current prio to buffer mapping 172d3669ca9SSaeed Mahameed * @xoff: <input> xoff value 173d3669ca9SSaeed Mahameed * @port_buffer: <output> port receive buffer configuration 174d3669ca9SSaeed Mahameed * @change: <output> 1750696d608SHuy Nguyen * 176d3669ca9SSaeed Mahameed * Update buffer configuration based on pfc configuraiton and 177d3669ca9SSaeed Mahameed * priority to buffer mapping. 1780696d608SHuy Nguyen * Buffer's lossy bit is changed to: 179d3669ca9SSaeed Mahameed * lossless if there is at least one PFC enabled priority 180d3669ca9SSaeed Mahameed * mapped to this buffer lossy if all priorities mapped to 181d3669ca9SSaeed Mahameed * this buffer are PFC disabled 1820696d608SHuy Nguyen * 183d3669ca9SSaeed Mahameed * @return: 0 if no error, 184d3669ca9SSaeed Mahameed * sets change to true if buffer configuration was modified. 1850696d608SHuy Nguyen */ 1860696d608SHuy Nguyen static int update_buffer_lossy(unsigned int mtu, 1870696d608SHuy Nguyen u8 pfc_en, u8 *buffer, u32 xoff, 1880696d608SHuy Nguyen struct mlx5e_port_buffer *port_buffer, 1890696d608SHuy Nguyen bool *change) 1900696d608SHuy Nguyen { 1910696d608SHuy Nguyen bool changed = false; 1920696d608SHuy Nguyen u8 lossy_count; 1930696d608SHuy Nguyen u8 prio_count; 1940696d608SHuy Nguyen u8 lossy; 1950696d608SHuy Nguyen int prio; 1960696d608SHuy Nguyen int err; 1970696d608SHuy Nguyen int i; 1980696d608SHuy Nguyen 1990696d608SHuy Nguyen for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 2000696d608SHuy Nguyen prio_count = 0; 2010696d608SHuy Nguyen lossy_count = 0; 2020696d608SHuy Nguyen 2030696d608SHuy Nguyen for (prio = 0; prio < MLX5E_MAX_PRIORITY; prio++) { 2040696d608SHuy Nguyen if (buffer[prio] != i) 2050696d608SHuy Nguyen continue; 2060696d608SHuy Nguyen 2070696d608SHuy Nguyen prio_count++; 2080696d608SHuy Nguyen lossy_count += !(pfc_en & (1 << prio)); 2090696d608SHuy Nguyen } 2100696d608SHuy Nguyen 2110696d608SHuy Nguyen if (lossy_count == prio_count) 2120696d608SHuy Nguyen lossy = 1; 2130696d608SHuy Nguyen else /* lossy_count < prio_count */ 2140696d608SHuy Nguyen lossy = 0; 2150696d608SHuy Nguyen 2160696d608SHuy Nguyen if (lossy != port_buffer->buffer[i].lossy) { 2170696d608SHuy Nguyen port_buffer->buffer[i].lossy = lossy; 2180696d608SHuy Nguyen changed = true; 2190696d608SHuy Nguyen } 2200696d608SHuy Nguyen } 2210696d608SHuy Nguyen 2220696d608SHuy Nguyen if (changed) { 2230696d608SHuy Nguyen err = update_xoff_threshold(port_buffer, xoff, mtu); 2240696d608SHuy Nguyen if (err) 2250696d608SHuy Nguyen return err; 2260696d608SHuy Nguyen 2270696d608SHuy Nguyen *change = true; 2280696d608SHuy Nguyen } 2290696d608SHuy Nguyen 2300696d608SHuy Nguyen return 0; 2310696d608SHuy Nguyen } 2320696d608SHuy Nguyen 2330696d608SHuy Nguyen int mlx5e_port_manual_buffer_config(struct mlx5e_priv *priv, 2340696d608SHuy Nguyen u32 change, unsigned int mtu, 2350696d608SHuy Nguyen struct ieee_pfc *pfc, 2360696d608SHuy Nguyen u32 *buffer_size, 2370696d608SHuy Nguyen u8 *prio2buffer) 2380696d608SHuy Nguyen { 2390696d608SHuy Nguyen struct mlx5e_port_buffer port_buffer; 2400696d608SHuy Nguyen u32 xoff = calculate_xoff(priv, mtu); 2410696d608SHuy Nguyen bool update_prio2buffer = false; 2420696d608SHuy Nguyen u8 buffer[MLX5E_MAX_PRIORITY]; 2430696d608SHuy Nguyen bool update_buffer = false; 2440696d608SHuy Nguyen u32 total_used = 0; 2450696d608SHuy Nguyen u8 curr_pfc_en; 2460696d608SHuy Nguyen int err; 2470696d608SHuy Nguyen int i; 2480696d608SHuy Nguyen 2490696d608SHuy Nguyen mlx5e_dbg(HW, priv, "%s: change=%x\n", __func__, change); 2500696d608SHuy Nguyen 2510696d608SHuy Nguyen err = mlx5e_port_query_buffer(priv, &port_buffer); 2520696d608SHuy Nguyen if (err) 2530696d608SHuy Nguyen return err; 2540696d608SHuy Nguyen 2550696d608SHuy Nguyen if (change & MLX5E_PORT_BUFFER_CABLE_LEN) { 2560696d608SHuy Nguyen update_buffer = true; 2570696d608SHuy Nguyen err = update_xoff_threshold(&port_buffer, xoff, mtu); 2580696d608SHuy Nguyen if (err) 2590696d608SHuy Nguyen return err; 2600696d608SHuy Nguyen } 2610696d608SHuy Nguyen 2620696d608SHuy Nguyen if (change & MLX5E_PORT_BUFFER_PFC) { 2630696d608SHuy Nguyen err = mlx5e_port_query_priority2buffer(priv->mdev, buffer); 2640696d608SHuy Nguyen if (err) 2650696d608SHuy Nguyen return err; 2660696d608SHuy Nguyen 2670696d608SHuy Nguyen err = update_buffer_lossy(mtu, pfc->pfc_en, buffer, xoff, 2680696d608SHuy Nguyen &port_buffer, &update_buffer); 2690696d608SHuy Nguyen if (err) 2700696d608SHuy Nguyen return err; 2710696d608SHuy Nguyen } 2720696d608SHuy Nguyen 2730696d608SHuy Nguyen if (change & MLX5E_PORT_BUFFER_PRIO2BUFFER) { 2740696d608SHuy Nguyen update_prio2buffer = true; 2750696d608SHuy Nguyen err = mlx5_query_port_pfc(priv->mdev, &curr_pfc_en, NULL); 2760696d608SHuy Nguyen if (err) 2770696d608SHuy Nguyen return err; 2780696d608SHuy Nguyen 2790696d608SHuy Nguyen err = update_buffer_lossy(mtu, curr_pfc_en, prio2buffer, xoff, 2800696d608SHuy Nguyen &port_buffer, &update_buffer); 2810696d608SHuy Nguyen if (err) 2820696d608SHuy Nguyen return err; 2830696d608SHuy Nguyen } 2840696d608SHuy Nguyen 2850696d608SHuy Nguyen if (change & MLX5E_PORT_BUFFER_SIZE) { 2860696d608SHuy Nguyen for (i = 0; i < MLX5E_MAX_BUFFER; i++) { 2870696d608SHuy Nguyen mlx5e_dbg(HW, priv, "%s: buffer[%d]=%d\n", __func__, i, buffer_size[i]); 2880696d608SHuy Nguyen if (!port_buffer.buffer[i].lossy && !buffer_size[i]) { 2890696d608SHuy Nguyen mlx5e_dbg(HW, priv, "%s: lossless buffer[%d] size cannot be zero\n", 2900696d608SHuy Nguyen __func__, i); 2910696d608SHuy Nguyen return -EINVAL; 2920696d608SHuy Nguyen } 2930696d608SHuy Nguyen 2940696d608SHuy Nguyen port_buffer.buffer[i].size = buffer_size[i]; 2950696d608SHuy Nguyen total_used += buffer_size[i]; 2960696d608SHuy Nguyen } 2970696d608SHuy Nguyen 2980696d608SHuy Nguyen mlx5e_dbg(HW, priv, "%s: total buffer requested=%d\n", __func__, total_used); 2990696d608SHuy Nguyen 3000696d608SHuy Nguyen if (total_used > port_buffer.port_buffer_size) 3010696d608SHuy Nguyen return -EINVAL; 3020696d608SHuy Nguyen 3030696d608SHuy Nguyen update_buffer = true; 3040696d608SHuy Nguyen err = update_xoff_threshold(&port_buffer, xoff, mtu); 3050696d608SHuy Nguyen if (err) 3060696d608SHuy Nguyen return err; 3070696d608SHuy Nguyen } 3080696d608SHuy Nguyen 3090696d608SHuy Nguyen /* Need to update buffer configuration if xoff value is changed */ 3100696d608SHuy Nguyen if (!update_buffer && xoff != priv->dcbx.xoff) { 3110696d608SHuy Nguyen update_buffer = true; 3120696d608SHuy Nguyen err = update_xoff_threshold(&port_buffer, xoff, mtu); 3130696d608SHuy Nguyen if (err) 3140696d608SHuy Nguyen return err; 3150696d608SHuy Nguyen } 3160696d608SHuy Nguyen priv->dcbx.xoff = xoff; 3170696d608SHuy Nguyen 3180696d608SHuy Nguyen /* Apply the settings */ 3190696d608SHuy Nguyen if (update_buffer) { 3200696d608SHuy Nguyen err = port_set_buffer(priv, &port_buffer); 3210696d608SHuy Nguyen if (err) 3220696d608SHuy Nguyen return err; 3230696d608SHuy Nguyen } 3240696d608SHuy Nguyen 3250696d608SHuy Nguyen if (update_prio2buffer) 3260696d608SHuy Nguyen err = mlx5e_port_set_priority2buffer(priv->mdev, prio2buffer); 3270696d608SHuy Nguyen 3280696d608SHuy Nguyen return err; 3290696d608SHuy Nguyen } 330