File Index
Files and directories
Sign in
FILE

rl.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/mellanox/mlx5/core/rl.c C
  1/*
  2 * Copyright (c) 2013-2016, Mellanox Technologies. All rights reserved.
  3 *
  4 * This software is available to you under a choice of one of two
  5 * licenses.  You may choose to be licensed under the terms of the GNU
  6 * General Public License (GPL) Version 2, available from the file
  7 * COPYING in the main directory of this source tree, or the
  8 * OpenIB.org BSD license below:
  9 *
 10 *     Redistribution and use in source and binary forms, with or
 11 *     without modification, are permitted provided that the following
 12 *     conditions are met:
 13 *
 14 *      - Redistributions of source code must retain the above
 15 *        copyright notice, this list of conditions and the following
 16 *        disclaimer.
 17 *
 18 *      - Redistributions in binary form must reproduce the above
 19 *        copyright notice, this list of conditions and the following
 20 *        disclaimer in the documentation and/or other materials
 21 *        provided with the distribution.
 22 *
 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 30 * SOFTWARE.
 31 */
 32
 33#include <linux/kernel.h>
 34#include <linux/mlx5/driver.h>
 35#include "mlx5_core.h"
 36
 37bool mlx5_qos_tsar_type_supported(struct mlx5_core_dev *dev, int type, u8 hierarchy)
 38{
 39	int cap;
 40
 41	switch (hierarchy) {
 42	case SCHEDULING_HIERARCHY_E_SWITCH:
 43		cap =  MLX5_CAP_QOS(dev, esw_tsar_type);
 44		break;
 45	case SCHEDULING_HIERARCHY_NIC:
 46		cap = MLX5_CAP_QOS(dev, nic_tsar_type);
 47		break;
 48	default:
 49		return false;
 50	}
 51
 52	switch (type) {
 53	case TSAR_ELEMENT_TSAR_TYPE_DWRR:
 54		return cap & TSAR_TYPE_CAP_MASK_DWRR;
 55	case TSAR_ELEMENT_TSAR_TYPE_ROUND_ROBIN:
 56		return cap & TSAR_TYPE_CAP_MASK_ROUND_ROBIN;
 57	case TSAR_ELEMENT_TSAR_TYPE_ETS:
 58		return cap & TSAR_TYPE_CAP_MASK_ETS;
 59	case TSAR_ELEMENT_TSAR_TYPE_TC_ARB:
 60		return cap & TSAR_TYPE_CAP_MASK_TC_ARB;
 61	}
 62
 63	return false;
 64}
 65
 66bool mlx5_qos_element_type_supported(struct mlx5_core_dev *dev, int type, u8 hierarchy)
 67{
 68	int cap;
 69
 70	switch (hierarchy) {
 71	case SCHEDULING_HIERARCHY_E_SWITCH:
 72		cap = MLX5_CAP_QOS(dev, esw_element_type);
 73		break;
 74	case SCHEDULING_HIERARCHY_NIC:
 75		cap = MLX5_CAP_QOS(dev, nic_element_type);
 76		break;
 77	default:
 78		return false;
 79	}
 80
 81	switch (type) {
 82	case SCHEDULING_CONTEXT_ELEMENT_TYPE_TSAR:
 83		return cap & ELEMENT_TYPE_CAP_MASK_TSAR;
 84	case SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT:
 85		return cap & ELEMENT_TYPE_CAP_MASK_VPORT;
 86	case SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT_TC:
 87		return cap & ELEMENT_TYPE_CAP_MASK_VPORT_TC;
 88	case SCHEDULING_CONTEXT_ELEMENT_TYPE_PARA_VPORT_TC:
 89		return cap & ELEMENT_TYPE_CAP_MASK_PARA_VPORT_TC;
 90	case SCHEDULING_CONTEXT_ELEMENT_TYPE_QUEUE_GROUP:
 91		return cap & ELEMENT_TYPE_CAP_MASK_QUEUE_GROUP;
 92	case SCHEDULING_CONTEXT_ELEMENT_TYPE_RATE_LIMIT:
 93		return cap & ELEMENT_TYPE_CAP_MASK_RATE_LIMIT;
 94	}
 95
 96	return false;
 97}
 98
 99/* Scheduling element fw management */
