xref: /dpdk/drivers/common/mlx5/mlx5_common_pci.c (revision ad435d3204736e8110cd3ecdf5363ae20f68e66f)
18a41f4deSParav Pandit /* SPDX-License-Identifier: BSD-3-Clause
28a41f4deSParav Pandit  * Copyright 2020 Mellanox Technologies Ltd
38a41f4deSParav Pandit  */
48a41f4deSParav Pandit 
58a41f4deSParav Pandit #include <stdlib.h>
6*ad435d32SXueming Li 
78a41f4deSParav Pandit #include <rte_malloc.h>
8*ad435d32SXueming Li #include <rte_devargs.h>
9*ad435d32SXueming Li #include <rte_errno.h>
1025245d5dSShiri Kuzin #include <rte_class.h>
1125245d5dSShiri Kuzin 
1225245d5dSShiri Kuzin #include "mlx5_common_log.h"
138a41f4deSParav Pandit #include "mlx5_common_pci.h"
14*ad435d32SXueming Li #include "mlx5_common_private.h"
15*ad435d32SXueming Li 
16*ad435d32SXueming Li static struct rte_pci_driver mlx5_common_pci_driver;
17*ad435d32SXueming Li 
18*ad435d32SXueming Li /********** Legacy PCI bus driver, to be removed ********/
198a41f4deSParav Pandit 
208a41f4deSParav Pandit struct mlx5_pci_device {
218a41f4deSParav Pandit 	struct rte_pci_device *pci_dev;
228a41f4deSParav Pandit 	TAILQ_ENTRY(mlx5_pci_device) next;
238a41f4deSParav Pandit 	uint32_t classes_loaded;
248a41f4deSParav Pandit };
258a41f4deSParav Pandit 
268a41f4deSParav Pandit /* Head of list of drivers. */
278a41f4deSParav Pandit static TAILQ_HEAD(mlx5_pci_bus_drv_head, mlx5_pci_driver) drv_list =
288a41f4deSParav Pandit 				TAILQ_HEAD_INITIALIZER(drv_list);
298a41f4deSParav Pandit 
308a41f4deSParav Pandit /* Head of mlx5 pci devices. */
318a41f4deSParav Pandit static TAILQ_HEAD(mlx5_pci_devices_head, mlx5_pci_device) devices_list =
328a41f4deSParav Pandit 				TAILQ_HEAD_INITIALIZER(devices_list);
338a41f4deSParav Pandit 
348a41f4deSParav Pandit static const struct {
358a41f4deSParav Pandit 	const char *name;
368a41f4deSParav Pandit 	unsigned int driver_class;
378a41f4deSParav Pandit } mlx5_classes[] = {
388a41f4deSParav Pandit 	{ .name = "vdpa", .driver_class = MLX5_CLASS_VDPA },
39a99f2f90SXueming Li 	{ .name = "eth", .driver_class = MLX5_CLASS_ETH },
40a99f2f90SXueming Li 	/* Keep name "net" for backward compatibility. */
41a99f2f90SXueming Li 	{ .name = "net", .driver_class = MLX5_CLASS_ETH },
428a41f4deSParav Pandit 	{ .name = "regex", .driver_class = MLX5_CLASS_REGEX },
43832a4cf1SMatan Azrad 	{ .name = "compress", .driver_class = MLX5_CLASS_COMPRESS },
44a7c86884SShiri Kuzin 	{ .name = "crypto", .driver_class = MLX5_CLASS_CRYPTO },
458a41f4deSParav Pandit };
468a41f4deSParav Pandit 
478a41f4deSParav Pandit static const unsigned int mlx5_class_combinations[] = {
48a99f2f90SXueming Li 	MLX5_CLASS_ETH,
498a41f4deSParav Pandit 	MLX5_CLASS_VDPA,
508a41f4deSParav Pandit 	MLX5_CLASS_REGEX,
51832a4cf1SMatan Azrad 	MLX5_CLASS_COMPRESS,
52a7c86884SShiri Kuzin 	MLX5_CLASS_CRYPTO,
53a99f2f90SXueming Li 	MLX5_CLASS_ETH | MLX5_CLASS_REGEX,
548a41f4deSParav Pandit 	MLX5_CLASS_VDPA | MLX5_CLASS_REGEX,
55a99f2f90SXueming Li 	MLX5_CLASS_ETH | MLX5_CLASS_COMPRESS,
56832a4cf1SMatan Azrad 	MLX5_CLASS_VDPA | MLX5_CLASS_COMPRESS,
57832a4cf1SMatan Azrad 	MLX5_CLASS_REGEX | MLX5_CLASS_COMPRESS,
58a99f2f90SXueming Li 	MLX5_CLASS_ETH | MLX5_CLASS_CRYPTO,
59a99f2f90SXueming Li 	MLX5_CLASS_ETH | MLX5_CLASS_REGEX | MLX5_CLASS_COMPRESS,
60a7c86884SShiri Kuzin 	MLX5_CLASS_VDPA | MLX5_CLASS_CRYPTO,
61a7c86884SShiri Kuzin 	MLX5_CLASS_REGEX | MLX5_CLASS_CRYPTO,
62a7c86884SShiri Kuzin 	MLX5_CLASS_COMPRESS | MLX5_CLASS_CRYPTO,
63832a4cf1SMatan Azrad 	MLX5_CLASS_VDPA | MLX5_CLASS_REGEX | MLX5_CLASS_COMPRESS,
64a99f2f90SXueming Li 	MLX5_CLASS_ETH | MLX5_CLASS_REGEX | MLX5_CLASS_CRYPTO,
65a7c86884SShiri Kuzin 	MLX5_CLASS_VDPA | MLX5_CLASS_REGEX | MLX5_CLASS_CRYPTO,
66a99f2f90SXueming Li 	MLX5_CLASS_ETH | MLX5_CLASS_COMPRESS | MLX5_CLASS_CRYPTO,
67a7c86884SShiri Kuzin 	MLX5_CLASS_VDPA | MLX5_CLASS_COMPRESS | MLX5_CLASS_CRYPTO,
68a99f2f90SXueming Li 	MLX5_CLASS_ETH | MLX5_CLASS_REGEX | MLX5_CLASS_COMPRESS |
69a7c86884SShiri Kuzin 	MLX5_CLASS_CRYPTO,
70a7c86884SShiri Kuzin 	MLX5_CLASS_VDPA | MLX5_CLASS_REGEX | MLX5_CLASS_COMPRESS |
71a7c86884SShiri Kuzin 	MLX5_CLASS_CRYPTO,
728a41f4deSParav Pandit 	/* New class combination should be added here. */
738a41f4deSParav Pandit };
748a41f4deSParav Pandit 
758a41f4deSParav Pandit static int
768a41f4deSParav Pandit class_name_to_value(const char *class_name)
778a41f4deSParav Pandit {
788a41f4deSParav Pandit 	unsigned int i;
798a41f4deSParav Pandit 
808a41f4deSParav Pandit 	for (i = 0; i < RTE_DIM(mlx5_classes); i++) {
818a41f4deSParav Pandit 		if (strcmp(class_name, mlx5_classes[i].name) == 0)
828a41f4deSParav Pandit 			return mlx5_classes[i].driver_class;
838a41f4deSParav Pandit 	}
848a41f4deSParav Pandit 	return -EINVAL;
858a41f4deSParav Pandit }
868a41f4deSParav Pandit 
878a41f4deSParav Pandit static struct mlx5_pci_driver *
888a41f4deSParav Pandit driver_get(uint32_t class)
898a41f4deSParav Pandit {
908a41f4deSParav Pandit 	struct mlx5_pci_driver *driver;
918a41f4deSParav Pandit 
928a41f4deSParav Pandit 	TAILQ_FOREACH(driver, &drv_list, next) {
938a41f4deSParav Pandit 		if (driver->driver_class == class)
948a41f4deSParav Pandit 			return driver;
958a41f4deSParav Pandit 	}
968a41f4deSParav Pandit 	return NULL;
978a41f4deSParav Pandit }
988a41f4deSParav Pandit 
998a41f4deSParav Pandit static int
1008a41f4deSParav Pandit bus_cmdline_options_handler(__rte_unused const char *key,
1018a41f4deSParav Pandit 			    const char *class_names, void *opaque)
1028a41f4deSParav Pandit {
1038a41f4deSParav Pandit 	int *ret = opaque;
1048a41f4deSParav Pandit 	char *nstr_org;
1058a41f4deSParav Pandit 	int class_val;
1068a41f4deSParav Pandit 	char *found;
1078a41f4deSParav Pandit 	char *nstr;
1083475aeacSOphir Munk 	char *refstr = NULL;
1098a41f4deSParav Pandit 
1108a41f4deSParav Pandit 	*ret = 0;
1118a41f4deSParav Pandit 	nstr = strdup(class_names);
1128a41f4deSParav Pandit 	if (!nstr) {
1138a41f4deSParav Pandit 		*ret = -ENOMEM;
1148a41f4deSParav Pandit 		return *ret;
1158a41f4deSParav Pandit 	}
1168a41f4deSParav Pandit 	nstr_org = nstr;
1173475aeacSOphir Munk 	found = strtok_r(nstr, ":", &refstr);
1183475aeacSOphir Munk 	if (!found)
1193475aeacSOphir Munk 		goto err;
1203475aeacSOphir Munk 	do {
1218a41f4deSParav Pandit 		/* Extract each individual class name. Multiple
1228a41f4deSParav Pandit 		 * class key,value is supplied as class=net:vdpa:foo:bar.
1238a41f4deSParav Pandit 		 */
1248a41f4deSParav Pandit 		class_val = class_name_to_value(found);
1253475aeacSOphir Munk 		/* Check if its a valid class. */
1268a41f4deSParav Pandit 		if (class_val < 0) {
1278a41f4deSParav Pandit 			*ret = -EINVAL;
1288a41f4deSParav Pandit 			goto err;
1298a41f4deSParav Pandit 		}
1308a41f4deSParav Pandit 		*ret |= class_val;
1313475aeacSOphir Munk 		found = strtok_r(NULL, ":", &refstr);
1323475aeacSOphir Munk 	} while (found);
1338a41f4deSParav Pandit err:
1348a41f4deSParav Pandit 	free(nstr_org);
1358a41f4deSParav Pandit 	if (*ret < 0)
1368a41f4deSParav Pandit 		DRV_LOG(ERR, "Invalid mlx5 class options %s."
1378a41f4deSParav Pandit 			" Maybe typo in device class argument setting?",
1388a41f4deSParav Pandit 			class_names);
1398a41f4deSParav Pandit 	return *ret;
1408a41f4deSParav Pandit }
1418a41f4deSParav Pandit 
1428a41f4deSParav Pandit static int
1438a41f4deSParav Pandit parse_class_options(const struct rte_devargs *devargs)
1448a41f4deSParav Pandit {
14535d4f17bSXueming Li 	const char *key = RTE_DEVARGS_KEY_CLASS;
1468a41f4deSParav Pandit 	struct rte_kvargs *kvlist;
1478a41f4deSParav Pandit 	int ret = 0;
1488a41f4deSParav Pandit 
1498a41f4deSParav Pandit 	if (devargs == NULL)
1508a41f4deSParav Pandit 		return 0;
1518a41f4deSParav Pandit 	kvlist = rte_kvargs_parse(devargs->args, NULL);
1528a41f4deSParav Pandit 	if (kvlist == NULL)
1538a41f4deSParav Pandit 		return 0;
1548a41f4deSParav Pandit 	if (rte_kvargs_count(kvlist, key))
1558a41f4deSParav Pandit 		rte_kvargs_process(kvlist, key, bus_cmdline_options_handler,
1568a41f4deSParav Pandit 				   &ret);
1578a41f4deSParav Pandit 	rte_kvargs_free(kvlist);
1588a41f4deSParav Pandit 	return ret;
1598a41f4deSParav Pandit }
1608a41f4deSParav Pandit 
1618a41f4deSParav Pandit static bool
1628a41f4deSParav Pandit mlx5_bus_match(const struct mlx5_pci_driver *drv,
1638a41f4deSParav Pandit 	       const struct rte_pci_device *pci_dev)
1648a41f4deSParav Pandit {
1658a41f4deSParav Pandit 	const struct rte_pci_id *id_table;
1668a41f4deSParav Pandit 
1678a41f4deSParav Pandit 	for (id_table = drv->pci_driver.id_table; id_table->vendor_id != 0;
1688a41f4deSParav Pandit 	     id_table++) {
1698a41f4deSParav Pandit 		/* Check if device's ids match the class driver's ids. */
1708a41f4deSParav Pandit 		if (id_table->vendor_id != pci_dev->id.vendor_id &&
1714d509afaSThomas Monjalon 		    id_table->vendor_id != RTE_PCI_ANY_ID)
1728a41f4deSParav Pandit 			continue;
1738a41f4deSParav Pandit 		if (id_table->device_id != pci_dev->id.device_id &&
1744d509afaSThomas Monjalon 		    id_table->device_id != RTE_PCI_ANY_ID)
1758a41f4deSParav Pandit 			continue;
1768a41f4deSParav Pandit 		if (id_table->subsystem_vendor_id !=
1778a41f4deSParav Pandit 		    pci_dev->id.subsystem_vendor_id &&
1784d509afaSThomas Monjalon 		    id_table->subsystem_vendor_id != RTE_PCI_ANY_ID)
1798a41f4deSParav Pandit 			continue;
1808a41f4deSParav Pandit 		if (id_table->subsystem_device_id !=
1818a41f4deSParav Pandit 		    pci_dev->id.subsystem_device_id &&
1824d509afaSThomas Monjalon 		    id_table->subsystem_device_id != RTE_PCI_ANY_ID)
1838a41f4deSParav Pandit 			continue;
1848a41f4deSParav Pandit 		if (id_table->class_id != pci_dev->id.class_id &&
1858a41f4deSParav Pandit 		    id_table->class_id != RTE_CLASS_ANY_ID)
1868a41f4deSParav Pandit 			continue;
1878a41f4deSParav Pandit 		return true;
1888a41f4deSParav Pandit 	}
1898a41f4deSParav Pandit 	return false;
1908a41f4deSParav Pandit }
1918a41f4deSParav Pandit 
1928a41f4deSParav Pandit static int
1938a41f4deSParav Pandit is_valid_class_combination(uint32_t user_classes)
1948a41f4deSParav Pandit {
1958a41f4deSParav Pandit 	unsigned int i;
1968a41f4deSParav Pandit 
1978a41f4deSParav Pandit 	/* Verify if user specified valid supported combination. */
1988a41f4deSParav Pandit 	for (i = 0; i < RTE_DIM(mlx5_class_combinations); i++) {
1998a41f4deSParav Pandit 		if (mlx5_class_combinations[i] == user_classes)
2008a41f4deSParav Pandit 			return 0;
2018a41f4deSParav Pandit 	}
2028a41f4deSParav Pandit 	/* Not found any valid class combination. */
2038a41f4deSParav Pandit 	return -EINVAL;
2048a41f4deSParav Pandit }
2058a41f4deSParav Pandit 
2068a41f4deSParav Pandit static struct mlx5_pci_device *
2078a41f4deSParav Pandit pci_to_mlx5_device(const struct rte_pci_device *pci_dev)
2088a41f4deSParav Pandit {
2098a41f4deSParav Pandit 	struct mlx5_pci_device *dev;
2108a41f4deSParav Pandit 
2118a41f4deSParav Pandit 	TAILQ_FOREACH(dev, &devices_list, next) {
2128a41f4deSParav Pandit 		if (dev->pci_dev == pci_dev)
2138a41f4deSParav Pandit 			return dev;
2148a41f4deSParav Pandit 	}
2158a41f4deSParav Pandit 	return NULL;
2168a41f4deSParav Pandit }
2178a41f4deSParav Pandit 
2188a41f4deSParav Pandit static bool
2198a41f4deSParav Pandit device_class_enabled(const struct mlx5_pci_device *device, uint32_t class)
2208a41f4deSParav Pandit {
2218a41f4deSParav Pandit 	return (device->classes_loaded & class) ? true : false;
2228a41f4deSParav Pandit }
2238a41f4deSParav Pandit 
2248a41f4deSParav Pandit static void
2258a41f4deSParav Pandit dev_release(struct mlx5_pci_device *dev)
2268a41f4deSParav Pandit {
2278a41f4deSParav Pandit 	TAILQ_REMOVE(&devices_list, dev, next);
2288a41f4deSParav Pandit 	rte_free(dev);
2298a41f4deSParav Pandit }
2308a41f4deSParav Pandit 
2318a41f4deSParav Pandit static int
2328a41f4deSParav Pandit drivers_remove(struct mlx5_pci_device *dev, uint32_t enabled_classes)
2338a41f4deSParav Pandit {
2348a41f4deSParav Pandit 	struct mlx5_pci_driver *driver;
2358a41f4deSParav Pandit 	int local_ret = -ENODEV;
2368a41f4deSParav Pandit 	unsigned int i = 0;
2378a41f4deSParav Pandit 	int ret = 0;
2388a41f4deSParav Pandit 
2398a41f4deSParav Pandit 	enabled_classes &= dev->classes_loaded;
2408a41f4deSParav Pandit 	while (enabled_classes) {
2418a41f4deSParav Pandit 		driver = driver_get(RTE_BIT64(i));
2428a41f4deSParav Pandit 		if (driver) {
2438a41f4deSParav Pandit 			local_ret = driver->pci_driver.remove(dev->pci_dev);
2448a41f4deSParav Pandit 			if (!local_ret)
2458a41f4deSParav Pandit 				dev->classes_loaded &= ~RTE_BIT64(i);
2468a41f4deSParav Pandit 			else if (ret == 0)
2478a41f4deSParav Pandit 				ret = local_ret;
2488a41f4deSParav Pandit 		}
2498a41f4deSParav Pandit 		enabled_classes &= ~RTE_BIT64(i);
2508a41f4deSParav Pandit 		i++;
2518a41f4deSParav Pandit 	}
2528a41f4deSParav Pandit 	if (local_ret)
2538a41f4deSParav Pandit 		ret = local_ret;
2548a41f4deSParav Pandit 	return ret;
2558a41f4deSParav Pandit }
2568a41f4deSParav Pandit 
2578a41f4deSParav Pandit static int
2588a41f4deSParav Pandit drivers_probe(struct mlx5_pci_device *dev, struct rte_pci_driver *pci_drv,
2598a41f4deSParav Pandit 	      struct rte_pci_device *pci_dev, uint32_t user_classes)
2608a41f4deSParav Pandit {
2618a41f4deSParav Pandit 	struct mlx5_pci_driver *driver;
2628a41f4deSParav Pandit 	uint32_t enabled_classes = 0;
2638a41f4deSParav Pandit 	bool already_loaded;
2648a41f4deSParav Pandit 	int ret;
2658a41f4deSParav Pandit 
2668a41f4deSParav Pandit 	TAILQ_FOREACH(driver, &drv_list, next) {
2678a41f4deSParav Pandit 		if ((driver->driver_class & user_classes) == 0)
2688a41f4deSParav Pandit 			continue;
2698a41f4deSParav Pandit 		if (!mlx5_bus_match(driver, pci_dev))
2708a41f4deSParav Pandit 			continue;
2718a41f4deSParav Pandit 		already_loaded = dev->classes_loaded & driver->driver_class;
2728a41f4deSParav Pandit 		if (already_loaded &&
2738a41f4deSParav Pandit 		    !(driver->pci_driver.drv_flags & RTE_PCI_DRV_PROBE_AGAIN)) {
2741b9e9826SThomas Monjalon 			DRV_LOG(ERR, "Device %s is already probed",
2758a41f4deSParav Pandit 				pci_dev->device.name);
2768a41f4deSParav Pandit 			ret = -EEXIST;
2778a41f4deSParav Pandit 			goto probe_err;
2788a41f4deSParav Pandit 		}
2798a41f4deSParav Pandit 		ret = driver->pci_driver.probe(pci_drv, pci_dev);
2808a41f4deSParav Pandit 		if (ret < 0) {
2811b9e9826SThomas Monjalon 			DRV_LOG(ERR, "Failed to load driver %s",
2828a41f4deSParav Pandit 				driver->pci_driver.driver.name);
2838a41f4deSParav Pandit 			goto probe_err;
2848a41f4deSParav Pandit 		}
2858a41f4deSParav Pandit 		enabled_classes |= driver->driver_class;
2868a41f4deSParav Pandit 	}
2878a41f4deSParav Pandit 	dev->classes_loaded |= enabled_classes;
2888a41f4deSParav Pandit 	return 0;
2898a41f4deSParav Pandit probe_err:
2908a41f4deSParav Pandit 	/* Only unload drivers which are enabled which were enabled
2918a41f4deSParav Pandit 	 * in this probe instance.
2928a41f4deSParav Pandit 	 */
2938a41f4deSParav Pandit 	drivers_remove(dev, enabled_classes);
2948a41f4deSParav Pandit 	return ret;
2958a41f4deSParav Pandit }
2968a41f4deSParav Pandit 
2978a41f4deSParav Pandit /**
2988a41f4deSParav Pandit  * DPDK callback to register to probe multiple drivers for a PCI device.
2998a41f4deSParav Pandit  *
3008a41f4deSParav Pandit  * @param[in] pci_drv
3018a41f4deSParav Pandit  *   PCI driver structure.
3028a41f4deSParav Pandit  * @param[in] dev
3038a41f4deSParav Pandit  *   PCI device information.
3048a41f4deSParav Pandit  *
3058a41f4deSParav Pandit  * @return
3068a41f4deSParav Pandit  *   0 on success, a negative errno value otherwise and rte_errno is set.
3078a41f4deSParav Pandit  */
3088a41f4deSParav Pandit static int
309*ad435d32SXueming Li mlx5_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
3108a41f4deSParav Pandit 	       struct rte_pci_device *pci_dev)
3118a41f4deSParav Pandit {
3128a41f4deSParav Pandit 	struct mlx5_pci_device *dev;
3138a41f4deSParav Pandit 	uint32_t user_classes = 0;
3148a41f4deSParav Pandit 	bool new_device = false;
3158a41f4deSParav Pandit 	int ret;
3168a41f4deSParav Pandit 
3178a41f4deSParav Pandit 	ret = parse_class_options(pci_dev->device.devargs);
3188a41f4deSParav Pandit 	if (ret < 0)
3198a41f4deSParav Pandit 		return ret;
3208a41f4deSParav Pandit 	user_classes = ret;
3218a41f4deSParav Pandit 	if (user_classes) {
3228a41f4deSParav Pandit 		/* Validate combination here. */
3238a41f4deSParav Pandit 		ret = is_valid_class_combination(user_classes);
3248a41f4deSParav Pandit 		if (ret) {
3258a41f4deSParav Pandit 			DRV_LOG(ERR, "Unsupported mlx5 classes supplied.");
3268a41f4deSParav Pandit 			return ret;
3278a41f4deSParav Pandit 		}
3288a41f4deSParav Pandit 	} else {
3298a41f4deSParav Pandit 		/* Default to net class. */
330a99f2f90SXueming Li 		user_classes = MLX5_CLASS_ETH;
3318a41f4deSParav Pandit 	}
3328a41f4deSParav Pandit 	dev = pci_to_mlx5_device(pci_dev);
3338a41f4deSParav Pandit 	if (!dev) {
3348a41f4deSParav Pandit 		dev = rte_zmalloc("mlx5_pci_device", sizeof(*dev), 0);
3358a41f4deSParav Pandit 		if (!dev)
3368a41f4deSParav Pandit 			return -ENOMEM;
3378a41f4deSParav Pandit 		dev->pci_dev = pci_dev;
3388a41f4deSParav Pandit 		TAILQ_INSERT_HEAD(&devices_list, dev, next);
3398a41f4deSParav Pandit 		new_device = true;
3408a41f4deSParav Pandit 	}
3418a41f4deSParav Pandit 	ret = drivers_probe(dev, pci_drv, pci_dev, user_classes);
3428a41f4deSParav Pandit 	if (ret)
3438a41f4deSParav Pandit 		goto class_err;
3448a41f4deSParav Pandit 	return 0;
3458a41f4deSParav Pandit class_err:
3468a41f4deSParav Pandit 	if (new_device)
3478a41f4deSParav Pandit 		dev_release(dev);
3488a41f4deSParav Pandit 	return ret;
3498a41f4deSParav Pandit }
3508a41f4deSParav Pandit 
3518a41f4deSParav Pandit /**
3528a41f4deSParav Pandit  * DPDK callback to remove one or more drivers for a PCI device.
3538a41f4deSParav Pandit  *
3548a41f4deSParav Pandit  * This function removes all drivers probed for a given PCI device.
3558a41f4deSParav Pandit  *
3568a41f4deSParav Pandit  * @param[in] pci_dev
3578a41f4deSParav Pandit  *   Pointer to the PCI device.
3588a41f4deSParav Pandit  *
3598a41f4deSParav Pandit  * @return
3608a41f4deSParav Pandit  *   0 on success, the function cannot fail.
3618a41f4deSParav Pandit  */
3628a41f4deSParav Pandit static int
363*ad435d32SXueming Li mlx5_pci_remove(struct rte_pci_device *pci_dev)
3648a41f4deSParav Pandit {
3658a41f4deSParav Pandit 	struct mlx5_pci_device *dev;
3668a41f4deSParav Pandit 	int ret;
3678a41f4deSParav Pandit 
3688a41f4deSParav Pandit 	dev = pci_to_mlx5_device(pci_dev);
3698a41f4deSParav Pandit 	if (!dev)
3708a41f4deSParav Pandit 		return -ENODEV;
3718a41f4deSParav Pandit 	/* Matching device found, cleanup and unload drivers. */
3728a41f4deSParav Pandit 	ret = drivers_remove(dev, dev->classes_loaded);
3738a41f4deSParav Pandit 	if (!ret)
3748a41f4deSParav Pandit 		dev_release(dev);
3758a41f4deSParav Pandit 	return ret;
3768a41f4deSParav Pandit }
3778a41f4deSParav Pandit 
3788a41f4deSParav Pandit static int
379*ad435d32SXueming Li mlx5_pci_dma_map(struct rte_pci_device *pci_dev, void *addr,
3808a41f4deSParav Pandit 		 uint64_t iova, size_t len)
3818a41f4deSParav Pandit {
3828a41f4deSParav Pandit 	struct mlx5_pci_driver *driver = NULL;
3838a41f4deSParav Pandit 	struct mlx5_pci_driver *temp;
3848a41f4deSParav Pandit 	struct mlx5_pci_device *dev;
3858a41f4deSParav Pandit 	int ret = -EINVAL;
3868a41f4deSParav Pandit 
3878a41f4deSParav Pandit 	dev = pci_to_mlx5_device(pci_dev);
3888a41f4deSParav Pandit 	if (!dev)
3898a41f4deSParav Pandit 		return -ENODEV;
3908a41f4deSParav Pandit 	TAILQ_FOREACH(driver, &drv_list, next) {
3918a41f4deSParav Pandit 		if (device_class_enabled(dev, driver->driver_class) &&
3928a41f4deSParav Pandit 		    driver->pci_driver.dma_map) {
3938a41f4deSParav Pandit 			ret = driver->pci_driver.dma_map(pci_dev, addr,
3948a41f4deSParav Pandit 							 iova, len);
3958a41f4deSParav Pandit 			if (ret)
3968a41f4deSParav Pandit 				goto map_err;
3978a41f4deSParav Pandit 		}
3988a41f4deSParav Pandit 	}
3998a41f4deSParav Pandit 	return ret;
4008a41f4deSParav Pandit map_err:
4018a41f4deSParav Pandit 	TAILQ_FOREACH(temp, &drv_list, next) {
4028a41f4deSParav Pandit 		if (temp == driver)
4038a41f4deSParav Pandit 			break;
4048a41f4deSParav Pandit 		if (device_class_enabled(dev, temp->driver_class) &&
4058a41f4deSParav Pandit 		    temp->pci_driver.dma_map && temp->pci_driver.dma_unmap)
4068a41f4deSParav Pandit 			temp->pci_driver.dma_unmap(pci_dev, addr, iova, len);
4078a41f4deSParav Pandit 	}
4088a41f4deSParav Pandit 	return ret;
4098a41f4deSParav Pandit }
4108a41f4deSParav Pandit 
4118a41f4deSParav Pandit static int
412*ad435d32SXueming Li mlx5_pci_dma_unmap(struct rte_pci_device *pci_dev, void *addr,
4138a41f4deSParav Pandit 		   uint64_t iova, size_t len)
4148a41f4deSParav Pandit {
4158a41f4deSParav Pandit 	struct mlx5_pci_driver *driver;
4168a41f4deSParav Pandit 	struct mlx5_pci_device *dev;
4178a41f4deSParav Pandit 	int local_ret = -EINVAL;
4188a41f4deSParav Pandit 	int ret;
4198a41f4deSParav Pandit 
4208a41f4deSParav Pandit 	dev = pci_to_mlx5_device(pci_dev);
4218a41f4deSParav Pandit 	if (!dev)
4228a41f4deSParav Pandit 		return -ENODEV;
4238a41f4deSParav Pandit 	ret = 0;
4248a41f4deSParav Pandit 	/* There is no unmap error recovery in current implementation. */
4258a41f4deSParav Pandit 	TAILQ_FOREACH_REVERSE(driver, &drv_list, mlx5_pci_bus_drv_head, next) {
4268a41f4deSParav Pandit 		if (device_class_enabled(dev, driver->driver_class) &&
4278a41f4deSParav Pandit 		    driver->pci_driver.dma_unmap) {
4288a41f4deSParav Pandit 			local_ret = driver->pci_driver.dma_unmap(pci_dev, addr,
4298a41f4deSParav Pandit 								 iova, len);
4308a41f4deSParav Pandit 			if (local_ret && (ret == 0))
4318a41f4deSParav Pandit 				ret = local_ret;
4328a41f4deSParav Pandit 		}
4338a41f4deSParav Pandit 	}
4348a41f4deSParav Pandit 	if (local_ret)
4358a41f4deSParav Pandit 		ret = local_ret;
4368a41f4deSParav Pandit 	return ret;
4378a41f4deSParav Pandit }
4388a41f4deSParav Pandit 
4398a41f4deSParav Pandit /* PCI ID table is build dynamically based on registered mlx5 drivers. */
4408a41f4deSParav Pandit static struct rte_pci_id *mlx5_pci_id_table;
4418a41f4deSParav Pandit 
4428a41f4deSParav Pandit static struct rte_pci_driver mlx5_pci_driver = {
4438a41f4deSParav Pandit 	.driver = {
444188773a2SAsaf Penso 		.name = MLX5_PCI_DRIVER_NAME,
4458a41f4deSParav Pandit 	},
446*ad435d32SXueming Li 	.probe = mlx5_pci_probe,
447*ad435d32SXueming Li 	.remove = mlx5_pci_remove,
448*ad435d32SXueming Li 	.dma_map = mlx5_pci_dma_map,
449*ad435d32SXueming Li 	.dma_unmap = mlx5_pci_dma_unmap,
4508a41f4deSParav Pandit };
4518a41f4deSParav Pandit 
4528a41f4deSParav Pandit static int
4538a41f4deSParav Pandit pci_id_table_size_get(const struct rte_pci_id *id_table)
4548a41f4deSParav Pandit {
4558a41f4deSParav Pandit 	int table_size = 0;
4568a41f4deSParav Pandit 
4578a41f4deSParav Pandit 	for (; id_table->vendor_id != 0; id_table++)
4588a41f4deSParav Pandit 		table_size++;
4598a41f4deSParav Pandit 	return table_size;
4608a41f4deSParav Pandit }
4618a41f4deSParav Pandit 
4628a41f4deSParav Pandit static bool
4638a41f4deSParav Pandit pci_id_exists(const struct rte_pci_id *id, const struct rte_pci_id *table,
4648a41f4deSParav Pandit 	      int next_idx)
4658a41f4deSParav Pandit {
4668a41f4deSParav Pandit 	int current_size = next_idx - 1;
4678a41f4deSParav Pandit 	int i;
4688a41f4deSParav Pandit 
4698a41f4deSParav Pandit 	for (i = 0; i < current_size; i++) {
4708a41f4deSParav Pandit 		if (id->device_id == table[i].device_id &&
4718a41f4deSParav Pandit 		    id->vendor_id == table[i].vendor_id &&
4728a41f4deSParav Pandit 		    id->subsystem_vendor_id == table[i].subsystem_vendor_id &&
4738a41f4deSParav Pandit 		    id->subsystem_device_id == table[i].subsystem_device_id)
4748a41f4deSParav Pandit 			return true;
4758a41f4deSParav Pandit 	}
4768a41f4deSParav Pandit 	return false;
4778a41f4deSParav Pandit }
4788a41f4deSParav Pandit 
4798a41f4deSParav Pandit static void
4808a41f4deSParav Pandit pci_id_insert(struct rte_pci_id *new_table, int *next_idx,
4818a41f4deSParav Pandit 	      const struct rte_pci_id *id_table)
4828a41f4deSParav Pandit {
4838a41f4deSParav Pandit 	/* Traverse the id_table, check if entry exists in new_table;
4848a41f4deSParav Pandit 	 * Add non duplicate entries to new table.
4858a41f4deSParav Pandit 	 */
4868a41f4deSParav Pandit 	for (; id_table->vendor_id != 0; id_table++) {
4878a41f4deSParav Pandit 		if (!pci_id_exists(id_table, new_table, *next_idx)) {
4888a41f4deSParav Pandit 			/* New entry; add to the table. */
4898a41f4deSParav Pandit 			new_table[*next_idx] = *id_table;
4908a41f4deSParav Pandit 			(*next_idx)++;
4918a41f4deSParav Pandit 		}
4928a41f4deSParav Pandit 	}
4938a41f4deSParav Pandit }
4948a41f4deSParav Pandit 
4958a41f4deSParav Pandit static int
4968a41f4deSParav Pandit pci_ids_table_update(const struct rte_pci_id *driver_id_table)
4978a41f4deSParav Pandit {
4988a41f4deSParav Pandit 	const struct rte_pci_id *id_iter;
4998a41f4deSParav Pandit 	struct rte_pci_id *updated_table;
5008a41f4deSParav Pandit 	struct rte_pci_id *old_table;
5018a41f4deSParav Pandit 	int num_ids = 0;
5028a41f4deSParav Pandit 	int i = 0;
5038a41f4deSParav Pandit 
5048a41f4deSParav Pandit 	old_table = mlx5_pci_id_table;
5058a41f4deSParav Pandit 	if (old_table)
5068a41f4deSParav Pandit 		num_ids = pci_id_table_size_get(old_table);
5078a41f4deSParav Pandit 	num_ids += pci_id_table_size_get(driver_id_table);
5088a41f4deSParav Pandit 	/* Increase size by one for the termination entry of vendor_id = 0. */
5098a41f4deSParav Pandit 	num_ids += 1;
5108a41f4deSParav Pandit 	updated_table = calloc(num_ids, sizeof(*updated_table));
5118a41f4deSParav Pandit 	if (!updated_table)
5128a41f4deSParav Pandit 		return -ENOMEM;
513*ad435d32SXueming Li 	if (old_table == NULL) {
5148a41f4deSParav Pandit 		/* Copy the first driver's ID table. */
5158a41f4deSParav Pandit 		for (id_iter = driver_id_table; id_iter->vendor_id != 0;
5168a41f4deSParav Pandit 		     id_iter++, i++)
5178a41f4deSParav Pandit 			updated_table[i] = *id_iter;
5188a41f4deSParav Pandit 	} else {
5198a41f4deSParav Pandit 		/* First copy existing table entries. */
5208a41f4deSParav Pandit 		for (id_iter = old_table; id_iter->vendor_id != 0;
5218a41f4deSParav Pandit 		     id_iter++, i++)
5228a41f4deSParav Pandit 			updated_table[i] = *id_iter;
5238a41f4deSParav Pandit 		/* New id to be added at the end of current ID table. */
5248a41f4deSParav Pandit 		pci_id_insert(updated_table, &i, driver_id_table);
5258a41f4deSParav Pandit 	}
5268a41f4deSParav Pandit 	/* Terminate table with empty entry. */
5278a41f4deSParav Pandit 	updated_table[i].vendor_id = 0;
5288a41f4deSParav Pandit 	mlx5_pci_driver.id_table = updated_table;
529*ad435d32SXueming Li 	mlx5_common_pci_driver.id_table = updated_table;
5308a41f4deSParav Pandit 	mlx5_pci_id_table = updated_table;
5318a41f4deSParav Pandit 	if (old_table)
5328a41f4deSParav Pandit 		free(old_table);
5338a41f4deSParav Pandit 	return 0;
5348a41f4deSParav Pandit }
5358a41f4deSParav Pandit 
5368a41f4deSParav Pandit void
5378a41f4deSParav Pandit mlx5_pci_driver_register(struct mlx5_pci_driver *driver)
5388a41f4deSParav Pandit {
5398a41f4deSParav Pandit 	int ret;
5408a41f4deSParav Pandit 
5418a41f4deSParav Pandit 	ret = pci_ids_table_update(driver->pci_driver.id_table);
5428a41f4deSParav Pandit 	if (ret)
5438a41f4deSParav Pandit 		return;
5448a41f4deSParav Pandit 	mlx5_pci_driver.drv_flags |= driver->pci_driver.drv_flags;
5458a41f4deSParav Pandit 	TAILQ_INSERT_TAIL(&drv_list, driver, next);
5468a41f4deSParav Pandit }
5478a41f4deSParav Pandit 
548*ad435d32SXueming Li /********** New common PCI bus driver ********/
549*ad435d32SXueming Li 
550*ad435d32SXueming Li bool
551*ad435d32SXueming Li mlx5_dev_is_pci(const struct rte_device *dev)
552*ad435d32SXueming Li {
553*ad435d32SXueming Li 	return strcmp(dev->bus->name, "pci") == 0;
554*ad435d32SXueming Li }
555*ad435d32SXueming Li 
556*ad435d32SXueming Li bool
557*ad435d32SXueming Li mlx5_dev_pci_match(const struct mlx5_class_driver *drv,
558*ad435d32SXueming Li 		   const struct rte_device *dev)
559*ad435d32SXueming Li {
560*ad435d32SXueming Li 	const struct rte_pci_device *pci_dev;
561*ad435d32SXueming Li 	const struct rte_pci_id *id_table;
562*ad435d32SXueming Li 
563*ad435d32SXueming Li 	if (!mlx5_dev_is_pci(dev))
564*ad435d32SXueming Li 		return false;
565*ad435d32SXueming Li 	pci_dev = RTE_DEV_TO_PCI_CONST(dev);
566*ad435d32SXueming Li 	for (id_table = drv->id_table; id_table->vendor_id != 0;
567*ad435d32SXueming Li 	     id_table++) {
568*ad435d32SXueming Li 		/* Check if device's ids match the class driver's ids. */
569*ad435d32SXueming Li 		if (id_table->vendor_id != pci_dev->id.vendor_id &&
570*ad435d32SXueming Li 		    id_table->vendor_id != RTE_PCI_ANY_ID)
571*ad435d32SXueming Li 			continue;
572*ad435d32SXueming Li 		if (id_table->device_id != pci_dev->id.device_id &&
573*ad435d32SXueming Li 		    id_table->device_id != RTE_PCI_ANY_ID)
574*ad435d32SXueming Li 			continue;
575*ad435d32SXueming Li 		if (id_table->subsystem_vendor_id !=
576*ad435d32SXueming Li 		    pci_dev->id.subsystem_vendor_id &&
577*ad435d32SXueming Li 		    id_table->subsystem_vendor_id != RTE_PCI_ANY_ID)
578*ad435d32SXueming Li 			continue;
579*ad435d32SXueming Li 		if (id_table->subsystem_device_id !=
580*ad435d32SXueming Li 		    pci_dev->id.subsystem_device_id &&
581*ad435d32SXueming Li 		    id_table->subsystem_device_id != RTE_PCI_ANY_ID)
582*ad435d32SXueming Li 			continue;
583*ad435d32SXueming Li 		if (id_table->class_id != pci_dev->id.class_id &&
584*ad435d32SXueming Li 		    id_table->class_id != RTE_CLASS_ANY_ID)
585*ad435d32SXueming Li 			continue;
586*ad435d32SXueming Li 		return true;
587*ad435d32SXueming Li 	}
588*ad435d32SXueming Li 	return false;
589*ad435d32SXueming Li }
590*ad435d32SXueming Li 
591*ad435d32SXueming Li static int
592*ad435d32SXueming Li mlx5_common_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
593*ad435d32SXueming Li 		      struct rte_pci_device *pci_dev)
594*ad435d32SXueming Li {
595*ad435d32SXueming Li 	return mlx5_common_dev_probe(&pci_dev->device);
596*ad435d32SXueming Li }
597*ad435d32SXueming Li 
598*ad435d32SXueming Li static int
599*ad435d32SXueming Li mlx5_common_pci_remove(struct rte_pci_device *pci_dev)
600*ad435d32SXueming Li {
601*ad435d32SXueming Li 	return mlx5_common_dev_remove(&pci_dev->device);
602*ad435d32SXueming Li }
603*ad435d32SXueming Li 
604*ad435d32SXueming Li static int
605*ad435d32SXueming Li mlx5_common_pci_dma_map(struct rte_pci_device *pci_dev, void *addr,
606*ad435d32SXueming Li 			uint64_t iova, size_t len)
607*ad435d32SXueming Li {
608*ad435d32SXueming Li 	return mlx5_common_dev_dma_map(&pci_dev->device, addr, iova, len);
609*ad435d32SXueming Li }
610*ad435d32SXueming Li 
611*ad435d32SXueming Li static int
612*ad435d32SXueming Li mlx5_common_pci_dma_unmap(struct rte_pci_device *pci_dev, void *addr,
613*ad435d32SXueming Li 			  uint64_t iova, size_t len)
614*ad435d32SXueming Li {
615*ad435d32SXueming Li 	return mlx5_common_dev_dma_unmap(&pci_dev->device, addr, iova, len);
616*ad435d32SXueming Li }
617*ad435d32SXueming Li 
618*ad435d32SXueming Li void
619*ad435d32SXueming Li mlx5_common_driver_on_register_pci(struct mlx5_class_driver *driver)
620*ad435d32SXueming Li {
621*ad435d32SXueming Li 	if (driver->id_table != NULL) {
622*ad435d32SXueming Li 		if (pci_ids_table_update(driver->id_table) != 0)
623*ad435d32SXueming Li 			return;
624*ad435d32SXueming Li 	}
625*ad435d32SXueming Li 	if (driver->probe_again)
626*ad435d32SXueming Li 		mlx5_common_pci_driver.drv_flags |= RTE_PCI_DRV_PROBE_AGAIN;
627*ad435d32SXueming Li 	if (driver->intr_lsc)
628*ad435d32SXueming Li 		mlx5_common_pci_driver.drv_flags |= RTE_PCI_DRV_INTR_LSC;
629*ad435d32SXueming Li 	if (driver->intr_rmv)
630*ad435d32SXueming Li 		mlx5_common_pci_driver.drv_flags |= RTE_PCI_DRV_INTR_RMV;
631*ad435d32SXueming Li }
632*ad435d32SXueming Li 
633*ad435d32SXueming Li static struct rte_pci_driver mlx5_common_pci_driver = {
634*ad435d32SXueming Li 	.driver = {
635*ad435d32SXueming Li 		   .name = MLX5_PCI_DRIVER_NAME,
636*ad435d32SXueming Li 	},
637*ad435d32SXueming Li 	.probe = mlx5_common_pci_probe,
638*ad435d32SXueming Li 	.remove = mlx5_common_pci_remove,
639*ad435d32SXueming Li 	.dma_map = mlx5_common_pci_dma_map,
640*ad435d32SXueming Li 	.dma_unmap = mlx5_common_pci_dma_unmap,
641*ad435d32SXueming Li };
642*ad435d32SXueming Li 
6438a41f4deSParav Pandit void mlx5_common_pci_init(void)
6448a41f4deSParav Pandit {
6458a41f4deSParav Pandit 	const struct rte_pci_id empty_table[] = {
6468a41f4deSParav Pandit 		{
6478a41f4deSParav Pandit 			.vendor_id = 0
6488a41f4deSParav Pandit 		},
6498a41f4deSParav Pandit 	};
6508a41f4deSParav Pandit 
6518a41f4deSParav Pandit 	/* All mlx5 PMDs constructor runs at same priority. So any of the PMD
6528a41f4deSParav Pandit 	 * including this one can register the PCI table first. If any other
6538a41f4deSParav Pandit 	 * PMD(s) have registered the PCI ID table, No need to register an empty
6548a41f4deSParav Pandit 	 * default one.
6558a41f4deSParav Pandit 	 */
6568a41f4deSParav Pandit 	if (mlx5_pci_id_table == NULL && pci_ids_table_update(empty_table))
6578a41f4deSParav Pandit 		return;
658*ad435d32SXueming Li 	rte_pci_register(&mlx5_common_pci_driver);
6598a41f4deSParav Pandit }
6608a41f4deSParav Pandit 
6618a41f4deSParav Pandit RTE_FINI(mlx5_common_pci_finish)
6628a41f4deSParav Pandit {
6638a41f4deSParav Pandit 	if (mlx5_pci_id_table != NULL) {
6648a41f4deSParav Pandit 		/* Constructor doesn't register with PCI bus if it failed
6658a41f4deSParav Pandit 		 * to build the table.
6668a41f4deSParav Pandit 		 */
667*ad435d32SXueming Li 		rte_pci_unregister(&mlx5_common_pci_driver);
6688a41f4deSParav Pandit 		free(mlx5_pci_id_table);
6698a41f4deSParav Pandit 	}
6708a41f4deSParav Pandit }
671*ad435d32SXueming Li 
6728a41f4deSParav Pandit RTE_PMD_EXPORT_NAME(mlx5_common_pci, __COUNTER__);
673