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)] = {}; 62 u32 out[MLX5_ST_SZ_DW(set_rate_limit_out)] = {}; 63 64 MLX5_SET(set_rate_limit_in, in, opcode, MLX5_CMD_OP_SET_RATE_LIMIT); 65 MLX5_SET(set_rate_limit_in, in, rate_limit_index, index); 66 MLX5_SET(set_rate_limit_in, in, rate_limit, rate); 67 MLX5_SET(set_rate_limit_in, in, burst_upper_bound, burst); 68 MLX5_SET(set_rate_limit_in, in, typical_packet_size, 0 /* use MTU */); 69 70 return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); 71 } 72 73 bool mlx5_rl_is_in_range(const struct mlx5_core_dev *dev, u32 rate, u32 burst) 74 { 75 const struct mlx5_rl_table *table = &dev->priv.rl_table; 76 77 return (rate <= table->max_rate && rate >= table->min_rate && 78 burst <= 65535); 79 } 80 EXPORT_SYMBOL(mlx5_rl_is_in_range); 81 82 int mlx5_rl_add_rate(struct mlx5_core_dev *dev, u32 rate, u32 burst, u16 *index) 83 { 84 struct mlx5_rl_table *table = &dev->priv.rl_table; 85 struct mlx5_rl_entry *entry; 86 int err = 0; 87 88 mutex_lock(&table->rl_lock); 89 90 if (!rate || !mlx5_rl_is_in_range(dev, rate, burst)) { 91 mlx5_core_err(dev, "Invalid rate: %u, should be %u to %u\n", 92 rate, table->min_rate, table->max_rate); 93 err = -ERANGE; 94 goto out; 95 } 96 97 entry = find_rl_entry(table, rate, burst); 98 if (!entry) { 99 mlx5_core_err(dev, "Max number of %u rates reached\n", 100 table->max_size); 101 err = -ENOSPC; 102 goto out; 103 } 104 if (entry->refcount == 0xFFFFFFFFU) { 105 /* out of refcounts */ 106 err = -ENOMEM; 107 goto out; 108 } else if (entry->refcount != 0) { 109 /* rate already configured */ 110 entry->refcount++; 111 } else { 112 /* new rate limit */ 113 err = mlx5_set_rate_limit_cmd(dev, rate, burst, entry->index); 114 if (err) { 115 mlx5_core_err(dev, "Failed configuring rate: %u (%d)\n", 116 rate, err); 117 goto out; 118 } 119 entry->rate = rate; 120 entry->burst = burst; 121 entry->refcount = 1; 122 } 123 *index = entry->index; 124 125 out: 126 mutex_unlock(&table->rl_lock); 127 return err; 128 } 129 EXPORT_SYMBOL(mlx5_rl_add_rate); 130 131 void mlx5_rl_remove_rate(struct mlx5_core_dev *dev, u32 rate, u32 burst) 132 { 133 struct mlx5_rl_table *table = &dev->priv.rl_table; 134 struct mlx5_rl_entry *entry = NULL; 135 136 /* 0 is a reserved value for unlimited rate */ 137 if (rate == 0) 138 return; 139 140 mutex_lock(&table->rl_lock); 141 entry = find_rl_entry(table, rate, burst); 142 if (!entry || !entry->refcount) { 143 mlx5_core_warn(dev, "Rate %u is not configured\n", rate); 144 goto out; 145 } 146 147 entry->refcount--; 148 if (!entry->refcount) { 149 /* need to remove rate */ 150 mlx5_set_rate_limit_cmd(dev, 0, 0, entry->index); 151 entry->rate = 0; 152 entry->burst = 0; 153 } 154 155 out: 156 mutex_unlock(&table->rl_lock); 157 } 158 EXPORT_SYMBOL(mlx5_rl_remove_rate); 159 160 int mlx5_init_rl_table(struct mlx5_core_dev *dev) 161 { 162 struct mlx5_rl_table *table = &dev->priv.rl_table; 163 int i; 164 165 mutex_init(&table->rl_lock); 166 if (!MLX5_CAP_GEN(dev, qos) || !MLX5_CAP_QOS(dev, packet_pacing)) { 167 table->max_size = 0; 168 return 0; 169 } 170 171 /* First entry is reserved for unlimited rate */ 172 table->max_size = MLX5_CAP_QOS(dev, packet_pacing_rate_table_size) - 1; 173 table->max_rate = MLX5_CAP_QOS(dev, packet_pacing_max_rate); 174 table->min_rate = MLX5_CAP_QOS(dev, packet_pacing_min_rate); 175 176 table->rl_entry = kcalloc(table->max_size, sizeof(struct mlx5_rl_entry), 177 GFP_KERNEL); 178 if (!table->rl_entry) 179 return -ENOMEM; 180 181 /* The index represents the index in HW rate limit table 182 * Index 0 is reserved for unlimited rate 183 */ 184 for (i = 0; i < table->max_size; i++) 185 table->rl_entry[i].index = i + 1; 186 187 return 0; 188 } 189 190 void mlx5_cleanup_rl_table(struct mlx5_core_dev *dev) 191 { 192 struct mlx5_rl_table *table = &dev->priv.rl_table; 193 int i; 194 195 /* Clear all configured rates */ 196 for (i = 0; i < table->max_size; i++) 197 if (table->rl_entry[i].rate) 198 mlx5_set_rate_limit_cmd(dev, 0, 0, 199 table->rl_entry[i].index); 200 201 kfree(dev->priv.rl_table.rl_entry); 202 } 203 204 #endif 205