138535d6cSHans Petter Selasky /*- 238535d6cSHans Petter Selasky * Copyright (c) 2013-2017, Mellanox Technologies, Ltd. All rights reserved. 338535d6cSHans Petter Selasky * 438535d6cSHans Petter Selasky * Redistribution and use in source and binary forms, with or without 538535d6cSHans Petter Selasky * modification, are permitted provided that the following conditions 638535d6cSHans Petter Selasky * are met: 738535d6cSHans Petter Selasky * 1. Redistributions of source code must retain the above copyright 838535d6cSHans Petter Selasky * notice, this list of conditions and the following disclaimer. 938535d6cSHans Petter Selasky * 2. Redistributions in binary form must reproduce the above copyright 1038535d6cSHans Petter Selasky * notice, this list of conditions and the following disclaimer in the 1138535d6cSHans Petter Selasky * documentation and/or other materials provided with the distribution. 1238535d6cSHans Petter Selasky * 1338535d6cSHans Petter Selasky * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND 1438535d6cSHans Petter Selasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1538535d6cSHans Petter Selasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1638535d6cSHans Petter Selasky * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 1738535d6cSHans Petter Selasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1838535d6cSHans Petter Selasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1938535d6cSHans Petter Selasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2038535d6cSHans Petter Selasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2138535d6cSHans Petter Selasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2238535d6cSHans Petter Selasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2338535d6cSHans Petter Selasky * SUCH DAMAGE. 2438535d6cSHans Petter Selasky * 2538535d6cSHans Petter Selasky * $FreeBSD$ 2638535d6cSHans Petter Selasky */ 2738535d6cSHans Petter Selasky 2838535d6cSHans Petter Selasky #include <linux/kernel.h> 2938535d6cSHans Petter Selasky #include <linux/module.h> 3038535d6cSHans Petter Selasky #include <dev/mlx5/driver.h> 3138535d6cSHans Petter Selasky #include "mlx5_core.h" 3238535d6cSHans Petter Selasky 3338535d6cSHans Petter Selasky #ifdef RATELIMIT 3438535d6cSHans Petter Selasky 3538535d6cSHans Petter Selasky /* Finds an entry where we can register the given rate 3638535d6cSHans Petter Selasky * If the rate already exists, return the entry where it is registered, 3738535d6cSHans Petter Selasky * otherwise return the first available entry. 3838535d6cSHans Petter Selasky * If the table is full, return NULL 3938535d6cSHans Petter Selasky */ 4038535d6cSHans Petter Selasky static struct mlx5_rl_entry *find_rl_entry(struct mlx5_rl_table *table, 4138535d6cSHans Petter Selasky u32 rate, u16 burst) 4238535d6cSHans Petter Selasky { 4338535d6cSHans Petter Selasky struct mlx5_rl_entry *ret_entry = NULL; 4438535d6cSHans Petter Selasky struct mlx5_rl_entry *entry; 4538535d6cSHans Petter Selasky u16 i; 4638535d6cSHans Petter Selasky 4738535d6cSHans Petter Selasky for (i = 0; i < table->max_size; i++) { 4838535d6cSHans Petter Selasky entry = table->rl_entry + i; 4938535d6cSHans Petter Selasky if (entry->rate == rate && entry->burst == burst) 5038535d6cSHans Petter Selasky return entry; 5138535d6cSHans Petter Selasky if (ret_entry == NULL && entry->rate == 0) 5238535d6cSHans Petter Selasky ret_entry = entry; 5338535d6cSHans Petter Selasky } 5438535d6cSHans Petter Selasky 5538535d6cSHans Petter Selasky return ret_entry; 5638535d6cSHans Petter Selasky } 5738535d6cSHans Petter Selasky 5838535d6cSHans Petter Selasky static int mlx5_set_rate_limit_cmd(struct mlx5_core_dev *dev, 5938535d6cSHans Petter Selasky u32 rate, u32 burst, u16 index) 6038535d6cSHans Petter Selasky { 61bf43f981SHans Petter Selasky u32 in[MLX5_ST_SZ_DW(set_rate_limit_in)] = {}; 62bf43f981SHans Petter Selasky u32 out[MLX5_ST_SZ_DW(set_rate_limit_out)] = {}; 6338535d6cSHans Petter Selasky 64bf43f981SHans Petter Selasky MLX5_SET(set_rate_limit_in, in, opcode, MLX5_CMD_OP_SET_RATE_LIMIT); 6538535d6cSHans Petter Selasky MLX5_SET(set_rate_limit_in, in, rate_limit_index, index); 6638535d6cSHans Petter Selasky MLX5_SET(set_rate_limit_in, in, rate_limit, rate); 6738535d6cSHans Petter Selasky MLX5_SET(set_rate_limit_in, in, burst_upper_bound, burst); 68bf43f981SHans Petter Selasky MLX5_SET(set_rate_limit_in, in, typical_packet_size, 0 /* use MTU */); 6938535d6cSHans Petter Selasky 7038535d6cSHans Petter Selasky return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); 7138535d6cSHans Petter Selasky } 7238535d6cSHans Petter Selasky 7338535d6cSHans Petter Selasky bool mlx5_rl_is_in_range(const struct mlx5_core_dev *dev, u32 rate, u32 burst) 7438535d6cSHans Petter Selasky { 7538535d6cSHans Petter Selasky const struct mlx5_rl_table *table = &dev->priv.rl_table; 7638535d6cSHans Petter Selasky 7738535d6cSHans Petter Selasky return (rate <= table->max_rate && rate >= table->min_rate && 7838535d6cSHans Petter Selasky burst <= 65535); 7938535d6cSHans Petter Selasky } 8038535d6cSHans Petter Selasky EXPORT_SYMBOL(mlx5_rl_is_in_range); 8138535d6cSHans Petter Selasky 8238535d6cSHans Petter Selasky int mlx5_rl_add_rate(struct mlx5_core_dev *dev, u32 rate, u32 burst, u16 *index) 8338535d6cSHans Petter Selasky { 8438535d6cSHans Petter Selasky struct mlx5_rl_table *table = &dev->priv.rl_table; 8538535d6cSHans Petter Selasky struct mlx5_rl_entry *entry; 8638535d6cSHans Petter Selasky int err = 0; 8738535d6cSHans Petter Selasky 8838535d6cSHans Petter Selasky mutex_lock(&table->rl_lock); 8938535d6cSHans Petter Selasky 9038535d6cSHans Petter Selasky if (!rate || !mlx5_rl_is_in_range(dev, rate, burst)) { 9138535d6cSHans Petter Selasky mlx5_core_err(dev, "Invalid rate: %u, should be %u to %u\n", 9238535d6cSHans Petter Selasky rate, table->min_rate, table->max_rate); 9338535d6cSHans Petter Selasky err = -ERANGE; 9438535d6cSHans Petter Selasky goto out; 9538535d6cSHans Petter Selasky } 9638535d6cSHans Petter Selasky 9738535d6cSHans Petter Selasky entry = find_rl_entry(table, rate, burst); 9838535d6cSHans Petter Selasky if (!entry) { 9938535d6cSHans Petter Selasky mlx5_core_err(dev, "Max number of %u rates reached\n", 10038535d6cSHans Petter Selasky table->max_size); 10138535d6cSHans Petter Selasky err = -ENOSPC; 10238535d6cSHans Petter Selasky goto out; 10338535d6cSHans Petter Selasky } 10438535d6cSHans Petter Selasky if (entry->refcount == 0xFFFFFFFFU) { 10538535d6cSHans Petter Selasky /* out of refcounts */ 10638535d6cSHans Petter Selasky err = -ENOMEM; 10738535d6cSHans Petter Selasky goto out; 10838535d6cSHans Petter Selasky } else if (entry->refcount != 0) { 10938535d6cSHans Petter Selasky /* rate already configured */ 11038535d6cSHans Petter Selasky entry->refcount++; 11138535d6cSHans Petter Selasky } else { 11238535d6cSHans Petter Selasky /* new rate limit */ 11338535d6cSHans Petter Selasky err = mlx5_set_rate_limit_cmd(dev, rate, burst, entry->index); 11438535d6cSHans Petter Selasky if (err) { 11538535d6cSHans Petter Selasky mlx5_core_err(dev, "Failed configuring rate: %u (%d)\n", 11638535d6cSHans Petter Selasky rate, err); 11738535d6cSHans Petter Selasky goto out; 11838535d6cSHans Petter Selasky } 11938535d6cSHans Petter Selasky entry->rate = rate; 12038535d6cSHans Petter Selasky entry->burst = burst; 12138535d6cSHans Petter Selasky entry->refcount = 1; 12238535d6cSHans Petter Selasky } 12338535d6cSHans Petter Selasky *index = entry->index; 12438535d6cSHans Petter Selasky 12538535d6cSHans Petter Selasky out: 12638535d6cSHans Petter Selasky mutex_unlock(&table->rl_lock); 12738535d6cSHans Petter Selasky return err; 12838535d6cSHans Petter Selasky } 12938535d6cSHans Petter Selasky EXPORT_SYMBOL(mlx5_rl_add_rate); 13038535d6cSHans Petter Selasky 13138535d6cSHans Petter Selasky void mlx5_rl_remove_rate(struct mlx5_core_dev *dev, u32 rate, u32 burst) 13238535d6cSHans Petter Selasky { 13338535d6cSHans Petter Selasky struct mlx5_rl_table *table = &dev->priv.rl_table; 13438535d6cSHans Petter Selasky struct mlx5_rl_entry *entry = NULL; 13538535d6cSHans Petter Selasky 13638535d6cSHans Petter Selasky /* 0 is a reserved value for unlimited rate */ 13738535d6cSHans Petter Selasky if (rate == 0) 13838535d6cSHans Petter Selasky return; 13938535d6cSHans Petter Selasky 14038535d6cSHans Petter Selasky mutex_lock(&table->rl_lock); 14138535d6cSHans Petter Selasky entry = find_rl_entry(table, rate, burst); 14238535d6cSHans Petter Selasky if (!entry || !entry->refcount) { 14338535d6cSHans Petter Selasky mlx5_core_warn(dev, "Rate %u is not configured\n", rate); 14438535d6cSHans Petter Selasky goto out; 14538535d6cSHans Petter Selasky } 14638535d6cSHans Petter Selasky 14738535d6cSHans Petter Selasky entry->refcount--; 14838535d6cSHans Petter Selasky if (!entry->refcount) { 14938535d6cSHans Petter Selasky /* need to remove rate */ 15038535d6cSHans Petter Selasky mlx5_set_rate_limit_cmd(dev, 0, 0, entry->index); 15138535d6cSHans Petter Selasky entry->rate = 0; 15238535d6cSHans Petter Selasky entry->burst = 0; 15338535d6cSHans Petter Selasky } 15438535d6cSHans Petter Selasky 15538535d6cSHans Petter Selasky out: 15638535d6cSHans Petter Selasky mutex_unlock(&table->rl_lock); 15738535d6cSHans Petter Selasky } 15838535d6cSHans Petter Selasky EXPORT_SYMBOL(mlx5_rl_remove_rate); 15938535d6cSHans Petter Selasky 16038535d6cSHans Petter Selasky int mlx5_init_rl_table(struct mlx5_core_dev *dev) 16138535d6cSHans Petter Selasky { 16238535d6cSHans Petter Selasky struct mlx5_rl_table *table = &dev->priv.rl_table; 16338535d6cSHans Petter Selasky int i; 16438535d6cSHans Petter Selasky 16538535d6cSHans Petter Selasky mutex_init(&table->rl_lock); 16638535d6cSHans Petter Selasky if (!MLX5_CAP_GEN(dev, qos) || !MLX5_CAP_QOS(dev, packet_pacing)) { 16738535d6cSHans Petter Selasky table->max_size = 0; 16838535d6cSHans Petter Selasky return 0; 16938535d6cSHans Petter Selasky } 17038535d6cSHans Petter Selasky 17138535d6cSHans Petter Selasky /* First entry is reserved for unlimited rate */ 17238535d6cSHans Petter Selasky table->max_size = MLX5_CAP_QOS(dev, packet_pacing_rate_table_size) - 1; 17338535d6cSHans Petter Selasky table->max_rate = MLX5_CAP_QOS(dev, packet_pacing_max_rate); 17438535d6cSHans Petter Selasky table->min_rate = MLX5_CAP_QOS(dev, packet_pacing_min_rate); 17538535d6cSHans Petter Selasky 17638535d6cSHans Petter Selasky table->rl_entry = kcalloc(table->max_size, sizeof(struct mlx5_rl_entry), 17738535d6cSHans Petter Selasky GFP_KERNEL); 17838535d6cSHans Petter Selasky if (!table->rl_entry) 17938535d6cSHans Petter Selasky return -ENOMEM; 18038535d6cSHans Petter Selasky 18138535d6cSHans Petter Selasky /* The index represents the index in HW rate limit table 18238535d6cSHans Petter Selasky * Index 0 is reserved for unlimited rate 18338535d6cSHans Petter Selasky */ 18438535d6cSHans Petter Selasky for (i = 0; i < table->max_size; i++) 18538535d6cSHans Petter Selasky table->rl_entry[i].index = i + 1; 18638535d6cSHans Petter Selasky 18738535d6cSHans Petter Selasky return 0; 18838535d6cSHans Petter Selasky } 18938535d6cSHans Petter Selasky 19038535d6cSHans Petter Selasky void mlx5_cleanup_rl_table(struct mlx5_core_dev *dev) 19138535d6cSHans Petter Selasky { 19238535d6cSHans Petter Selasky struct mlx5_rl_table *table = &dev->priv.rl_table; 19338535d6cSHans Petter Selasky int i; 19438535d6cSHans Petter Selasky 19538535d6cSHans Petter Selasky /* Clear all configured rates */ 19638535d6cSHans Petter Selasky for (i = 0; i < table->max_size; i++) 19738535d6cSHans Petter Selasky if (table->rl_entry[i].rate) 19838535d6cSHans Petter Selasky mlx5_set_rate_limit_cmd(dev, 0, 0, 19938535d6cSHans Petter Selasky table->rl_entry[i].index); 20038535d6cSHans Petter Selasky 20138535d6cSHans Petter Selasky kfree(dev->priv.rl_table.rl_entry); 20238535d6cSHans Petter Selasky } 20338535d6cSHans Petter Selasky 20438535d6cSHans Petter Selasky #endif 205