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 "opt_rss.h" 29 #include "opt_ratelimit.h" 30 31 #include <linux/kernel.h> 32 #include <linux/module.h> 33 #include <dev/mlx5/driver.h> 34 #include <dev/mlx5/mlx5_core/mlx5_core.h> 35 36 #ifdef RATELIMIT 37 38 /* Finds an entry where we can register the given rate 39 * If the rate already exists, return the entry where it is registered, 40 * otherwise return the first available entry. 41 * If the table is full, return NULL 42 */ 43 static struct mlx5_rl_entry *find_rl_entry(struct mlx5_rl_table *table, 44 u32 rate, u16 burst) 45 { 46 struct mlx5_rl_entry *ret_entry = NULL; 47 struct mlx5_rl_entry *entry; 48 u16 i; 49 50 for (i = 0; i < table->max_size; i++) { 51 entry = table->rl_entry + i; 52 if (entry->rate == rate && entry->burst == burst) 53 return entry; 54 if (ret_entry == NULL && entry->rate == 0) 55 ret_entry = entry; 56 } 57 58 return ret_entry; 59 } 60 61 static int mlx5_set_rate_limit_cmd(struct mlx5_core_dev *dev, 62 u32 rate, u32 burst, u16 index) 63 { 64 u32 in[MLX5_ST_SZ_DW(set_rate_limit_in)] = {}; 65 u32 out[MLX5_ST_SZ_DW(set_rate_limit_out)] = {}; 66 67 MLX5_SET(set_rate_limit_in, in, opcode, MLX5_CMD_OP_SET_RATE_LIMIT); 68 MLX5_SET(set_rate_limit_in, in, rate_limit_index, index); 69 MLX5_SET(set_rate_limit_in, in, rate_limit, rate); 70 MLX5_SET(set_rate_limit_in, in, burst_upper_bound, burst); 71 MLX5_SET(set_rate_limit_in, in, typical_packet_size, 0 /* use MTU */); 72 73 return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); 74 } 75 76 int mlx5e_query_rate_limit_cmd(struct mlx5_core_dev *dev, 77 u16 index, u32 *scq_handle) 78 { 79 int err; 80 u32 in[MLX5_ST_SZ_DW(query_pp_rate_limit_in)] = {}; 81 u32 out[MLX5_ST_SZ_DW(query_pp_rate_limit_out)] = {}; 82 83 MLX5_SET(query_pp_rate_limit_in, in, opcode, MLX5_CMD_OP_QUERY_RATE_LIMIT); 84 MLX5_SET(query_pp_rate_limit_in, in, rate_limit_index, index); 85 86 err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); 87 if (err) 88 return err; 89 90 *scq_handle = MLX5_GET(query_pp_rate_limit_out, out, pp_context.qos_handle); 91 92 return 0; 93 } 94 95 bool mlx5_rl_is_in_range(const struct mlx5_core_dev *dev, u32 rate, u32 burst) 96 { 97 const struct mlx5_rl_table *table = &dev->priv.rl_table; 98 99 return (rate <= table->max_rate && rate >= table->min_rate && 100 burst <= 65535); 101 } 102 EXPORT_SYMBOL(mlx5_rl_is_in_range); 103 104 int mlx5_rl_add_rate(struct mlx5_core_dev *dev, u32 rate, u32 burst, u16 *index) 105 { 106 struct mlx5_rl_table *table = &dev->priv.rl_table; 107 struct mlx5_rl_entry *entry; 108 int err = 0; 109 110 mutex_lock(&table->rl_lock); 111 112 if (!rate || !mlx5_rl_is_in_range(dev, rate, burst)) { 113 mlx5_core_err(dev, "Invalid rate: %u, should be %u to %u\n", 114 rate, table->min_rate, table->max_rate); 115 err = -ERANGE; 116 goto out; 117 } 118 119 entry = find_rl_entry(table, rate, burst); 120 if (!entry) { 121 mlx5_core_err(dev, "Max number of %u rates reached\n", 122 table->max_size); 123 err = -ENOSPC; 124 goto out; 125 } 126 if (entry->refcount == 0xFFFFFFFFU) { 127 /* out of refcounts */ 128 err = -ENOMEM; 129 goto out; 130 } else if (entry->refcount != 0) { 131 /* rate already configured */ 132 entry->refcount++; 133 } else { 134 /* new rate limit */ 135 err = mlx5_set_rate_limit_cmd(dev, rate, burst, entry->index); 136 if (err) { 137 mlx5_core_err(dev, "Failed configuring rate: %u (%d)\n", 138 rate, err); 139 goto out; 140 } 141 entry->rate = rate; 142 entry->burst = burst; 143 entry->refcount = 1; 144 145 if (MLX5_CAP_QOS(dev, qos_remap_pp)) { 146 err = mlx5e_query_rate_limit_cmd(dev, entry->index, &entry->qos_handle); 147 if (err) { 148 mlx5_core_err(dev, "Failed retrieving schedule queue handle for" 149 "SQ remap: rate: %u error:(%d)\n", rate, err); 150 entry->qos_handle = MLX5_INVALID_QUEUE_HANDLE; 151 } 152 } else 153 entry->qos_handle = MLX5_INVALID_QUEUE_HANDLE; 154 } 155 *index = entry->index; 156 157 out: 158 mutex_unlock(&table->rl_lock); 159 return err; 160 } 161 EXPORT_SYMBOL(mlx5_rl_add_rate); 162 163 void mlx5_rl_remove_rate(struct mlx5_core_dev *dev, u32 rate, u32 burst) 164 { 165 struct mlx5_rl_table *table = &dev->priv.rl_table; 166 struct mlx5_rl_entry *entry = NULL; 167 168 /* 0 is a reserved value for unlimited rate */ 169 if (rate == 0) 170 return; 171 172 mutex_lock(&table->rl_lock); 173 entry = find_rl_entry(table, rate, burst); 174 if (!entry || !entry->refcount) { 175 mlx5_core_warn(dev, "Rate %u is not configured\n", rate); 176 goto out; 177 } 178 179 entry->refcount--; 180 if (!entry->refcount) { 181 /* need to remove rate */ 182 mlx5_set_rate_limit_cmd(dev, 0, 0, entry->index); 183 entry->rate = 0; 184 entry->burst = 0; 185 } 186 187 out: 188 mutex_unlock(&table->rl_lock); 189 } 190 EXPORT_SYMBOL(mlx5_rl_remove_rate); 191 192 int mlx5_init_rl_table(struct mlx5_core_dev *dev) 193 { 194 struct mlx5_rl_table *table = &dev->priv.rl_table; 195 int i; 196 197 mutex_init(&table->rl_lock); 198 if (!MLX5_CAP_GEN(dev, qos) || !MLX5_CAP_QOS(dev, packet_pacing)) { 199 table->max_size = 0; 200 return 0; 201 } 202 203 /* First entry is reserved for unlimited rate */ 204 table->max_size = MLX5_CAP_QOS(dev, packet_pacing_rate_table_size) - 1; 205 table->max_rate = MLX5_CAP_QOS(dev, packet_pacing_max_rate); 206 table->min_rate = MLX5_CAP_QOS(dev, packet_pacing_min_rate); 207 208 table->rl_entry = kcalloc(table->max_size, sizeof(struct mlx5_rl_entry), 209 GFP_KERNEL); 210 if (!table->rl_entry) 211 return -ENOMEM; 212 213 /* The index represents the index in HW rate limit table 214 * Index 0 is reserved for unlimited rate 215 */ 216 for (i = 0; i < table->max_size; i++) 217 table->rl_entry[i].index = i + 1; 218 219 return 0; 220 } 221 222 void mlx5_cleanup_rl_table(struct mlx5_core_dev *dev) 223 { 224 struct mlx5_rl_table *table = &dev->priv.rl_table; 225 int i; 226 227 /* Clear all configured rates */ 228 for (i = 0; i < table->max_size; i++) 229 if (table->rl_entry[i].rate) 230 mlx5_set_rate_limit_cmd(dev, 0, 0, 231 table->rl_entry[i].index); 232 233 kfree(dev->priv.rl_table.rl_entry); 234 } 235 236 #endif 237