100int mlx5_create_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy,
101				       void *ctx, u32 *element_id)
102{
103	u32 out[MLX5_ST_SZ_DW(create_scheduling_element_in)] = {};
104	u32 in[MLX5_ST_SZ_DW(create_scheduling_element_in)] = {};
105	void *schedc;
106	int err;
107
108	schedc = MLX5_ADDR_OF(create_scheduling_element_in, in,
109			      scheduling_context);
110	MLX5_SET(create_scheduling_element_in, in, opcode,
111		 MLX5_CMD_OP_CREATE_SCHEDULING_ELEMENT);
112	MLX5_SET(create_scheduling_element_in, in, scheduling_hierarchy,
113		 hierarchy);
114	memcpy(schedc, ctx, MLX5_ST_SZ_BYTES(scheduling_context));
115
116	err = mlx5_cmd_exec_inout(dev, create_scheduling_element, in, out);
117	if (err)
118		return err;
119
120	*element_id = MLX5_GET(create_scheduling_element_out, out,
121			       scheduling_element_id);
122	return 0;
123}
124
125int mlx5_modify_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy,
126				       void *ctx, u32 element_id,
127				       u32 modify_bitmask)
128{
129	u32 in[MLX5_ST_SZ_DW(modify_scheduling_element_in)] = {};
130	void *schedc;
131
132	schedc = MLX5_ADDR_OF(modify_scheduling_element_in, in,
133			      scheduling_context);
134	MLX5_SET(modify_scheduling_element_in, in, opcode,
135		 MLX5_CMD_OP_MODIFY_SCHEDULING_ELEMENT);
136	MLX5_SET(modify_scheduling_element_in, in, scheduling_element_id,
137		 element_id);
138	MLX5_SET(modify_scheduling_element_in, in, modify_bitmask,
139		 modify_bitmask);
140	MLX5_SET(modify_scheduling_element_in, in, scheduling_hierarchy,
141		 hierarchy);
142	memcpy(schedc, ctx, MLX5_ST_SZ_BYTES(scheduling_context));
143
144	return mlx5_cmd_exec_in(dev, modify_scheduling_element, in);
145}
146
147int mlx5_destroy_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy,
148					u32 element_id)
149{
150	u32 in[MLX5_ST_SZ_DW(destroy_scheduling_element_in)] = {};
151
152	MLX5_SET(destroy_scheduling_element_in, in, opcode,
153		 MLX5_CMD_OP_DESTROY_SCHEDULING_ELEMENT);
154	MLX5_SET(destroy_scheduling_element_in, in, scheduling_element_id,
155		 element_id);
156	MLX5_SET(destroy_scheduling_element_in, in, scheduling_hierarchy,
157		 hierarchy);
158
159	return mlx5_cmd_exec_in(dev, destroy_scheduling_element, in);
160}
161
162static bool mlx5_rl_are_equal_raw(struct mlx5_rl_entry *entry, void *rl_in,
163				  u16 uid)
164{
165	return (!memcmp(entry->rl_raw, rl_in, sizeof(entry->rl_raw)) &&
166		entry->uid == uid);
167}
168
169/* Finds an entry where we can register the given rate
170 * If the rate already exists, return the entry where it is registered,
171 * otherwise return the first available entry.
172 * If the table is full, return NULL
173 */
174static struct mlx5_rl_entry *find_rl_entry(struct mlx5_rl_table *table,
175					   void *rl_in, u16 uid, bool dedicated)
176{
177	struct mlx5_rl_entry *ret_entry = NULL;
178	bool empty_found = false;
179	int i;
180
181	lockdep_assert_held(&table->rl_lock);
182	WARN_ON(!table->rl_entry);
183
184	for (i = 0; i < table->max_size; i++) {
185		if (dedicated) {
186			if (!table->rl_entry[i].refcount)
187				return &table->rl_entry[i];
188			continue;
189		}
190
191		if (table->rl_entry[i].refcount) {
192			if (table->rl_entry[i].dedicated)
193				continue;
194			if (mlx5_rl_are_equal_raw(&table->rl_entry[i], rl_in,
195						  uid))
196				return &table->rl_entry[i];
197		} else if (!empty_found) {
198			empty_found = true;
199			ret_entry = &table->rl_entry[i];
200		}
201	}
202
203	return ret_entry;
204}
205
206static int mlx5_set_pp_rate_limit_cmd(struct mlx5_core_dev *dev,
207				      struct mlx5_rl_entry *entry, bool set)
208{
209	u32 in[MLX5_ST_SZ_DW(set_pp_rate_limit_in)] = {};
210	void *pp_context;
211
212	pp_context = MLX5_ADDR_OF(set_pp_rate_limit_in, in, ctx);
213	MLX5_SET(set_pp_rate_limit_in, in, opcode,
214		 MLX5_CMD_OP_SET_PP_RATE_LIMIT);
215	MLX5_SET(set_pp_rate_limit_in, in, uid, entry->uid);
216	MLX5_SET(set_pp_rate_limit_in, in, rate_limit_index, entry->index);
217	if (set)
218		memcpy(pp_context, entry->rl_raw, sizeof(entry->rl_raw));
219	return mlx5_cmd_exec_in(dev, set_pp_rate_limit, in);
220}
221
222bool mlx5_rl_is_in_range(struct mlx5_core_dev *dev, u32 rate)
223{
224	struct mlx5_rl_table *table = &dev->priv.rl_table;
225
226	return (rate <= table->max_rate && rate >= table->min_rate);
227}
228EXPORT_SYMBOL(mlx5_rl_is_in_range);
229
230bool mlx5_rl_are_equal(struct mlx5_rate_limit *rl_0,
231		       struct mlx5_rate_limit *rl_1)
232{
233	return ((rl_0->rate == rl_1->rate) &&
234		(rl_0->max_burst_sz == rl_1->max_burst_sz) &&
235		(rl_0->typical_pkt_sz == rl_1->typical_pkt_sz));
236}
237EXPORT_SYMBOL(mlx5_rl_are_equal);
238
239static int mlx5_rl_table_get(struct mlx5_rl_table *table)
240{
241	int i;
242
243	lockdep_assert_held(&table->rl_lock);
244
245	if (table->rl_entry) {
246		table->refcount++;
247		return 0;
248	}
249
250	table->rl_entry = kcalloc(table->max_size, sizeof(struct mlx5_rl_entry),
251				  GFP_KERNEL);
252	if (!table->rl_entry)
253		return -ENOMEM;
254
255	/* The index represents the index in HW rate limit table
256	 * Index 0 is reserved for unlimited rate
257	 */
258	for (i = 0; i < table->max_size; i++)
259		table->rl_entry[i].index = i + 1;
260
261	table->refcount++;
262	return 0;
263}
264
265static void mlx5_rl_table_put(struct mlx5_rl_table *table)
266{
267	lockdep_assert_held(&table->rl_lock);
268	if (--table->refcount)
269		return;
270
271	kfree(table->rl_entry);
272	table->rl_entry = NULL;
273}
274
275static void mlx5_rl_table_free(struct mlx5_core_dev *dev, struct mlx5_rl_table *table)
276{
277	int i;
278
279	if (!table->rl_entry)
280		return;
281
282	/* Clear all configured rates */
283	for (i = 0; i < table->max_size; i++)
284		if (table->rl_entry[i].refcount)
285			mlx5_set_pp_rate_limit_cmd(dev, &table->rl_entry[i], false);
286	kfree(table->rl_entry);
287}
288
289static void mlx5_rl_entry_get(struct mlx5_rl_entry *entry)
290{
291	entry->refcount++;
292}
293
294static void
295mlx5_rl_entry_put(struct mlx5_core_dev *dev, struct mlx5_rl_entry *entry)
296{
297	entry->refcount--;
298	if (!entry->refcount)
299		mlx5_set_pp_rate_limit_cmd(dev, entry, false);
300}
301
302int mlx5_rl_add_rate_raw(struct mlx5_core_dev *dev, void *rl_in, u16 uid,
303			 bool dedicated_entry, u16 *index)
304{
305	struct mlx5_rl_table *table = &dev->priv.rl_table;
306	struct mlx5_rl_entry *entry;
307	u32 rate;
308	int err;
309
310	if (!table->max_size)
311		return -EOPNOTSUPP;
312
313	rate = MLX5_GET(set_pp_rate_limit_context, rl_in, rate_limit);
314	if (!rate || !mlx5_rl_is_in_range(dev, rate)) {
315		mlx5_core_err(dev, "Invalid rate: %u, should be %u to %u\n",
316			      rate, table->min_rate, table->max_rate);
317		return -EINVAL;
318	}
319
320	mutex_lock(&table->rl_lock);
321	err = mlx5_rl_table_get(table);
322	if (err)
323		goto out;
324
325	entry = find_rl_entry(table, rl_in, uid, dedicated_entry);
326	if (!entry) {
327		mlx5_core_err(dev, "Max number of %u rates reached\n",
328			      table->max_size);
329		err = -ENOSPC;
330		goto rl_err;
331	}
332	if (!entry->refcount) {
333		/* new rate limit */
334		memcpy(entry->rl_raw, rl_in, sizeof(entry->rl_raw));
335		entry->uid = uid;
336		err = mlx5_set_pp_rate_limit_cmd(dev, entry, true);
337		if (err) {
338			mlx5_core_err(
339				dev,
340				"Failed configuring rate limit(err %d): rate %u, max_burst_sz %u, typical_pkt_sz %u\n",
341				err, rate,
342				MLX5_GET(set_pp_rate_limit_context, rl_in,
343					 burst_upper_bound),
344				MLX5_GET(set_pp_rate_limit_context, rl_in,
345					 typical_packet_size));
346			goto rl_err;
347		}
348
349		entry->dedicated = dedicated_entry;
350	}
351	mlx5_rl_entry_get(entry);
352	*index = entry->index;
353	mutex_unlock(&table->rl_lock);
354	return 0;
355
356rl_err:
357	mlx5_rl_table_put(table);
358out:
359	mutex_unlock(&table->rl_lock);
360	return err;
361}
362EXPORT_SYMBOL(mlx5_rl_add_rate_raw);
363
364void mlx5_rl_remove_rate_raw(struct mlx5_core_dev *dev, u16 index)
365{
366	struct mlx5_rl_table *table = &dev->priv.rl_table;
367	struct mlx5_rl_entry *entry;
368
369	mutex_lock(&table->rl_lock);
370	entry = &table->rl_entry[index - 1];
371	mlx5_rl_entry_put(dev, entry);
372	mlx5_rl_table_put(table);
373	mutex_unlock(&table->rl_lock);
374}
375EXPORT_SYMBOL(mlx5_rl_remove_rate_raw);
376
377int mlx5_rl_add_rate(struct mlx5_core_dev *dev, u16 *index,
378		     struct mlx5_rate_limit *rl)
379{
380	u8 rl_raw[MLX5_ST_SZ_BYTES(set_pp_rate_limit_context)] = {};
381
382	MLX5_SET(set_pp_rate_limit_context, rl_raw, rate_limit, rl->rate);
383	MLX5_SET(set_pp_rate_limit_context, rl_raw, burst_upper_bound,
384		 rl->max_burst_sz);
385	MLX5_SET(set_pp_rate_limit_context, rl_raw, typical_packet_size,
386		 rl->typical_pkt_sz);
387
388	return mlx5_rl_add_rate_raw(dev, rl_raw,
389				    MLX5_CAP_QOS(dev, packet_pacing_uid) ?
390					MLX5_SHARED_RESOURCE_UID : 0,
391				    false, index);
392}
393EXPORT_SYMBOL(mlx5_rl_add_rate);
394
395void mlx5_rl_remove_rate(struct mlx5_core_dev *dev, struct mlx5_rate_limit *rl)
396{
397	u8 rl_raw[MLX5_ST_SZ_BYTES(set_pp_rate_limit_context)] = {};
398	struct mlx5_rl_table *table = &dev->priv.rl_table;
399	struct mlx5_rl_entry *entry = NULL;
400
401	/* 0 is a reserved value for unlimited rate */
402	if (rl->rate == 0)
403		return;
404
405	MLX5_SET(set_pp_rate_limit_context, rl_raw, rate_limit, rl->rate);
406	MLX5_SET(set_pp_rate_limit_context, rl_raw, burst_upper_bound,
407		 rl->max_burst_sz);
408	MLX5_SET(set_pp_rate_limit_context, rl_raw, typical_packet_size,
409		 rl->typical_pkt_sz);
410
411	mutex_lock(&table->rl_lock);
412	entry = find_rl_entry(table, rl_raw,
413			      MLX5_CAP_QOS(dev, packet_pacing_uid) ?
414				MLX5_SHARED_RESOURCE_UID : 0, false);
415	if (!entry || !entry->refcount) {
416		mlx5_core_warn(dev, "Rate %u, max_burst_sz %u typical_pkt_sz %u are not configured\n",
417			       rl->rate, rl->max_burst_sz, rl->typical_pkt_sz);
418		goto out;
419	}
420	mlx5_rl_entry_put(dev, entry);
421	mlx5_rl_table_put(table);
422out:
423	mutex_unlock(&table->rl_lock);
424}
425EXPORT_SYMBOL(mlx5_rl_remove_rate);
426
427int mlx5_init_rl_table(struct mlx5_core_dev *dev)
428{
429	struct mlx5_rl_table *table = &dev->priv.rl_table;
430
431	if (!MLX5_CAP_GEN(dev, qos) || !MLX5_CAP_QOS(dev, packet_pacing)) {
432		table->max_size = 0;
433		return 0;
434	}
435
436	mutex_init(&table->rl_lock);
437
438	/* First entry is reserved for unlimited rate */
439	table->max_size = MLX5_CAP_QOS(dev, packet_pacing_rate_table_size) - 1;
440	table->max_rate = MLX5_CAP_QOS(dev, packet_pacing_max_rate);
441	table->min_rate = MLX5_CAP_QOS(dev, packet_pacing_min_rate);
442
443	mlx5_core_info(dev, "Rate limit: %u rates are supported, range: %uMbps to %uMbps\n",
444		       table->max_size,
445		       table->min_rate >> 10,
446		       table->max_rate >> 10);
447
448	return 0;
449}
450
451void mlx5_cleanup_rl_table(struct mlx5_core_dev *dev)
452{
453	struct mlx5_rl_table *table = &dev->priv.rl_table;
454
455	if (!MLX5_CAP_GEN(dev, qos) || !MLX5_CAP_QOS(dev, packet_pacing))
456		return;
457
458	mlx5_rl_table_free(dev, table);
459	mutex_destroy(&table->rl_lock);
460}
~/ Read-only file preview.