xref: /dpdk/drivers/common/mlx5/mlx5_common_pci.c (revision 99a2dd955fba6e4cc23b77d590a033650ced9c45)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2020 Mellanox Technologies Ltd
3  */
4 
5 #include <stdlib.h>
6 #include <rte_malloc.h>
7 #include "mlx5_common_utils.h"
8 #include "mlx5_common_pci.h"
9 
10 struct mlx5_pci_device {
11 	struct rte_pci_device *pci_dev;
12 	TAILQ_ENTRY(mlx5_pci_device) next;
13 	uint32_t classes_loaded;
14 };
15 
16 /* Head of list of drivers. */
17 static TAILQ_HEAD(mlx5_pci_bus_drv_head, mlx5_pci_driver) drv_list =
18 				TAILQ_HEAD_INITIALIZER(drv_list);
19 
20 /* Head of mlx5 pci devices. */
21 static TAILQ_HEAD(mlx5_pci_devices_head, mlx5_pci_device) devices_list =
22 				TAILQ_HEAD_INITIALIZER(devices_list);
23 
24 static const struct {
25 	const char *name;
26 	unsigned int driver_class;
27 } mlx5_classes[] = {
28 	{ .name = "vdpa", .driver_class = MLX5_CLASS_VDPA },
29 	{ .name = "net", .driver_class = MLX5_CLASS_NET },
30 	{ .name = "regex", .driver_class = MLX5_CLASS_REGEX },
31 	{ .name = "compress", .driver_class = MLX5_CLASS_COMPRESS },
32 };
33 
34 static const unsigned int mlx5_class_combinations[] = {
35 	MLX5_CLASS_NET,
36 	MLX5_CLASS_VDPA,
37 	MLX5_CLASS_REGEX,
38 	MLX5_CLASS_COMPRESS,
39 	MLX5_CLASS_NET | MLX5_CLASS_REGEX,
40 	MLX5_CLASS_VDPA | MLX5_CLASS_REGEX,
41 	MLX5_CLASS_NET | MLX5_CLASS_COMPRESS,
42 	MLX5_CLASS_VDPA | MLX5_CLASS_COMPRESS,
43 	MLX5_CLASS_REGEX | MLX5_CLASS_COMPRESS,
44 	MLX5_CLASS_NET | MLX5_CLASS_REGEX | MLX5_CLASS_COMPRESS,
45 	MLX5_CLASS_VDPA | MLX5_CLASS_REGEX | MLX5_CLASS_COMPRESS,
46 	/* New class combination should be added here. */
47 };
48 
49 static int
50 class_name_to_value(const char *class_name)
51 {
52 	unsigned int i;
53 
54 	for (i = 0; i < RTE_DIM(mlx5_classes); i++) {
55 		if (strcmp(class_name, mlx5_classes[i].name) == 0)
56 			return mlx5_classes[i].driver_class;
57 	}
58 	return -EINVAL;
59 }
60 
61 static struct mlx5_pci_driver *
62 driver_get(uint32_t class)
63 {
64 	struct mlx5_pci_driver *driver;
65 
66 	TAILQ_FOREACH(driver, &drv_list, next) {
67 		if (driver->driver_class == class)
68 			return driver;
69 	}
70 	return NULL;
71 }
72 
73 static int
74 bus_cmdline_options_handler(__rte_unused const char *key,
75 			    const char *class_names, void *opaque)
76 {
77 	int *ret = opaque;
78 	char *nstr_org;
79 	int class_val;
80 	char *found;
81 	char *nstr;
82 	char *refstr = NULL;
83 
84 	*ret = 0;
85 	nstr = strdup(class_names);
86 	if (!nstr) {
87 		*ret = -ENOMEM;
88 		return *ret;
89 	}
90 	nstr_org = nstr;
91 	found = strtok_r(nstr, ":", &refstr);
92 	if (!found)
93 		goto err;
94 	do {
95 		/* Extract each individual class name. Multiple
96 		 * class key,value is supplied as class=net:vdpa:foo:bar.
97 		 */
98 		class_val = class_name_to_value(found);
99 		/* Check if its a valid class. */
100 		if (class_val < 0) {
101 			*ret = -EINVAL;
102 			goto err;
103 		}
104 		*ret |= class_val;
105 		found = strtok_r(NULL, ":", &refstr);
106 	} while (found);
107 err:
108 	free(nstr_org);
109 	if (*ret < 0)
110 		DRV_LOG(ERR, "Invalid mlx5 class options %s."
111 			" Maybe typo in device class argument setting?",
112 			class_names);
113 	return *ret;
114 }
115 
116 static int
117 parse_class_options(const struct rte_devargs *devargs)
118 {
119 	const char *key = MLX5_CLASS_ARG_NAME;
120 	struct rte_kvargs *kvlist;
121 	int ret = 0;
122 
123 	if (devargs == NULL)
124 		return 0;
125 	kvlist = rte_kvargs_parse(devargs->args, NULL);
126 	if (kvlist == NULL)
127 		return 0;
128 	if (rte_kvargs_count(kvlist, key))
129 		rte_kvargs_process(kvlist, key, bus_cmdline_options_handler,
130 				   &ret);
131 	rte_kvargs_free(kvlist);
132 	return ret;
133 }
134 
135 static bool
136 mlx5_bus_match(const struct mlx5_pci_driver *drv,
137 	       const struct rte_pci_device *pci_dev)
138 {
139 	const struct rte_pci_id *id_table;
140 
141 	for (id_table = drv->pci_driver.id_table; id_table->vendor_id != 0;
142 	     id_table++) {
143 		/* Check if device's ids match the class driver's ids. */
144 		if (id_table->vendor_id != pci_dev->id.vendor_id &&
145 		    id_table->vendor_id != RTE_PCI_ANY_ID)
146 			continue;
147 		if (id_table->device_id != pci_dev->id.device_id &&
148 		    id_table->device_id != RTE_PCI_ANY_ID)
149 			continue;
150 		if (id_table->subsystem_vendor_id !=
151 		    pci_dev->id.subsystem_vendor_id &&
152 		    id_table->subsystem_vendor_id != RTE_PCI_ANY_ID)
153 			continue;
154 		if (id_table->subsystem_device_id !=
155 		    pci_dev->id.subsystem_device_id &&
156 		    id_table->subsystem_device_id != RTE_PCI_ANY_ID)
157 			continue;
158 		if (id_table->class_id != pci_dev->id.class_id &&
159 		    id_table->class_id != RTE_CLASS_ANY_ID)
160 			continue;
161 		return true;
162 	}
163 	return false;
164 }
165 
166 static int
167 is_valid_class_combination(uint32_t user_classes)
168 {
169 	unsigned int i;
170 
171 	/* Verify if user specified valid supported combination. */
172 	for (i = 0; i < RTE_DIM(mlx5_class_combinations); i++) {
173 		if (mlx5_class_combinations[i] == user_classes)
174 			return 0;
175 	}
176 	/* Not found any valid class combination. */
177 	return -EINVAL;
178 }
179 
180 static struct mlx5_pci_device *
181 pci_to_mlx5_device(const struct rte_pci_device *pci_dev)
182 {
183 	struct mlx5_pci_device *dev;
184 
185 	TAILQ_FOREACH(dev, &devices_list, next) {
186 		if (dev->pci_dev == pci_dev)
187 			return dev;
188 	}
189 	return NULL;
190 }
191 
192 static bool
193 device_class_enabled(const struct mlx5_pci_device *device, uint32_t class)
194 {
195 	return (device->classes_loaded & class) ? true : false;
196 }
197 
198 static void
199 dev_release(struct mlx5_pci_device *dev)
200 {
201 	TAILQ_REMOVE(&devices_list, dev, next);
202 	rte_free(dev);
203 }
204 
205 static int
206 drivers_remove(struct mlx5_pci_device *dev, uint32_t enabled_classes)
207 {
208 	struct mlx5_pci_driver *driver;
209 	int local_ret = -ENODEV;
210 	unsigned int i = 0;
211 	int ret = 0;
212 
213 	enabled_classes &= dev->classes_loaded;
214 	while (enabled_classes) {
215 		driver = driver_get(RTE_BIT64(i));
216 		if (driver) {
217 			local_ret = driver->pci_driver.remove(dev->pci_dev);
218 			if (!local_ret)
219 				dev->classes_loaded &= ~RTE_BIT64(i);
220 			else if (ret == 0)
221 				ret = local_ret;
222 		}
223 		enabled_classes &= ~RTE_BIT64(i);
224 		i++;
225 	}
226 	if (local_ret)
227 		ret = local_ret;
228 	return ret;
229 }
230 
231 static int
232 drivers_probe(struct mlx5_pci_device *dev, struct rte_pci_driver *pci_drv,
233 	      struct rte_pci_device *pci_dev, uint32_t user_classes)
234 {
235 	struct mlx5_pci_driver *driver;
236 	uint32_t enabled_classes = 0;
237 	bool already_loaded;
238 	int ret;
239 
240 	TAILQ_FOREACH(driver, &drv_list, next) {
241 		if ((driver->driver_class & user_classes) == 0)
242 			continue;
243 		if (!mlx5_bus_match(driver, pci_dev))
244 			continue;
245 		already_loaded = dev->classes_loaded & driver->driver_class;
246 		if (already_loaded &&
247 		    !(driver->pci_driver.drv_flags & RTE_PCI_DRV_PROBE_AGAIN)) {
248 			DRV_LOG(ERR, "Device %s is already probed",
249 				pci_dev->device.name);
250 			ret = -EEXIST;
251 			goto probe_err;
252 		}
253 		ret = driver->pci_driver.probe(pci_drv, pci_dev);
254 		if (ret < 0) {
255 			DRV_LOG(ERR, "Failed to load driver %s",
256 				driver->pci_driver.driver.name);
257 			goto probe_err;
258 		}
259 		enabled_classes |= driver->driver_class;
260 	}
261 	dev->classes_loaded |= enabled_classes;
262 	return 0;
263 probe_err:
264 	/* Only unload drivers which are enabled which were enabled
265 	 * in this probe instance.
266 	 */
267 	drivers_remove(dev, enabled_classes);
268 	return ret;
269 }
270 
271 /**
272  * DPDK callback to register to probe multiple drivers for a PCI device.
273  *
274  * @param[in] pci_drv
275  *   PCI driver structure.
276  * @param[in] dev
277  *   PCI device information.
278  *
279  * @return
280  *   0 on success, a negative errno value otherwise and rte_errno is set.
281  */
282 static int
283 mlx5_common_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
284 		      struct rte_pci_device *pci_dev)
285 {
286 	struct mlx5_pci_device *dev;
287 	uint32_t user_classes = 0;
288 	bool new_device = false;
289 	int ret;
290 
291 	ret = parse_class_options(pci_dev->device.devargs);
292 	if (ret < 0)
293 		return ret;
294 	user_classes = ret;
295 	if (user_classes) {
296 		/* Validate combination here. */
297 		ret = is_valid_class_combination(user_classes);
298 		if (ret) {
299 			DRV_LOG(ERR, "Unsupported mlx5 classes supplied.");
300 			return ret;
301 		}
302 	} else {
303 		/* Default to net class. */
304 		user_classes = MLX5_CLASS_NET;
305 	}
306 	dev = pci_to_mlx5_device(pci_dev);
307 	if (!dev) {
308 		dev = rte_zmalloc("mlx5_pci_device", sizeof(*dev), 0);
309 		if (!dev)
310 			return -ENOMEM;
311 		dev->pci_dev = pci_dev;
312 		TAILQ_INSERT_HEAD(&devices_list, dev, next);
313 		new_device = true;
314 	}
315 	ret = drivers_probe(dev, pci_drv, pci_dev, user_classes);
316 	if (ret)
317 		goto class_err;
318 	return 0;
319 class_err:
320 	if (new_device)
321 		dev_release(dev);
322 	return ret;
323 }
324 
325 /**
326  * DPDK callback to remove one or more drivers for a PCI device.
327  *
328  * This function removes all drivers probed for a given PCI device.
329  *
330  * @param[in] pci_dev
331  *   Pointer to the PCI device.
332  *
333  * @return
334  *   0 on success, the function cannot fail.
335  */
336 static int
337 mlx5_common_pci_remove(struct rte_pci_device *pci_dev)
338 {
339 	struct mlx5_pci_device *dev;
340 	int ret;
341 
342 	dev = pci_to_mlx5_device(pci_dev);
343 	if (!dev)
344 		return -ENODEV;
345 	/* Matching device found, cleanup and unload drivers. */
346 	ret = drivers_remove(dev, dev->classes_loaded);
347 	if (!ret)
348 		dev_release(dev);
349 	return ret;
350 }
351 
352 static int
353 mlx5_common_pci_dma_map(struct rte_pci_device *pci_dev, void *addr,
354 			uint64_t iova, size_t len)
355 {
356 	struct mlx5_pci_driver *driver = NULL;
357 	struct mlx5_pci_driver *temp;
358 	struct mlx5_pci_device *dev;
359 	int ret = -EINVAL;
360 
361 	dev = pci_to_mlx5_device(pci_dev);
362 	if (!dev)
363 		return -ENODEV;
364 	TAILQ_FOREACH(driver, &drv_list, next) {
365 		if (device_class_enabled(dev, driver->driver_class) &&
366 		    driver->pci_driver.dma_map) {
367 			ret = driver->pci_driver.dma_map(pci_dev, addr,
368 							 iova, len);
369 			if (ret)
370 				goto map_err;
371 		}
372 	}
373 	return ret;
374 map_err:
375 	TAILQ_FOREACH(temp, &drv_list, next) {
376 		if (temp == driver)
377 			break;
378 		if (device_class_enabled(dev, temp->driver_class) &&
379 		    temp->pci_driver.dma_map && temp->pci_driver.dma_unmap)
380 			temp->pci_driver.dma_unmap(pci_dev, addr, iova, len);
381 	}
382 	return ret;
383 }
384 
385 static int
386 mlx5_common_pci_dma_unmap(struct rte_pci_device *pci_dev, void *addr,
387 			  uint64_t iova, size_t len)
388 {
389 	struct mlx5_pci_driver *driver;
390 	struct mlx5_pci_device *dev;
391 	int local_ret = -EINVAL;
392 	int ret;
393 
394 	dev = pci_to_mlx5_device(pci_dev);
395 	if (!dev)
396 		return -ENODEV;
397 	ret = 0;
398 	/* There is no unmap error recovery in current implementation. */
399 	TAILQ_FOREACH_REVERSE(driver, &drv_list, mlx5_pci_bus_drv_head, next) {
400 		if (device_class_enabled(dev, driver->driver_class) &&
401 		    driver->pci_driver.dma_unmap) {
402 			local_ret = driver->pci_driver.dma_unmap(pci_dev, addr,
403 								 iova, len);
404 			if (local_ret && (ret == 0))
405 				ret = local_ret;
406 		}
407 	}
408 	if (local_ret)
409 		ret = local_ret;
410 	return ret;
411 }
412 
413 /* PCI ID table is build dynamically based on registered mlx5 drivers. */
414 static struct rte_pci_id *mlx5_pci_id_table;
415 
416 static struct rte_pci_driver mlx5_pci_driver = {
417 	.driver = {
418 		.name = MLX5_PCI_DRIVER_NAME,
419 	},
420 	.probe = mlx5_common_pci_probe,
421 	.remove = mlx5_common_pci_remove,
422 	.dma_map = mlx5_common_pci_dma_map,
423 	.dma_unmap = mlx5_common_pci_dma_unmap,
424 };
425 
426 static int
427 pci_id_table_size_get(const struct rte_pci_id *id_table)
428 {
429 	int table_size = 0;
430 
431 	for (; id_table->vendor_id != 0; id_table++)
432 		table_size++;
433 	return table_size;
434 }
435 
436 static bool
437 pci_id_exists(const struct rte_pci_id *id, const struct rte_pci_id *table,
438 	      int next_idx)
439 {
440 	int current_size = next_idx - 1;
441 	int i;
442 
443 	for (i = 0; i < current_size; i++) {
444 		if (id->device_id == table[i].device_id &&
445 		    id->vendor_id == table[i].vendor_id &&
446 		    id->subsystem_vendor_id == table[i].subsystem_vendor_id &&
447 		    id->subsystem_device_id == table[i].subsystem_device_id)
448 			return true;
449 	}
450 	return false;
451 }
452 
453 static void
454 pci_id_insert(struct rte_pci_id *new_table, int *next_idx,
455 	      const struct rte_pci_id *id_table)
456 {
457 	/* Traverse the id_table, check if entry exists in new_table;
458 	 * Add non duplicate entries to new table.
459 	 */
460 	for (; id_table->vendor_id != 0; id_table++) {
461 		if (!pci_id_exists(id_table, new_table, *next_idx)) {
462 			/* New entry; add to the table. */
463 			new_table[*next_idx] = *id_table;
464 			(*next_idx)++;
465 		}
466 	}
467 }
468 
469 static int
470 pci_ids_table_update(const struct rte_pci_id *driver_id_table)
471 {
472 	const struct rte_pci_id *id_iter;
473 	struct rte_pci_id *updated_table;
474 	struct rte_pci_id *old_table;
475 	int num_ids = 0;
476 	int i = 0;
477 
478 	old_table = mlx5_pci_id_table;
479 	if (old_table)
480 		num_ids = pci_id_table_size_get(old_table);
481 	num_ids += pci_id_table_size_get(driver_id_table);
482 	/* Increase size by one for the termination entry of vendor_id = 0. */
483 	num_ids += 1;
484 	updated_table = calloc(num_ids, sizeof(*updated_table));
485 	if (!updated_table)
486 		return -ENOMEM;
487 	if (TAILQ_EMPTY(&drv_list)) {
488 		/* Copy the first driver's ID table. */
489 		for (id_iter = driver_id_table; id_iter->vendor_id != 0;
490 		     id_iter++, i++)
491 			updated_table[i] = *id_iter;
492 	} else {
493 		/* First copy existing table entries. */
494 		for (id_iter = old_table; id_iter->vendor_id != 0;
495 		     id_iter++, i++)
496 			updated_table[i] = *id_iter;
497 		/* New id to be added at the end of current ID table. */
498 		pci_id_insert(updated_table, &i, driver_id_table);
499 	}
500 	/* Terminate table with empty entry. */
501 	updated_table[i].vendor_id = 0;
502 	mlx5_pci_driver.id_table = updated_table;
503 	mlx5_pci_id_table = updated_table;
504 	if (old_table)
505 		free(old_table);
506 	return 0;
507 }
508 
509 void
510 mlx5_pci_driver_register(struct mlx5_pci_driver *driver)
511 {
512 	int ret;
513 
514 	ret = pci_ids_table_update(driver->pci_driver.id_table);
515 	if (ret)
516 		return;
517 	mlx5_pci_driver.drv_flags |= driver->pci_driver.drv_flags;
518 	TAILQ_INSERT_TAIL(&drv_list, driver, next);
519 }
520 
521 void mlx5_common_pci_init(void)
522 {
523 	const struct rte_pci_id empty_table[] = {
524 		{
525 			.vendor_id = 0
526 		},
527 	};
528 
529 	/* All mlx5 PMDs constructor runs at same priority. So any of the PMD
530 	 * including this one can register the PCI table first. If any other
531 	 * PMD(s) have registered the PCI ID table, No need to register an empty
532 	 * default one.
533 	 */
534 	if (mlx5_pci_id_table == NULL && pci_ids_table_update(empty_table))
535 		return;
536 	rte_pci_register(&mlx5_pci_driver);
537 }
538 
539 RTE_FINI(mlx5_common_pci_finish)
540 {
541 	if (mlx5_pci_id_table != NULL) {
542 		/* Constructor doesn't register with PCI bus if it failed
543 		 * to build the table.
544 		 */
545 		rte_pci_unregister(&mlx5_pci_driver);
546 		free(mlx5_pci_id_table);
547 	}
548 }
549 RTE_PMD_EXPORT_NAME(mlx5_common_pci, __COUNTER__);
550