File Index
Files and directories
Sign in
FILE

dev.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c C
  1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
  2/* Copyright (c) 2020 Mellanox Technologies Ltd */
  3
  4#include <linux/mlx5/driver.h>
  5#include <linux/mlx5/device.h>
  6#include "mlx5_core.h"
  7#include "dev.h"
  8#include "sf/vhca_event.h"
  9#include "sf/sf.h"
 10#include "sf/mlx5_ifc_vhca_event.h"
 11#include "ecpf.h"
 12#define CREATE_TRACE_POINTS
 13#include "diag/dev_tracepoint.h"
 14
 15struct mlx5_sf_dev_table {
 16	struct xarray devices;
 17	phys_addr_t base_address;
 18	u64 sf_bar_length;
 19	struct workqueue_struct *active_wq;
 20	struct work_struct work;
 21	u8 stop_active_wq:1;
 22	struct mlx5_core_dev *dev;
 23};
 24
 25struct mlx5_sf_dev_active_work_ctx {
 26	struct work_struct work;
 27	struct mlx5_vhca_state_event event;
 28	struct mlx5_sf_dev_table *table;
 29	int sf_index;
 30};
 31
 32static bool mlx5_sf_dev_supported(const struct mlx5_core_dev *dev)
 33{
 34	return MLX5_CAP_GEN(dev, sf) && mlx5_vhca_event_supported(dev);
 35}
 36
 37bool mlx5_sf_dev_allocated(const struct mlx5_core_dev *dev)
 38{
 39	struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table;
 40
 41	return table && !xa_empty(&table->devices);
 42}
 43
 44static ssize_t sfnum_show(struct device *dev, struct device_attribute *attr, char *buf)
 45{
 46	struct auxiliary_device *adev = container_of(dev, struct auxiliary_device, dev);
 47	struct mlx5_sf_dev *sf_dev = container_of(adev, struct mlx5_sf_dev, adev);
 48
 49	return sysfs_emit(buf, "%u\n", sf_dev->sfnum);
 50}
 51static DEVICE_ATTR_RO(sfnum);
 52
 53static struct attribute *sf_device_attrs[] = {
 54	&dev_attr_sfnum.attr,
 55	NULL,
 56};
 57
 58static const struct attribute_group sf_attr_group = {
 59	.attrs = sf_device_attrs,
 60};
 61
 62static const struct attribute_group *sf_attr_groups[2] = {
 63	&sf_attr_group,
 64	NULL
 65};
 66
 67static void mlx5_sf_dev_release(struct device *device)
 68{
 69	struct auxiliary_device *adev = container_of(device, struct auxiliary_device, dev);
 70	struct mlx5_sf_dev *sf_dev = container_of(adev, struct mlx5_sf_dev, adev);
 71
 72	mlx5_adev_idx_free(adev->id);
 73	kfree(sf_dev);
 74}
 75
 76static void mlx5_sf_dev_remove_aux(struct mlx5_core_dev *dev,
 77				   struct mlx5_sf_dev *sf_dev)
 78{
 79	int id;
 80
 81	id = sf_dev->adev.id;
 82	trace_mlx5_sf_dev_del(dev, sf_dev, id);
 83
 84	auxiliary_device_delete(&sf_dev->adev);
 85	auxiliary_device_uninit(&sf_dev->adev);
 86}
 87
 88static void mlx5_sf_dev_add(struct mlx5_core_dev *dev, u16 sf_index, u16 fn_id, u32 sfnum)
 89{
 90	struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table;
 91	struct mlx5_sf_dev *sf_dev;
 92	struct pci_dev *pdev;
 93	int err;
 94	int id;
 95
 96	id = mlx5_adev_idx_alloc();
 97	if (id < 0) {
 98		err = id;
 99		goto add_err;
100	}
101
102	sf_dev = kzalloc(sizeof(*sf_dev), GFP_KERNEL);
103	if (!sf_dev) {
104		mlx5_adev_idx_free(id);
105		err = -ENOMEM;
106		goto add_err;
107	}
108	pdev = dev->pdev;
109	sf_dev->adev.id = id;
110	sf_dev->adev.name = MLX5_SF_DEV_ID_NAME;
111	sf_dev->adev.dev.release = mlx5_sf_dev_release;
112	sf_dev->adev.dev.parent = &pdev->dev;
113	sf_dev->adev.dev.groups = sf_attr_groups;
114	sf_dev->sfnum = sfnum;
115	sf_dev->parent_mdev = dev;
116	sf_dev->fn_id = fn_id;
117
118	sf_dev->bar_base_addr = table->base_address + (sf_index * table->sf_bar_length);
119
120	trace_mlx5_sf_dev_add(dev, sf_dev, id);
121
122	err = auxiliary_device_init(&sf_dev->adev);
123	if (err) {
124		mlx5_adev_idx_free(id);
125		kfree(sf_dev);
126		goto add_err;
127	}
128
129	err = auxiliary_device_add(&sf_dev->adev);
130	if (err) {
131		auxiliary_device_uninit(&sf_dev->adev);
132		goto add_err;
133	}
134
135	err = xa_insert(&table->devices, sf_index, sf_dev, GFP_KERNEL);
136	if (err)
137		goto xa_err;
138	return;
139
140xa_err:
141	mlx5_sf_dev_remove_aux(dev, sf_dev);
142add_err:
143	mlx5_core_err(dev, "SF DEV: fail device add for index=%d sfnum=%d err=%d\n",
144		      sf_index, sfnum, err);
145}
146
147static void mlx5_sf_dev_del(struct mlx5_core_dev *dev, struct mlx5_sf_dev *sf_dev, u16 sf_index)
148{
149	struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table;
150
151	xa_erase(&table->devices, sf_index);
152	mlx5_sf_dev_remove_aux(dev, sf_dev);
153}
154
155static int
156mlx5_sf_dev_state_change_handler(struct notifier_block *nb, unsigned long event_code, void *data)
157{
158	struct mlx5_core_dev *dev = container_of(nb, struct mlx5_core_dev,
159						 priv.sf_dev_nb);
160	struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table;
161	const struct mlx5_vhca_state_event *event = data;
162	struct mlx5_sf_dev *sf_dev;
163	u16 max_functions;
164	u16 sf_index;
165	u16 base_id;
166
167	if (!table)
168		return 0;
169
170	max_functions = mlx5_sf_max_functions(dev);
171	if (!max_functions)
172		return 0;
173
174	base_id = mlx5_sf_start_function_id(dev);
175	if (event->function_id < base_id || event->function_id >= (base_id + max_functions))
176		return 0;
177
178	sf_index = event->function_id - base_id;
179	sf_dev = xa_load(&table->devices, sf_index);
180	switch (event->new_vhca_state) {
181	case MLX5_VHCA_STATE_INVALID:
182	case MLX5_VHCA_STATE_ALLOCATED:
183		if (sf_dev)
184			mlx5_sf_dev_del(dev, sf_dev, sf_index);
185		break;
186	case MLX5_VHCA_STATE_TEARDOWN_REQUEST:
187		if (sf_dev)
188			mlx5_sf_dev_del(dev, sf_dev, sf_index);
189		else
190			mlx5_core_err(dev,
191				      "SF DEV: teardown state for invalid dev index=%d sfnum=0x%x\n",
192				      sf_index, event->sw_function_id);
193		break;
194	case MLX5_VHCA_STATE_ACTIVE:
195		if (!sf_dev)
196			mlx5_sf_dev_add(dev, sf_index, event->function_id,
197					event->sw_function_id);
198		break;
199	default:
200		break;
201	}
202	return 0;
203}
204
205static int mlx5_sf_dev_vhca_arm_all(struct mlx5_sf_dev_table *table)
206{
207	struct mlx5_core_dev *dev = table->dev;
208	u16 max_functions;
209	u16 function_id;
210	int err = 0;
211	int i;
212
213	max_functions = mlx5_sf_max_functions(dev);
214	function_id = mlx5_sf_start_function_id(dev);
215	/* Arm the vhca context as the vhca event notifier */
216	for (i = 0; i < max_functions; i++) {
217		err = mlx5_vhca_event_arm(dev, function_id);
218		if (err)
219			return err;
220
221		function_id++;
222	}
223	return 0;
224}
225
226static void mlx5_sf_dev_add_active_work(struct work_struct *_work)
227{
228	struct mlx5_sf_dev_active_work_ctx *work_ctx;
229
230	work_ctx = container_of(_work, struct mlx5_sf_dev_active_work_ctx, work);
231	if (work_ctx->table->stop_active_wq)
232		goto out;
233	/* Don't probe device which is already probe */
234	if (!xa_load(&work_ctx->table->devices, work_ctx->sf_index))
235		mlx5_sf_dev_add(work_ctx->table->dev, work_ctx->sf_index,
236				work_ctx->event.function_id, work_ctx->event.sw_function_id);
237	/* There is a race where SF got inactive after the query
238	 * above. e.g.: the query returns that the state of the
239	 * SF is active, and after that the eswitch manager set it to
240	 * inactive.
241	 * This case cannot be managed in SW, since the probing of the
242	 * SF is on one system, and the inactivation is on a different
243	 * system.
244	 * If the inactive is done after the SF perform init_hca(),
245	 * the SF will fully probe and then removed. If it was
246	 * done before init_hca(), the SF probe will fail.
247	 */
248out:
249	kfree(work_ctx);
250}
251
252/* In case SFs are generated externally, probe active SFs */
253static void mlx5_sf_dev_queue_active_works(struct work_struct *_work)
254{
255	struct mlx5_sf_dev_table *table = container_of(_work, struct mlx5_sf_dev_table, work);
256	u32 out[MLX5_ST_SZ_DW(query_vhca_state_out)] = {};
257	struct mlx5_sf_dev_active_work_ctx *work_ctx;
258	struct mlx5_core_dev *dev = table->dev;
259	u16 max_functions;
260	u16 function_id;
261	u16 sw_func_id;
262	int err = 0;
263	int wq_idx;
264	u8 state;
265	int i;
266
267	max_functions = mlx5_sf_max_functions(dev);
268	function_id = mlx5_sf_start_function_id(dev);
269	for (i = 0; i < max_functions; i++, function_id++) {
270		if (table->stop_active_wq)
271			return;
272		err = mlx5_cmd_query_vhca_state(dev, function_id, out, sizeof(out));
273		if (err)
274			/* A failure of specific vhca doesn't mean others will
275			 * fail as well.
276			 */
277			continue;
278		state = MLX5_GET(query_vhca_state_out, out, vhca_state_context.vhca_state);
279		if (state != MLX5_VHCA_STATE_ACTIVE)
280			continue;
281
282		sw_func_id = MLX5_GET(query_vhca_state_out, out, vhca_state_context.sw_function_id);
283		work_ctx = kzalloc(sizeof(*work_ctx), GFP_KERNEL);
284		if (!work_ctx)
285			return;
286
287		INIT_WORK(&work_ctx->work, &mlx5_sf_dev_add_active_work);
288		work_ctx->event.function_id = function_id;
289		work_ctx->event.sw_function_id = sw_func_id;
290		work_ctx->table = table;
291		work_ctx->sf_index = i;
292		wq_idx = work_ctx->event.function_id % MLX5_DEV_MAX_WQS;
293		mlx5_vhca_events_work_enqueue(dev, wq_idx, &work_ctx->work);
294	}
295}
296
297/* In case SFs are generated externally, probe active SFs */
298static int mlx5_sf_dev_create_active_works(struct mlx5_sf_dev_table *table)
299{
300	if (MLX5_CAP_GEN(table->dev, eswitch_manager))
301		return 0; /* the table is local */
302
303	/* Use a workqueue to probe active SFs, which are in large
304	 * quantity and may take up to minutes to probe.
305	 */
306	table->active_wq = create_singlethread_workqueue("mlx5_active_sf");
307	if (!table->active_wq)
308		return -ENOMEM;
309	INIT_WORK(&table->work, &mlx5_sf_dev_queue_active_works);
310	queue_work(table->active_wq, &table->work);
311	return 0;
312}
313
314static void mlx5_sf_dev_destroy_active_works(struct mlx5_sf_dev_table *table)
315{
316	if (table->active_wq) {
317		table->stop_active_wq = true;
318		destroy_workqueue(table->active_wq);
319	}
320}
321
322int mlx5_sf_dev_notifier_init(struct mlx5_core_dev *dev)
323{
324	if (mlx5_core_is_sf(dev))
325		return 0;
326
327	dev->priv.sf_dev_nb.notifier_call = mlx5_sf_dev_state_change_handler;
328	return mlx5_vhca_event_notifier_register(dev, &dev->priv.sf_dev_nb);
329}
330
331void mlx5_sf_dev_table_create(struct mlx5_core_dev *dev)
332{
333	struct mlx5_sf_dev_table *table;
334	int err;
335
336	if (!mlx5_sf_dev_supported(dev))
337		return;
338
339	table = kzalloc(sizeof(*table), GFP_KERNEL);
340	if (!table) {
341		err = -ENOMEM;
342		goto table_err;
343	}
344
345	table->dev = dev;
346	table->sf_bar_length = 1 << (MLX5_CAP_GEN(dev, log_min_sf_size) + 12);
347	table->base_address = pci_resource_start(dev->pdev, 2);
348	xa_init(&table->devices);
349	dev->priv.sf_dev_table = table;
350
351	err = mlx5_sf_dev_create_active_works(table);
352	if (err)
353		goto add_active_err;
354
355	err = mlx5_sf_dev_vhca_arm_all(table);
356	if (err)
357		goto arm_err;
358	return;
359
360arm_err:
361	mlx5_sf_dev_destroy_active_works(table);
362	mlx5_vhca_event_work_queues_flush(dev);
363add_active_err:
364	kfree(table);
365	dev->priv.sf_dev_table = NULL;
366table_err:
367	mlx5_core_err(dev, "SF DEV table create err = %d\n", err);
368}
369
370static void mlx5_sf_dev_destroy_all(struct mlx5_sf_dev_table *table)
371{
372	struct mlx5_sf_dev *sf_dev;
373	unsigned long index;
374
375	xa_for_each(&table->devices, index, sf_dev) {
376		xa_erase(&table->devices, index);
377		mlx5_sf_dev_remove_aux(table->dev, sf_dev);
378	}
379}
380
381void mlx5_sf_dev_notifier_cleanup(struct mlx5_core_dev *dev)
382{
383	if (mlx5_core_is_sf(dev))
384		return;
385
386	mlx5_vhca_event_notifier_unregister(dev, &dev->priv.sf_dev_nb);
387}
388
389void mlx5_sf_dev_table_destroy(struct mlx5_core_dev *dev)
390{
391	struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table;
392
393	if (!table)
394		return;
395
396	mlx5_sf_dev_destroy_active_works(table);
397
398	/* Now that event handler is not running, it is safe to destroy
399	 * the sf device without race.
400	 */
401	mlx5_sf_dev_destroy_all(table);
402
403	WARN_ON(!xa_empty(&table->devices));
404	kfree(table);
405	dev->priv.sf_dev_table = NULL;
406}
~/ Read-only file preview.