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