1 /*- 2 * Copyright (c) 2013-2017, Mellanox Technologies, Ltd. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 * 25 * $FreeBSD$ 26 */ 27 28 #include <linux/kernel.h> 29 #include <linux/module.h> 30 #include <dev/mlx5/driver.h> 31 #include "mlx5_core.h" 32 33 #ifdef RATELIMIT 34 35 /* Finds an entry where we can register the given rate 36 * If the rate already exists, return the entry where it is registered, 37 * otherwise return the first available entry. 38 * If the table is full, return NULL 39 */ 40 static struct mlx5_rl_entry *find_rl_entry(struct mlx5_rl_table *table, 41 u32 rate, u16 burst) 42 { 43 struct mlx5_rl_entry *ret_entry = NULL; 44 struct mlx5_rl_entry *entry; 45 u16 i; 46 47 for (i = 0; i < table->max_size; i++) { 48 entry = table->rl_entry + i; 49 if (entry->rate == rate && entry->burst == burst) 50 return entry; 51 if (ret_entry == NULL && entry->rate == 0) 52 ret_entry = entry; 53 } 54 55 return ret_entry; 56 } 57 58 static int mlx5_set_rate_limit_cmd(struct mlx5_core_dev *dev, 59 u32 rate, u32 burst, u16 index) 60 { 61 u32 in[MLX5_ST_SZ_DW(set_rate_limit_in)] = {0}; 62 u32 out[MLX5_ST_SZ_DW(set_rate_limit_out)] = {0}; 63 64 MLX5_SET(set_rate_limit_in, in, opcode, 65 MLX5_CMD_OP_SET_RATE_LIMIT); 66 MLX5_SET(set_rate_limit_in, in, rate_limit_index, index); 67 MLX5_SET(set_rate_limit_in, in, rate_limit, rate); 68 69 if (MLX5_CAP_QOS(dev, packet_pacing_burst_bound)) 70 MLX5_SET(set_rate_limit_in, in, burst_upper_bound, burst); 71 72 return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); 73 } 74 75 bool mlx5_rl_is_in_range(const struct mlx5_core_dev *dev, u32 rate, u32 burst) 76 { 77 const struct mlx5_rl_table *table = &dev->priv.rl_table; 78 79 return (rate <= table->max_rate && rate >= table->min_rate && 80 burst <= 65535); 81 } 82 EXPORT_SYMBOL(mlx5_rl_is_in_range); 83 84 int mlx5_rl_add_rate(struct mlx5_core_dev *dev, u32 rate, u32 burst, u16 *index) 85 { 86 struct mlx5_rl_table *table = &dev->priv.rl_table; 87 struct mlx5_rl_entry *entry; 88 int err = 0; 89 90 mutex_lock(&table->rl_lock); 91 92 if (!rate || !mlx5_rl_is_in_range(dev, rate, burst)) { 93 mlx5_core_err(dev, "Invalid rate: %u, should be %u to %u\n", 94 rate, table->min_rate, table->max_rate); 95 err = -ERANGE; 96 goto out; 97 } 98 99 entry = find_rl_entry(table, rate, burst); 100 if (!entry) { 101 mlx5_core_err(dev, "Max number of %u rates reached\n", 102 table->max_size); 103 err = -ENOSPC; 104 goto out; 105 } 106 if (entry->refcount == 0xFFFFFFFFU) { 107 /* out of refcounts */ 108 err = -ENOMEM; 109 goto out; 110 } else if (entry->refcount != 0) { 111 /* rate already configured */ 112 entry->refcount++; 113 } else { 114 /* new rate limit */ 115 err = mlx5_set_rate_limit_cmd(dev, rate, burst, entry->index); 116 if (err) { 117 mlx5_core_err(dev, "Failed configuring rate: %u (%d)\n", 118 rate, err); 119 goto out; 120 } 121 entry->rate = rate; 122 entry->burst = burst; 123 entry->refcount = 1; 124 } 125 *index = entry->index; 126 127 out: 128 mutex_unlock(&table->rl_lock); 129 return err; 130 } 131 EXPORT_SYMBOL(mlx5_rl_add_rate); 132 133 void mlx5_rl_remove_rate(struct mlx5_core_dev *dev, u32 rate, u32 burst) 134 { 135 struct mlx5_rl_table *table = &dev->priv.rl_table; 136 struct mlx5_rl_entry *entry = NULL; 137 138 /* 0 is a reserved value for unlimited rate */ 139 if (rate == 0) 140 return; 141 142 mutex_lock(&table->rl_lock); 143 entry = find_rl_entry(table, rate, burst); 144 if (!entry || !entry->refcount) { 145 mlx5_core_warn(dev, "Rate %u is not configured\n", rate); 146 goto out; 147 } 148 149 entry->refcount--; 150 if (!entry->refcount) { 151 /* need to remove rate */ 152 mlx5_set_rate_limit_cmd(dev, 0, 0, entry->index); 153 entry->rate = 0; 154 entry->burst = 0; 155 } 156 157 out: 158 mutex_unlock(&table->rl_lock); 159 } 160 EXPORT_SYMBOL(mlx5_rl_remove_rate); 161 162 int mlx5_init_rl_table(struct mlx5_core_dev *dev) 163 { 164 struct mlx5_rl_table *table = &dev->priv.rl_table; 165 int i; 166 167 mutex_init(&table->rl_lock); 168 if (!MLX5_CAP_GEN(dev, qos) || !MLX5_CAP_QOS(dev, packet_pacing)) { 169 table->max_size = 0; 170 return 0; 171 } 172 173 /* First entry is reserved for unlimited rate */ 174 table->max_size = MLX5_CAP_QOS(dev, packet_pacing_rate_table_size) - 1; 175 table->max_rate = MLX5_CAP_QOS(dev, packet_pacing_max_rate); 176 table->min_rate = MLX5_CAP_QOS(dev, packet_pacing_min_rate); 177 178 table->rl_entry = kcalloc(table->max_size, sizeof(struct mlx5_rl_entry), 179 GFP_KERNEL); 180 if (!table->rl_entry) 181 return -ENOMEM; 182 183 /* The index represents the index in HW rate limit table 184 * Index 0 is reserved for unlimited rate 185 */ 186 for (i = 0; i < table->max_size; i++) 187 table->rl_entry[i].index = i + 1; 188 189 return 0; 190 } 191 192 void mlx5_cleanup_rl_table(struct mlx5_core_dev *dev) 193 { 194 struct mlx5_rl_table *table = &dev->priv.rl_table; 195 int i; 196 197 /* Clear all configured rates */ 198 for (i = 0; i < table->max_size; i++) 199 if (table->rl_entry[i].rate) 200 mlx5_set_rate_limit_cmd(dev, 0, 0, 201 table->rl_entry[i].index); 202 203 kfree(dev->priv.rl_table.rl_entry); 204 } 205 206 #endif 207