xref: /dpdk/drivers/common/mlx5/mlx5_common_pci.c (revision 188773a2ee516fb790b0787b0da13c84fc1a984c)
18a41f4deSParav Pandit /* SPDX-License-Identifier: BSD-3-Clause
28a41f4deSParav Pandit  * Copyright 2020 Mellanox Technologies Ltd
38a41f4deSParav Pandit  */
48a41f4deSParav Pandit 
58a41f4deSParav Pandit #include <stdlib.h>
68a41f4deSParav Pandit #include <rte_malloc.h>
78a41f4deSParav Pandit #include "mlx5_common_utils.h"
88a41f4deSParav Pandit #include "mlx5_common_pci.h"
98a41f4deSParav Pandit 
108a41f4deSParav Pandit struct mlx5_pci_device {
118a41f4deSParav Pandit 	struct rte_pci_device *pci_dev;
128a41f4deSParav Pandit 	TAILQ_ENTRY(mlx5_pci_device) next;
138a41f4deSParav Pandit 	uint32_t classes_loaded;
148a41f4deSParav Pandit };
158a41f4deSParav Pandit 
168a41f4deSParav Pandit /* Head of list of drivers. */
178a41f4deSParav Pandit static TAILQ_HEAD(mlx5_pci_bus_drv_head, mlx5_pci_driver) drv_list =
188a41f4deSParav Pandit 				TAILQ_HEAD_INITIALIZER(drv_list);
198a41f4deSParav Pandit 
208a41f4deSParav Pandit /* Head of mlx5 pci devices. */
218a41f4deSParav Pandit static TAILQ_HEAD(mlx5_pci_devices_head, mlx5_pci_device) devices_list =
228a41f4deSParav Pandit 				TAILQ_HEAD_INITIALIZER(devices_list);
238a41f4deSParav Pandit 
248a41f4deSParav Pandit static const struct {
258a41f4deSParav Pandit 	const char *name;
268a41f4deSParav Pandit 	unsigned int driver_class;
278a41f4deSParav Pandit } mlx5_classes[] = {
288a41f4deSParav Pandit 	{ .name = "vdpa", .driver_class = MLX5_CLASS_VDPA },
298a41f4deSParav Pandit 	{ .name = "net", .driver_class = MLX5_CLASS_NET },
308a41f4deSParav Pandit 	{ .name = "regex", .driver_class = MLX5_CLASS_REGEX },
31832a4cf1SMatan Azrad 	{ .name = "compress", .driver_class = MLX5_CLASS_COMPRESS },
328a41f4deSParav Pandit };
338a41f4deSParav Pandit 
348a41f4deSParav Pandit static const unsigned int mlx5_class_combinations[] = {
358a41f4deSParav Pandit 	MLX5_CLASS_NET,
368a41f4deSParav Pandit 	MLX5_CLASS_VDPA,
378a41f4deSParav Pandit 	MLX5_CLASS_REGEX,
38832a4cf1SMatan Azrad 	MLX5_CLASS_COMPRESS,
398a41f4deSParav Pandit 	MLX5_CLASS_NET | MLX5_CLASS_REGEX,
408a41f4deSParav Pandit 	MLX5_CLASS_VDPA | MLX5_CLASS_REGEX,
41832a4cf1SMatan Azrad 	MLX5_CLASS_NET | MLX5_CLASS_COMPRESS,
42832a4cf1SMatan Azrad 	MLX5_CLASS_VDPA | MLX5_CLASS_COMPRESS,
43832a4cf1SMatan Azrad 	MLX5_CLASS_REGEX | MLX5_CLASS_COMPRESS,
44832a4cf1SMatan Azrad 	MLX5_CLASS_NET | MLX5_CLASS_REGEX | MLX5_CLASS_COMPRESS,
45832a4cf1SMatan Azrad 	MLX5_CLASS_VDPA | MLX5_CLASS_REGEX | MLX5_CLASS_COMPRESS,
468a41f4deSParav Pandit 	/* New class combination should be added here. */
478a41f4deSParav Pandit };
488a41f4deSParav Pandit 
498a41f4deSParav Pandit static int
508a41f4deSParav Pandit class_name_to_value(const char *class_name)
518a41f4deSParav Pandit {
528a41f4deSParav Pandit 	unsigned int i;
538a41f4deSParav Pandit 
548a41f4deSParav Pandit 	for (i = 0; i < RTE_DIM(mlx5_classes); i++) {
558a41f4deSParav Pandit 		if (strcmp(class_name, mlx5_classes[i].name) == 0)
568a41f4deSParav Pandit 			return mlx5_classes[i].driver_class;
578a41f4deSParav Pandit 	}
588a41f4deSParav Pandit 	return -EINVAL;
598a41f4deSParav Pandit }
608a41f4deSParav Pandit 
618a41f4deSParav Pandit static struct mlx5_pci_driver *
628a41f4deSParav Pandit driver_get(uint32_t class)
638a41f4deSParav Pandit {
648a41f4deSParav Pandit 	struct mlx5_pci_driver *driver;
658a41f4deSParav Pandit 
668a41f4deSParav Pandit 	TAILQ_FOREACH(driver, &drv_list, next) {
678a41f4deSParav Pandit 		if (driver->driver_class == class)
688a41f4deSParav Pandit 			return driver;
698a41f4deSParav Pandit 	}
708a41f4deSParav Pandit 	return NULL;
718a41f4deSParav Pandit }
728a41f4deSParav Pandit 
738a41f4deSParav Pandit static int
748a41f4deSParav Pandit bus_cmdline_options_handler(__rte_unused const char *key,
758a41f4deSParav Pandit 			    const char *class_names, void *opaque)
768a41f4deSParav Pandit {
778a41f4deSParav Pandit 	int *ret = opaque;
788a41f4deSParav Pandit 	char *nstr_org;
798a41f4deSParav Pandit 	int class_val;
808a41f4deSParav Pandit 	char *found;
818a41f4deSParav Pandit 	char *nstr;
823475aeacSOphir Munk 	char *refstr = NULL;
838a41f4deSParav Pandit 
848a41f4deSParav Pandit 	*ret = 0;
858a41f4deSParav Pandit 	nstr = strdup(class_names);
868a41f4deSParav Pandit 	if (!nstr) {
878a41f4deSParav Pandit 		*ret = -ENOMEM;
888a41f4deSParav Pandit 		return *ret;
898a41f4deSParav Pandit 	}
908a41f4deSParav Pandit 	nstr_org = nstr;
913475aeacSOphir Munk 	found = strtok_r(nstr, ":", &refstr);
923475aeacSOphir Munk 	if (!found)
933475aeacSOphir Munk 		goto err;
943475aeacSOphir Munk 	do {
958a41f4deSParav Pandit 		/* Extract each individual class name. Multiple
968a41f4deSParav Pandit 		 * class key,value is supplied as class=net:vdpa:foo:bar.
978a41f4deSParav Pandit 		 */
988a41f4deSParav Pandit 		class_val = class_name_to_value(found);
993475aeacSOphir Munk 		/* Check if its a valid class. */
1008a41f4deSParav Pandit 		if (class_val < 0) {
1018a41f4deSParav Pandit 			*ret = -EINVAL;
1028a41f4deSParav Pandit 			goto err;
1038a41f4deSParav Pandit 		}
1048a41f4deSParav Pandit 		*ret |= class_val;
1053475aeacSOphir Munk 		found = strtok_r(NULL, ":", &refstr);
1063475aeacSOphir Munk 	} while (found);
1078a41f4deSParav Pandit err:
1088a41f4deSParav Pandit 	free(nstr_org);
1098a41f4deSParav Pandit 	if (*ret < 0)
1108a41f4deSParav Pandit 		DRV_LOG(ERR, "Invalid mlx5 class options %s."
1118a41f4deSParav Pandit 			" Maybe typo in device class argument setting?",
1128a41f4deSParav Pandit 			class_names);
1138a41f4deSParav Pandit 	return *ret;
1148a41f4deSParav Pandit }
1158a41f4deSParav Pandit 
1168a41f4deSParav Pandit static int
1178a41f4deSParav Pandit parse_class_options(const struct rte_devargs *devargs)
1188a41f4deSParav Pandit {
1198a41f4deSParav Pandit 	const char *key = MLX5_CLASS_ARG_NAME;
1208a41f4deSParav Pandit 	struct rte_kvargs *kvlist;
1218a41f4deSParav Pandit 	int ret = 0;
1228a41f4deSParav Pandit 
1238a41f4deSParav Pandit 	if (devargs == NULL)
1248a41f4deSParav Pandit 		return 0;
1258a41f4deSParav Pandit 	kvlist = rte_kvargs_parse(devargs->args, NULL);
1268a41f4deSParav Pandit 	if (kvlist == NULL)
1278a41f4deSParav Pandit 		return 0;
1288a41f4deSParav Pandit 	if (rte_kvargs_count(kvlist, key))
1298a41f4deSParav Pandit 		rte_kvargs_process(kvlist, key, bus_cmdline_options_handler,
1308a41f4deSParav Pandit 				   &ret);
1318a41f4deSParav Pandit 	rte_kvargs_free(kvlist);
1328a41f4deSParav Pandit 	return ret;
1338a41f4deSParav Pandit }
1348a41f4deSParav Pandit 
1358a41f4deSParav Pandit static bool
1368a41f4deSParav Pandit mlx5_bus_match(const struct mlx5_pci_driver *drv,
1378a41f4deSParav Pandit 	       const struct rte_pci_device *pci_dev)
1388a41f4deSParav Pandit {
1398a41f4deSParav Pandit 	const struct rte_pci_id *id_table;
1408a41f4deSParav Pandit 
1418a41f4deSParav Pandit 	for (id_table = drv->pci_driver.id_table; id_table->vendor_id != 0;
1428a41f4deSParav Pandit 	     id_table++) {
1438a41f4deSParav Pandit 		/* Check if device's ids match the class driver's ids. */
1448a41f4deSParav Pandit 		if (id_table->vendor_id != pci_dev->id.vendor_id &&
1454d509afaSThomas Monjalon 		    id_table->vendor_id != RTE_PCI_ANY_ID)
1468a41f4deSParav Pandit 			continue;
1478a41f4deSParav Pandit 		if (id_table->device_id != pci_dev->id.device_id &&
1484d509afaSThomas Monjalon 		    id_table->device_id != RTE_PCI_ANY_ID)
1498a41f4deSParav Pandit 			continue;
1508a41f4deSParav Pandit 		if (id_table->subsystem_vendor_id !=
1518a41f4deSParav Pandit 		    pci_dev->id.subsystem_vendor_id &&
1524d509afaSThomas Monjalon 		    id_table->subsystem_vendor_id != RTE_PCI_ANY_ID)
1538a41f4deSParav Pandit 			continue;
1548a41f4deSParav Pandit 		if (id_table->subsystem_device_id !=
1558a41f4deSParav Pandit 		    pci_dev->id.subsystem_device_id &&
1564d509afaSThomas Monjalon 		    id_table->subsystem_device_id != RTE_PCI_ANY_ID)
1578a41f4deSParav Pandit 			continue;
1588a41f4deSParav Pandit 		if (id_table->class_id != pci_dev->id.class_id &&
1598a41f4deSParav Pandit 		    id_table->class_id != RTE_CLASS_ANY_ID)
1608a41f4deSParav Pandit 			continue;
1618a41f4deSParav Pandit 		return true;
1628a41f4deSParav Pandit 	}
1638a41f4deSParav Pandit 	return false;
1648a41f4deSParav Pandit }
1658a41f4deSParav Pandit 
1668a41f4deSParav Pandit static int
1678a41f4deSParav Pandit is_valid_class_combination(uint32_t user_classes)
1688a41f4deSParav Pandit {
1698a41f4deSParav Pandit 	unsigned int i;
1708a41f4deSParav Pandit 
1718a41f4deSParav Pandit 	/* Verify if user specified valid supported combination. */
1728a41f4deSParav Pandit 	for (i = 0; i < RTE_DIM(mlx5_class_combinations); i++) {
1738a41f4deSParav Pandit 		if (mlx5_class_combinations[i] == user_classes)
1748a41f4deSParav Pandit 			return 0;
1758a41f4deSParav Pandit 	}
1768a41f4deSParav Pandit 	/* Not found any valid class combination. */
1778a41f4deSParav Pandit 	return -EINVAL;
1788a41f4deSParav Pandit }
1798a41f4deSParav Pandit 
1808a41f4deSParav Pandit static struct mlx5_pci_device *
1818a41f4deSParav Pandit pci_to_mlx5_device(const struct rte_pci_device *pci_dev)
1828a41f4deSParav Pandit {
1838a41f4deSParav Pandit 	struct mlx5_pci_device *dev;
1848a41f4deSParav Pandit 
1858a41f4deSParav Pandit 	TAILQ_FOREACH(dev, &devices_list, next) {
1868a41f4deSParav Pandit 		if (dev->pci_dev == pci_dev)
1878a41f4deSParav Pandit 			return dev;
1888a41f4deSParav Pandit 	}
1898a41f4deSParav Pandit 	return NULL;
1908a41f4deSParav Pandit }
1918a41f4deSParav Pandit 
1928a41f4deSParav Pandit static bool
1938a41f4deSParav Pandit device_class_enabled(const struct mlx5_pci_device *device, uint32_t class)
1948a41f4deSParav Pandit {
1958a41f4deSParav Pandit 	return (device->classes_loaded & class) ? true : false;
1968a41f4deSParav Pandit }
1978a41f4deSParav Pandit 
1988a41f4deSParav Pandit static void
1998a41f4deSParav Pandit dev_release(struct mlx5_pci_device *dev)
2008a41f4deSParav Pandit {
2018a41f4deSParav Pandit 	TAILQ_REMOVE(&devices_list, dev, next);
2028a41f4deSParav Pandit 	rte_free(dev);
2038a41f4deSParav Pandit }
2048a41f4deSParav Pandit 
2058a41f4deSParav Pandit static int
2068a41f4deSParav Pandit drivers_remove(struct mlx5_pci_device *dev, uint32_t enabled_classes)
2078a41f4deSParav Pandit {
2088a41f4deSParav Pandit 	struct mlx5_pci_driver *driver;
2098a41f4deSParav Pandit 	int local_ret = -ENODEV;
2108a41f4deSParav Pandit 	unsigned int i = 0;
2118a41f4deSParav Pandit 	int ret = 0;
2128a41f4deSParav Pandit 
2138a41f4deSParav Pandit 	enabled_classes &= dev->classes_loaded;
2148a41f4deSParav Pandit 	while (enabled_classes) {
2158a41f4deSParav Pandit 		driver = driver_get(RTE_BIT64(i));
2168a41f4deSParav Pandit 		if (driver) {
2178a41f4deSParav Pandit 			local_ret = driver->pci_driver.remove(dev->pci_dev);
2188a41f4deSParav Pandit 			if (!local_ret)
2198a41f4deSParav Pandit 				dev->classes_loaded &= ~RTE_BIT64(i);
2208a41f4deSParav Pandit 			else if (ret == 0)
2218a41f4deSParav Pandit 				ret = local_ret;
2228a41f4deSParav Pandit 		}
2238a41f4deSParav Pandit 		enabled_classes &= ~RTE_BIT64(i);
2248a41f4deSParav Pandit 		i++;
2258a41f4deSParav Pandit 	}
2268a41f4deSParav Pandit 	if (local_ret)
2278a41f4deSParav Pandit 		ret = local_ret;
2288a41f4deSParav Pandit 	return ret;
2298a41f4deSParav Pandit }
2308a41f4deSParav Pandit 
2318a41f4deSParav Pandit static int
2328a41f4deSParav Pandit drivers_probe(struct mlx5_pci_device *dev, struct rte_pci_driver *pci_drv,
2338a41f4deSParav Pandit 	      struct rte_pci_device *pci_dev, uint32_t user_classes)
2348a41f4deSParav Pandit {
2358a41f4deSParav Pandit 	struct mlx5_pci_driver *driver;
2368a41f4deSParav Pandit 	uint32_t enabled_classes = 0;
2378a41f4deSParav Pandit 	bool already_loaded;
2388a41f4deSParav Pandit 	int ret;
2398a41f4deSParav Pandit 
2408a41f4deSParav Pandit 	TAILQ_FOREACH(driver, &drv_list, next) {
2418a41f4deSParav Pandit 		if ((driver->driver_class & user_classes) == 0)
2428a41f4deSParav Pandit 			continue;
2438a41f4deSParav Pandit 		if (!mlx5_bus_match(driver, pci_dev))
2448a41f4deSParav Pandit 			continue;
2458a41f4deSParav Pandit 		already_loaded = dev->classes_loaded & driver->driver_class;
2468a41f4deSParav Pandit 		if (already_loaded &&
2478a41f4deSParav Pandit 		    !(driver->pci_driver.drv_flags & RTE_PCI_DRV_PROBE_AGAIN)) {
2481b9e9826SThomas Monjalon 			DRV_LOG(ERR, "Device %s is already probed",
2498a41f4deSParav Pandit 				pci_dev->device.name);
2508a41f4deSParav Pandit 			ret = -EEXIST;
2518a41f4deSParav Pandit 			goto probe_err;
2528a41f4deSParav Pandit 		}
2538a41f4deSParav Pandit 		ret = driver->pci_driver.probe(pci_drv, pci_dev);
2548a41f4deSParav Pandit 		if (ret < 0) {
2551b9e9826SThomas Monjalon 			DRV_LOG(ERR, "Failed to load driver %s",
2568a41f4deSParav Pandit 				driver->pci_driver.driver.name);
2578a41f4deSParav Pandit 			goto probe_err;
2588a41f4deSParav Pandit 		}
2598a41f4deSParav Pandit 		enabled_classes |= driver->driver_class;
2608a41f4deSParav Pandit 	}
2618a41f4deSParav Pandit 	dev->classes_loaded |= enabled_classes;
2628a41f4deSParav Pandit 	return 0;
2638a41f4deSParav Pandit probe_err:
2648a41f4deSParav Pandit 	/* Only unload drivers which are enabled which were enabled
2658a41f4deSParav Pandit 	 * in this probe instance.
2668a41f4deSParav Pandit 	 */
2678a41f4deSParav Pandit 	drivers_remove(dev, enabled_classes);
2688a41f4deSParav Pandit 	return ret;
2698a41f4deSParav Pandit }
2708a41f4deSParav Pandit 
2718a41f4deSParav Pandit /**
2728a41f4deSParav Pandit  * DPDK callback to register to probe multiple drivers for a PCI device.
2738a41f4deSParav Pandit  *
2748a41f4deSParav Pandit  * @param[in] pci_drv
2758a41f4deSParav Pandit  *   PCI driver structure.
2768a41f4deSParav Pandit  * @param[in] dev
2778a41f4deSParav Pandit  *   PCI device information.
2788a41f4deSParav Pandit  *
2798a41f4deSParav Pandit  * @return
2808a41f4deSParav Pandit  *   0 on success, a negative errno value otherwise and rte_errno is set.
2818a41f4deSParav Pandit  */
2828a41f4deSParav Pandit static int
2838a41f4deSParav Pandit mlx5_common_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
2848a41f4deSParav Pandit 		      struct rte_pci_device *pci_dev)
2858a41f4deSParav Pandit {
2868a41f4deSParav Pandit 	struct mlx5_pci_device *dev;
2878a41f4deSParav Pandit 	uint32_t user_classes = 0;
2888a41f4deSParav Pandit 	bool new_device = false;
2898a41f4deSParav Pandit 	int ret;
2908a41f4deSParav Pandit 
2918a41f4deSParav Pandit 	ret = parse_class_options(pci_dev->device.devargs);
2928a41f4deSParav Pandit 	if (ret < 0)
2938a41f4deSParav Pandit 		return ret;
2948a41f4deSParav Pandit 	user_classes = ret;
2958a41f4deSParav Pandit 	if (user_classes) {
2968a41f4deSParav Pandit 		/* Validate combination here. */
2978a41f4deSParav Pandit 		ret = is_valid_class_combination(user_classes);
2988a41f4deSParav Pandit 		if (ret) {
2998a41f4deSParav Pandit 			DRV_LOG(ERR, "Unsupported mlx5 classes supplied.");
3008a41f4deSParav Pandit 			return ret;
3018a41f4deSParav Pandit 		}
3028a41f4deSParav Pandit 	} else {
3038a41f4deSParav Pandit 		/* Default to net class. */
3048a41f4deSParav Pandit 		user_classes = MLX5_CLASS_NET;
3058a41f4deSParav Pandit 	}
3068a41f4deSParav Pandit 	dev = pci_to_mlx5_device(pci_dev);
3078a41f4deSParav Pandit 	if (!dev) {
3088a41f4deSParav Pandit 		dev = rte_zmalloc("mlx5_pci_device", sizeof(*dev), 0);
3098a41f4deSParav Pandit 		if (!dev)
3108a41f4deSParav Pandit 			return -ENOMEM;
3118a41f4deSParav Pandit 		dev->pci_dev = pci_dev;
3128a41f4deSParav Pandit 		TAILQ_INSERT_HEAD(&devices_list, dev, next);
3138a41f4deSParav Pandit 		new_device = true;
3148a41f4deSParav Pandit 	}
3158a41f4deSParav Pandit 	ret = drivers_probe(dev, pci_drv, pci_dev, user_classes);
3168a41f4deSParav Pandit 	if (ret)
3178a41f4deSParav Pandit 		goto class_err;
3188a41f4deSParav Pandit 	return 0;
3198a41f4deSParav Pandit class_err:
3208a41f4deSParav Pandit 	if (new_device)
3218a41f4deSParav Pandit 		dev_release(dev);
3228a41f4deSParav Pandit 	return ret;
3238a41f4deSParav Pandit }
3248a41f4deSParav Pandit 
3258a41f4deSParav Pandit /**
3268a41f4deSParav Pandit  * DPDK callback to remove one or more drivers for a PCI device.
3278a41f4deSParav Pandit  *
3288a41f4deSParav Pandit  * This function removes all drivers probed for a given PCI device.
3298a41f4deSParav Pandit  *
3308a41f4deSParav Pandit  * @param[in] pci_dev
3318a41f4deSParav Pandit  *   Pointer to the PCI device.
3328a41f4deSParav Pandit  *
3338a41f4deSParav Pandit  * @return
3348a41f4deSParav Pandit  *   0 on success, the function cannot fail.
3358a41f4deSParav Pandit  */
3368a41f4deSParav Pandit static int
3378a41f4deSParav Pandit mlx5_common_pci_remove(struct rte_pci_device *pci_dev)
3388a41f4deSParav Pandit {
3398a41f4deSParav Pandit 	struct mlx5_pci_device *dev;
3408a41f4deSParav Pandit 	int ret;
3418a41f4deSParav Pandit 
3428a41f4deSParav Pandit 	dev = pci_to_mlx5_device(pci_dev);
3438a41f4deSParav Pandit 	if (!dev)
3448a41f4deSParav Pandit 		return -ENODEV;
3458a41f4deSParav Pandit 	/* Matching device found, cleanup and unload drivers. */
3468a41f4deSParav Pandit 	ret = drivers_remove(dev, dev->classes_loaded);
3478a41f4deSParav Pandit 	if (!ret)
3488a41f4deSParav Pandit 		dev_release(dev);
3498a41f4deSParav Pandit 	return ret;
3508a41f4deSParav Pandit }
3518a41f4deSParav Pandit 
3528a41f4deSParav Pandit static int
3538a41f4deSParav Pandit mlx5_common_pci_dma_map(struct rte_pci_device *pci_dev, void *addr,
3548a41f4deSParav Pandit 			uint64_t iova, size_t len)
3558a41f4deSParav Pandit {
3568a41f4deSParav Pandit 	struct mlx5_pci_driver *driver = NULL;
3578a41f4deSParav Pandit 	struct mlx5_pci_driver *temp;
3588a41f4deSParav Pandit 	struct mlx5_pci_device *dev;
3598a41f4deSParav Pandit 	int ret = -EINVAL;
3608a41f4deSParav Pandit 
3618a41f4deSParav Pandit 	dev = pci_to_mlx5_device(pci_dev);
3628a41f4deSParav Pandit 	if (!dev)
3638a41f4deSParav Pandit 		return -ENODEV;
3648a41f4deSParav Pandit 	TAILQ_FOREACH(driver, &drv_list, next) {
3658a41f4deSParav Pandit 		if (device_class_enabled(dev, driver->driver_class) &&
3668a41f4deSParav Pandit 		    driver->pci_driver.dma_map) {
3678a41f4deSParav Pandit 			ret = driver->pci_driver.dma_map(pci_dev, addr,
3688a41f4deSParav Pandit 							 iova, len);
3698a41f4deSParav Pandit 			if (ret)
3708a41f4deSParav Pandit 				goto map_err;
3718a41f4deSParav Pandit 		}
3728a41f4deSParav Pandit 	}
3738a41f4deSParav Pandit 	return ret;
3748a41f4deSParav Pandit map_err:
3758a41f4deSParav Pandit 	TAILQ_FOREACH(temp, &drv_list, next) {
3768a41f4deSParav Pandit 		if (temp == driver)
3778a41f4deSParav Pandit 			break;
3788a41f4deSParav Pandit 		if (device_class_enabled(dev, temp->driver_class) &&
3798a41f4deSParav Pandit 		    temp->pci_driver.dma_map && temp->pci_driver.dma_unmap)
3808a41f4deSParav Pandit 			temp->pci_driver.dma_unmap(pci_dev, addr, iova, len);
3818a41f4deSParav Pandit 	}
3828a41f4deSParav Pandit 	return ret;
3838a41f4deSParav Pandit }
3848a41f4deSParav Pandit 
3858a41f4deSParav Pandit static int
3868a41f4deSParav Pandit mlx5_common_pci_dma_unmap(struct rte_pci_device *pci_dev, void *addr,
3878a41f4deSParav Pandit 			  uint64_t iova, size_t len)
3888a41f4deSParav Pandit {
3898a41f4deSParav Pandit 	struct mlx5_pci_driver *driver;
3908a41f4deSParav Pandit 	struct mlx5_pci_device *dev;
3918a41f4deSParav Pandit 	int local_ret = -EINVAL;
3928a41f4deSParav Pandit 	int ret;
3938a41f4deSParav Pandit 
3948a41f4deSParav Pandit 	dev = pci_to_mlx5_device(pci_dev);
3958a41f4deSParav Pandit 	if (!dev)
3968a41f4deSParav Pandit 		return -ENODEV;
3978a41f4deSParav Pandit 	ret = 0;
3988a41f4deSParav Pandit 	/* There is no unmap error recovery in current implementation. */
3998a41f4deSParav Pandit 	TAILQ_FOREACH_REVERSE(driver, &drv_list, mlx5_pci_bus_drv_head, next) {
4008a41f4deSParav Pandit 		if (device_class_enabled(dev, driver->driver_class) &&
4018a41f4deSParav Pandit 		    driver->pci_driver.dma_unmap) {
4028a41f4deSParav Pandit 			local_ret = driver->pci_driver.dma_unmap(pci_dev, addr,
4038a41f4deSParav Pandit 								 iova, len);
4048a41f4deSParav Pandit 			if (local_ret && (ret == 0))
4058a41f4deSParav Pandit 				ret = local_ret;
4068a41f4deSParav Pandit 		}
4078a41f4deSParav Pandit 	}
4088a41f4deSParav Pandit 	if (local_ret)
4098a41f4deSParav Pandit 		ret = local_ret;
4108a41f4deSParav Pandit 	return ret;
4118a41f4deSParav Pandit }
4128a41f4deSParav Pandit 
4138a41f4deSParav Pandit /* PCI ID table is build dynamically based on registered mlx5 drivers. */
4148a41f4deSParav Pandit static struct rte_pci_id *mlx5_pci_id_table;
4158a41f4deSParav Pandit 
4168a41f4deSParav Pandit static struct rte_pci_driver mlx5_pci_driver = {
4178a41f4deSParav Pandit 	.driver = {
418*188773a2SAsaf Penso 		.name = MLX5_PCI_DRIVER_NAME,
4198a41f4deSParav Pandit 	},
4208a41f4deSParav Pandit 	.probe = mlx5_common_pci_probe,
4218a41f4deSParav Pandit 	.remove = mlx5_common_pci_remove,
4228a41f4deSParav Pandit 	.dma_map = mlx5_common_pci_dma_map,
4238a41f4deSParav Pandit 	.dma_unmap = mlx5_common_pci_dma_unmap,
4248a41f4deSParav Pandit };
4258a41f4deSParav Pandit 
4268a41f4deSParav Pandit static int
4278a41f4deSParav Pandit pci_id_table_size_get(const struct rte_pci_id *id_table)
4288a41f4deSParav Pandit {
4298a41f4deSParav Pandit 	int table_size = 0;
4308a41f4deSParav Pandit 
4318a41f4deSParav Pandit 	for (; id_table->vendor_id != 0; id_table++)
4328a41f4deSParav Pandit 		table_size++;
4338a41f4deSParav Pandit 	return table_size;
4348a41f4deSParav Pandit }
4358a41f4deSParav Pandit 
4368a41f4deSParav Pandit static bool
4378a41f4deSParav Pandit pci_id_exists(const struct rte_pci_id *id, const struct rte_pci_id *table,
4388a41f4deSParav Pandit 	      int next_idx)
4398a41f4deSParav Pandit {
4408a41f4deSParav Pandit 	int current_size = next_idx - 1;
4418a41f4deSParav Pandit 	int i;
4428a41f4deSParav Pandit 
4438a41f4deSParav Pandit 	for (i = 0; i < current_size; i++) {
4448a41f4deSParav Pandit 		if (id->device_id == table[i].device_id &&
4458a41f4deSParav Pandit 		    id->vendor_id == table[i].vendor_id &&
4468a41f4deSParav Pandit 		    id->subsystem_vendor_id == table[i].subsystem_vendor_id &&
4478a41f4deSParav Pandit 		    id->subsystem_device_id == table[i].subsystem_device_id)
4488a41f4deSParav Pandit 			return true;
4498a41f4deSParav Pandit 	}
4508a41f4deSParav Pandit 	return false;
4518a41f4deSParav Pandit }
4528a41f4deSParav Pandit 
4538a41f4deSParav Pandit static void
4548a41f4deSParav Pandit pci_id_insert(struct rte_pci_id *new_table, int *next_idx,
4558a41f4deSParav Pandit 	      const struct rte_pci_id *id_table)
4568a41f4deSParav Pandit {
4578a41f4deSParav Pandit 	/* Traverse the id_table, check if entry exists in new_table;
4588a41f4deSParav Pandit 	 * Add non duplicate entries to new table.
4598a41f4deSParav Pandit 	 */
4608a41f4deSParav Pandit 	for (; id_table->vendor_id != 0; id_table++) {
4618a41f4deSParav Pandit 		if (!pci_id_exists(id_table, new_table, *next_idx)) {
4628a41f4deSParav Pandit 			/* New entry; add to the table. */
4638a41f4deSParav Pandit 			new_table[*next_idx] = *id_table;
4648a41f4deSParav Pandit 			(*next_idx)++;
4658a41f4deSParav Pandit 		}
4668a41f4deSParav Pandit 	}
4678a41f4deSParav Pandit }
4688a41f4deSParav Pandit 
4698a41f4deSParav Pandit static int
4708a41f4deSParav Pandit pci_ids_table_update(const struct rte_pci_id *driver_id_table)
4718a41f4deSParav Pandit {
4728a41f4deSParav Pandit 	const struct rte_pci_id *id_iter;
4738a41f4deSParav Pandit 	struct rte_pci_id *updated_table;
4748a41f4deSParav Pandit 	struct rte_pci_id *old_table;
4758a41f4deSParav Pandit 	int num_ids = 0;
4768a41f4deSParav Pandit 	int i = 0;
4778a41f4deSParav Pandit 
4788a41f4deSParav Pandit 	old_table = mlx5_pci_id_table;
4798a41f4deSParav Pandit 	if (old_table)
4808a41f4deSParav Pandit 		num_ids = pci_id_table_size_get(old_table);
4818a41f4deSParav Pandit 	num_ids += pci_id_table_size_get(driver_id_table);
4828a41f4deSParav Pandit 	/* Increase size by one for the termination entry of vendor_id = 0. */
4838a41f4deSParav Pandit 	num_ids += 1;
4848a41f4deSParav Pandit 	updated_table = calloc(num_ids, sizeof(*updated_table));
4858a41f4deSParav Pandit 	if (!updated_table)
4868a41f4deSParav Pandit 		return -ENOMEM;
4878a41f4deSParav Pandit 	if (TAILQ_EMPTY(&drv_list)) {
4888a41f4deSParav Pandit 		/* Copy the first driver's ID table. */
4898a41f4deSParav Pandit 		for (id_iter = driver_id_table; id_iter->vendor_id != 0;
4908a41f4deSParav Pandit 		     id_iter++, i++)
4918a41f4deSParav Pandit 			updated_table[i] = *id_iter;
4928a41f4deSParav Pandit 	} else {
4938a41f4deSParav Pandit 		/* First copy existing table entries. */
4948a41f4deSParav Pandit 		for (id_iter = old_table; id_iter->vendor_id != 0;
4958a41f4deSParav Pandit 		     id_iter++, i++)
4968a41f4deSParav Pandit 			updated_table[i] = *id_iter;
4978a41f4deSParav Pandit 		/* New id to be added at the end of current ID table. */
4988a41f4deSParav Pandit 		pci_id_insert(updated_table, &i, driver_id_table);
4998a41f4deSParav Pandit 	}
5008a41f4deSParav Pandit 	/* Terminate table with empty entry. */
5018a41f4deSParav Pandit 	updated_table[i].vendor_id = 0;
5028a41f4deSParav Pandit 	mlx5_pci_driver.id_table = updated_table;
5038a41f4deSParav Pandit 	mlx5_pci_id_table = updated_table;
5048a41f4deSParav Pandit 	if (old_table)
5058a41f4deSParav Pandit 		free(old_table);
5068a41f4deSParav Pandit 	return 0;
5078a41f4deSParav Pandit }
5088a41f4deSParav Pandit 
5098a41f4deSParav Pandit void
5108a41f4deSParav Pandit mlx5_pci_driver_register(struct mlx5_pci_driver *driver)
5118a41f4deSParav Pandit {
5128a41f4deSParav Pandit 	int ret;
5138a41f4deSParav Pandit 
5148a41f4deSParav Pandit 	ret = pci_ids_table_update(driver->pci_driver.id_table);
5158a41f4deSParav Pandit 	if (ret)
5168a41f4deSParav Pandit 		return;
5178a41f4deSParav Pandit 	mlx5_pci_driver.drv_flags |= driver->pci_driver.drv_flags;
5188a41f4deSParav Pandit 	TAILQ_INSERT_TAIL(&drv_list, driver, next);
5198a41f4deSParav Pandit }
5208a41f4deSParav Pandit 
5218a41f4deSParav Pandit void mlx5_common_pci_init(void)
5228a41f4deSParav Pandit {
5238a41f4deSParav Pandit 	const struct rte_pci_id empty_table[] = {
5248a41f4deSParav Pandit 		{
5258a41f4deSParav Pandit 			.vendor_id = 0
5268a41f4deSParav Pandit 		},
5278a41f4deSParav Pandit 	};
5288a41f4deSParav Pandit 
5298a41f4deSParav Pandit 	/* All mlx5 PMDs constructor runs at same priority. So any of the PMD
5308a41f4deSParav Pandit 	 * including this one can register the PCI table first. If any other
5318a41f4deSParav Pandit 	 * PMD(s) have registered the PCI ID table, No need to register an empty
5328a41f4deSParav Pandit 	 * default one.
5338a41f4deSParav Pandit 	 */
5348a41f4deSParav Pandit 	if (mlx5_pci_id_table == NULL && pci_ids_table_update(empty_table))
5358a41f4deSParav Pandit 		return;
5368a41f4deSParav Pandit 	rte_pci_register(&mlx5_pci_driver);
5378a41f4deSParav Pandit }
5388a41f4deSParav Pandit 
5398a41f4deSParav Pandit RTE_FINI(mlx5_common_pci_finish)
5408a41f4deSParav Pandit {
5418a41f4deSParav Pandit 	if (mlx5_pci_id_table != NULL) {
5428a41f4deSParav Pandit 		/* Constructor doesn't register with PCI bus if it failed
5438a41f4deSParav Pandit 		 * to build the table.
5448a41f4deSParav Pandit 		 */
5458a41f4deSParav Pandit 		rte_pci_unregister(&mlx5_pci_driver);
5468a41f4deSParav Pandit 		free(mlx5_pci_id_table);
5478a41f4deSParav Pandit 	}
5488a41f4deSParav Pandit }
5498a41f4deSParav Pandit RTE_PMD_EXPORT_NAME(mlx5_common_pci, __COUNTER__);
550