File Index
Files and directories
Sign in
FILE

hw_table.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c C
  1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
  2/* Copyright (c) 2020 Mellanox Technologies Ltd */
  3#include <linux/mlx5/driver.h>
  4#include "vhca_event.h"
  5#include "priv.h"
  6#include "sf.h"
  7#include "mlx5_ifc_vhca_event.h"
  8#include "ecpf.h"
  9#include "mlx5_core.h"
 10#include "eswitch.h"
 11#include "diag/sf_tracepoint.h"
 12#include "devlink.h"
 13
 14struct mlx5_sf_hw {
 15	u32 usr_sfnum;
 16	u8 allocated: 1;
 17	u8 pending_delete: 1;
 18};
 19
 20struct mlx5_sf_hwc_table {
 21	struct mlx5_sf_hw *sfs;
 22	int max_fn;
 23	u16 start_fn_id;
 24};
 25
 26enum mlx5_sf_hwc_index {
 27	MLX5_SF_HWC_LOCAL,
 28	MLX5_SF_HWC_EXTERNAL,
 29	MLX5_SF_HWC_MAX,
 30};
 31
 32struct mlx5_sf_hw_table {
 33	struct mutex table_lock; /* Serializes sf deletion and vhca state change handler. */
 34	struct mlx5_sf_hwc_table hwc[MLX5_SF_HWC_MAX];
 35};
 36
 37static struct mlx5_sf_hwc_table *
 38mlx5_sf_controller_to_hwc(struct mlx5_core_dev *dev, u32 controller)
 39{
 40	int idx = !!controller;
 41
 42	return &dev->priv.sf_hw_table->hwc[idx];
 43}
 44
 45u16 mlx5_sf_sw_to_hw_id(struct mlx5_core_dev *dev, u32 controller, u16 sw_id)
 46{
 47	struct mlx5_sf_hwc_table *hwc;
 48
 49	hwc = mlx5_sf_controller_to_hwc(dev, controller);
 50	return hwc->start_fn_id + sw_id;
 51}
 52
 53static u16 mlx5_sf_hw_to_sw_id(struct mlx5_sf_hwc_table *hwc, u16 hw_id)
 54{
 55	return hw_id - hwc->start_fn_id;
 56}
 57
 58static struct mlx5_sf_hwc_table *
 59mlx5_sf_table_fn_to_hwc(struct mlx5_sf_hw_table *table, u16 fn_id)
 60{
 61	int i;
 62
 63	for (i = 0; i < ARRAY_SIZE(table->hwc); i++) {
 64		if (table->hwc[i].max_fn &&
 65		    fn_id >= table->hwc[i].start_fn_id &&
 66		    fn_id < (table->hwc[i].start_fn_id + table->hwc[i].max_fn))
 67			return &table->hwc[i];
 68	}
 69	return NULL;
 70}
 71
 72static int mlx5_sf_hw_table_id_alloc(struct mlx5_core_dev *dev,
 73				     struct mlx5_sf_hw_table *table,
 74				     u32 controller,
 75				     u32 usr_sfnum)
 76{
 77	struct mlx5_sf_hwc_table *hwc;
 78	int free_idx = -1;
 79	int i;
 80
 81	hwc = mlx5_sf_controller_to_hwc(dev, controller);
 82	if (!hwc->sfs)
 83		return -ENOSPC;
 84
 85	for (i = 0; i < hwc->max_fn; i++) {
 86		if (!hwc->sfs[i].allocated && free_idx == -1) {
 87			free_idx = i;
 88			continue;
 89		}
 90
 91		if (hwc->sfs[i].allocated && hwc->sfs[i].usr_sfnum == usr_sfnum)
 92			return -EEXIST;
 93	}
 94
 95	if (free_idx == -1)
 96		return -ENOSPC;
 97
 98	hwc->sfs[free_idx].usr_sfnum = usr_sfnum;
 99	hwc->sfs[free_idx].allocated = true;
100	return free_idx;
101}
102
103static void mlx5_sf_hw_table_id_free(struct mlx5_core_dev *dev,
104				     struct mlx5_sf_hw_table *table,
105				     u32 controller, int id)
106{
107	struct mlx5_sf_hwc_table *hwc;
108
109	hwc = mlx5_sf_controller_to_hwc(dev, controller);
110	hwc->sfs[id].allocated = false;
111	hwc->sfs[id].pending_delete = false;
112}
113
114int mlx5_sf_hw_table_sf_alloc(struct mlx5_core_dev *dev, u32 controller, u32 usr_sfnum)
115{
116	struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table;
117	u16 hw_fn_id;
118	int sw_id;
119	int err;
120
121	if (!table)
122		return -EOPNOTSUPP;
123
124	mutex_lock(&table->table_lock);
125	sw_id = mlx5_sf_hw_table_id_alloc(dev, table, controller, usr_sfnum);
126	if (sw_id < 0) {
127		err = sw_id;
128		goto exist_err;
129	}
130
131	hw_fn_id = mlx5_sf_sw_to_hw_id(dev, controller, sw_id);
132	err = mlx5_cmd_alloc_sf(dev, hw_fn_id);
133	if (err)
134		goto err;
135
136	err = mlx5_modify_vhca_sw_id(dev, hw_fn_id, usr_sfnum);
137	if (err)
138		goto vhca_err;
139
140	if (controller) {
141		/* If this SF is for external controller, SF manager
142		 * needs to arm firmware to receive the events.
143		 */
144		err = mlx5_vhca_event_arm(dev, hw_fn_id);
145		if (err)
146			goto vhca_err;
147	}
148
149	trace_mlx5_sf_hwc_alloc(dev, controller, hw_fn_id, usr_sfnum);
150	mutex_unlock(&table->table_lock);
151	return sw_id;
152
153vhca_err:
154	mlx5_cmd_dealloc_sf(dev, hw_fn_id);
155err:
156	mlx5_sf_hw_table_id_free(dev, table, controller, sw_id);
157exist_err:
158	mutex_unlock(&table->table_lock);
159	return err;
160}
161
162void mlx5_sf_hw_table_sf_free(struct mlx5_core_dev *dev, u32 controller, u16 id)
163{
164	struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table;
165	u16 hw_fn_id;
166
167	mutex_lock(&table->table_lock);
168	hw_fn_id = mlx5_sf_sw_to_hw_id(dev, controller, id);
169	mlx5_cmd_dealloc_sf(dev, hw_fn_id);
170	mlx5_sf_hw_table_id_free(dev, table, controller, id);
171	mutex_unlock(&table->table_lock);
172}
173
174static void mlx5_sf_hw_table_hwc_sf_free(struct mlx5_core_dev *dev,
175					 struct mlx5_sf_hwc_table *hwc, int idx)
176{
177	mlx5_cmd_dealloc_sf(dev, hwc->start_fn_id + idx);
178	hwc->sfs[idx].allocated = false;
179	hwc->sfs[idx].pending_delete = false;
180	trace_mlx5_sf_hwc_free(dev, hwc->start_fn_id + idx);
181}
182
183void mlx5_sf_hw_table_sf_deferred_free(struct mlx5_core_dev *dev, u32 controller, u16 id)
184{
185	struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table;
186	u32 out[MLX5_ST_SZ_DW(query_vhca_state_out)] = {};
187	struct mlx5_sf_hwc_table *hwc;
188	u16 hw_fn_id;
189	u8 state;
190	int err;
191
192	hw_fn_id = mlx5_sf_sw_to_hw_id(dev, controller, id);
193	hwc = mlx5_sf_controller_to_hwc(dev, controller);
194	mutex_lock(&table->table_lock);
195	err = mlx5_cmd_query_vhca_state(dev, hw_fn_id, out, sizeof(out));
196	if (err)
197		goto err;
198	state = MLX5_GET(query_vhca_state_out, out, vhca_state_context.vhca_state);
199	if (state == MLX5_VHCA_STATE_ALLOCATED) {
200		mlx5_cmd_dealloc_sf(dev, hw_fn_id);
201		hwc->sfs[id].allocated = false;
202	} else {
203		hwc->sfs[id].pending_delete = true;
204		trace_mlx5_sf_hwc_deferred_free(dev, hw_fn_id);
205	}
206err:
207	mutex_unlock(&table->table_lock);
208}
209
210static void mlx5_sf_hw_table_hwc_dealloc_all(struct mlx5_core_dev *dev,
211					     struct mlx5_sf_hwc_table *hwc)
212{
213	int i;
214
215	for (i = 0; i < hwc->max_fn; i++) {
216		if (hwc->sfs[i].allocated)
217			mlx5_sf_hw_table_hwc_sf_free(dev, hwc, i);
218	}
219}
220
221static void mlx5_sf_hw_table_dealloc_all(struct mlx5_core_dev *dev,
222					 struct mlx5_sf_hw_table *table)
223{
224	mlx5_sf_hw_table_hwc_dealloc_all(dev,
225					 &table->hwc[MLX5_SF_HWC_EXTERNAL]);
226	mlx5_sf_hw_table_hwc_dealloc_all(dev, &table->hwc[MLX5_SF_HWC_LOCAL]);
227}
228
229static int mlx5_sf_hw_table_hwc_init(struct mlx5_sf_hwc_table *hwc, u16 max_fn, u16 base_id)
230{
231	struct mlx5_sf_hw *sfs;
232
233	if (!max_fn)
234		return 0;
235
236	sfs = kcalloc(max_fn, sizeof(*sfs), GFP_KERNEL);
237	if (!sfs)
238		return -ENOMEM;
239
240	hwc->sfs = sfs;
241	hwc->max_fn = max_fn;
242	hwc->start_fn_id = base_id;
243	return 0;
244}
245
246static void mlx5_sf_hw_table_hwc_cleanup(struct mlx5_sf_hwc_table *hwc)
247{
248	kfree(hwc->sfs);
249}
250
251static void mlx5_sf_hw_table_res_unregister(struct mlx5_core_dev *dev)
252{
253	devl_resources_unregister(priv_to_devlink(dev));
254}
255
256static int mlx5_sf_hw_table_res_register(struct mlx5_core_dev *dev, u16 max_fn,
257					 u16 max_ext_fn)
258{
259	struct devlink_resource_size_params size_params;
260	struct devlink *devlink = priv_to_devlink(dev);
261	int err;
262
263	devlink_resource_size_params_init(&size_params, max_fn, max_fn, 1,
264					  DEVLINK_RESOURCE_UNIT_ENTRY);
265	err = devl_resource_register(devlink, "max_local_SFs", max_fn, MLX5_DL_RES_MAX_LOCAL_SFS,
266				     DEVLINK_RESOURCE_ID_PARENT_TOP, &size_params);
267	if (err)
268		return err;
269
270	devlink_resource_size_params_init(&size_params, max_ext_fn, max_ext_fn, 1,
271					  DEVLINK_RESOURCE_UNIT_ENTRY);
272	return devl_resource_register(devlink, "max_external_SFs", max_ext_fn,
273				      MLX5_DL_RES_MAX_EXTERNAL_SFS, DEVLINK_RESOURCE_ID_PARENT_TOP,
274				      &size_params);
275}
276
277int mlx5_sf_hw_table_init(struct mlx5_core_dev *dev)
278{
279	struct mlx5_sf_hw_table *table;
280	u16 max_ext_fn = 0;
281	u16 ext_base_id = 0;
282	u16 base_id;
283	u16 max_fn;
284	int err;
285
286	if (!mlx5_vhca_event_supported(dev))
287		return 0;
288
289	max_fn = mlx5_sf_max_functions(dev);
290
291	err = mlx5_esw_sf_max_hpf_functions(dev, &max_ext_fn, &ext_base_id);
292	if (err)
293		return err;
294
295	if (mlx5_sf_hw_table_res_register(dev, max_fn, max_ext_fn))
296		mlx5_core_dbg(dev, "failed to register max SFs resources");
297
298	if (!max_fn && !max_ext_fn)
299		return 0;
300
301	table = kzalloc(sizeof(*table), GFP_KERNEL);
302	if (!table) {
303		err = -ENOMEM;
304		goto alloc_err;
305	}
306
307	mutex_init(&table->table_lock);
308	dev->priv.sf_hw_table = table;
309
310	base_id = mlx5_sf_start_function_id(dev);
311	err = mlx5_sf_hw_table_hwc_init(&table->hwc[MLX5_SF_HWC_LOCAL], max_fn, base_id);
312	if (err)
313		goto table_err;
314
315	err = mlx5_sf_hw_table_hwc_init(&table->hwc[MLX5_SF_HWC_EXTERNAL],
316					max_ext_fn, ext_base_id);
317	if (err)
318		goto ext_err;
319
320	mlx5_core_dbg(dev, "SF HW table: max sfs = %d, ext sfs = %d\n", max_fn, max_ext_fn);
321	return 0;
322
323ext_err:
324	mlx5_sf_hw_table_hwc_cleanup(&table->hwc[MLX5_SF_HWC_LOCAL]);
325table_err:
326	mutex_destroy(&table->table_lock);
327	kfree(table);
328alloc_err:
329	mlx5_sf_hw_table_res_unregister(dev);
330	return err;
331}
332
333void mlx5_sf_hw_table_cleanup(struct mlx5_core_dev *dev)
334{
335	struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table;
336
337	if (!table)
338		goto res_unregister;
339
340	mlx5_sf_hw_table_hwc_cleanup(&table->hwc[MLX5_SF_HWC_EXTERNAL]);
341	mlx5_sf_hw_table_hwc_cleanup(&table->hwc[MLX5_SF_HWC_LOCAL]);
342	mutex_destroy(&table->table_lock);
343	kfree(table);
344	dev->priv.sf_hw_table = NULL;
345res_unregister:
346	mlx5_sf_hw_table_res_unregister(dev);
347}
348
349static int mlx5_sf_hw_vhca_event(struct notifier_block *nb, unsigned long opcode, void *data)
350{
351	struct mlx5_core_dev *dev = container_of(nb, struct mlx5_core_dev,
352						 priv.sf_hw_table_vhca_nb);
353	struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table;
354	const struct mlx5_vhca_state_event *event = data;
355	struct mlx5_sf_hwc_table *hwc;
356	struct mlx5_sf_hw *sf_hw;
357	u16 sw_id;
358
359	if (!table || event->new_vhca_state != MLX5_VHCA_STATE_ALLOCATED)
360		return 0;
361
362	hwc = mlx5_sf_table_fn_to_hwc(table, event->function_id);
363	if (!hwc)
364		return 0;
365
366	sw_id = mlx5_sf_hw_to_sw_id(hwc, event->function_id);
367	sf_hw = &hwc->sfs[sw_id];
368
369	mutex_lock(&table->table_lock);
370	/* SF driver notified through firmware that SF is finally detached.
371	 * Hence recycle the sf hardware id for reuse.
372	 */
373	if (sf_hw->allocated && sf_hw->pending_delete)
374		mlx5_sf_hw_table_hwc_sf_free(dev, hwc, sw_id);
375	mutex_unlock(&table->table_lock);
376	return 0;
377}
378
379int mlx5_sf_hw_notifier_init(struct mlx5_core_dev *dev)
380{
381	if (mlx5_core_is_sf(dev))
382		return 0;
383
384	dev->priv.sf_hw_table_vhca_nb.notifier_call = mlx5_sf_hw_vhca_event;
385	return mlx5_vhca_event_notifier_register(dev,
386						 &dev->priv.sf_hw_table_vhca_nb);
387}
388
389void mlx5_sf_hw_notifier_cleanup(struct mlx5_core_dev *dev)
390{
391	if (mlx5_core_is_sf(dev))
392		return;
393
394	mlx5_vhca_event_notifier_unregister(dev,
395					    &dev->priv.sf_hw_table_vhca_nb);
396}
397
398void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev)
399{
400	struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table;
401
402	if (!table)
403		return;
404
405	/* Dealloc SFs whose firmware event has been missed. */
406	mlx5_sf_hw_table_dealloc_all(dev, table);
407}
408
409bool mlx5_sf_hw_table_supported(const struct mlx5_core_dev *dev)
410{
411	return !!dev->priv.sf_hw_table;
412}
~/ Read-only file preview.