vdpa/mlx5: introduce Mellanox vDPA driver
[dpdk.git] / drivers / vdpa / mlx5 / mlx5_vdpa.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2019 Mellanox Technologies, Ltd
3  */
4 #include <rte_malloc.h>
5 #include <rte_log.h>
6 #include <rte_errno.h>
7 #include <rte_bus_pci.h>
8 #ifdef PEDANTIC
9 #pragma GCC diagnostic ignored "-Wpedantic"
10 #endif
11 #include <rte_vdpa.h>
12 #ifdef PEDANTIC
13 #pragma GCC diagnostic error "-Wpedantic"
14 #endif
15
16 #include <mlx5_glue.h>
17 #include <mlx5_common.h>
18
19 #include "mlx5_vdpa_utils.h"
20
21
22 struct mlx5_vdpa_priv {
23         TAILQ_ENTRY(mlx5_vdpa_priv) next;
24         int id; /* vDPA device id. */
25         struct ibv_context *ctx; /* Device context. */
26         struct rte_vdpa_dev_addr dev_addr;
27 };
28
29 TAILQ_HEAD(mlx5_vdpa_privs, mlx5_vdpa_priv) priv_list =
30                                               TAILQ_HEAD_INITIALIZER(priv_list);
31 static pthread_mutex_t priv_list_lock = PTHREAD_MUTEX_INITIALIZER;
32 int mlx5_vdpa_logtype;
33
34 static struct rte_vdpa_dev_ops mlx5_vdpa_ops = {
35         .get_queue_num = NULL,
36         .get_features = NULL,
37         .get_protocol_features = NULL,
38         .dev_conf = NULL,
39         .dev_close = NULL,
40         .set_vring_state = NULL,
41         .set_features = NULL,
42         .migration_done = NULL,
43         .get_vfio_group_fd = NULL,
44         .get_vfio_device_fd = NULL,
45         .get_notify_area = NULL,
46 };
47
48 /**
49  * DPDK callback to register a PCI device.
50  *
51  * This function spawns vdpa device out of a given PCI device.
52  *
53  * @param[in] pci_drv
54  *   PCI driver structure (mlx5_vpda_driver).
55  * @param[in] pci_dev
56  *   PCI device information.
57  *
58  * @return
59  *   0 on success, 1 to skip this driver, a negative errno value otherwise
60  *   and rte_errno is set.
61  */
62 static int
63 mlx5_vdpa_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
64                     struct rte_pci_device *pci_dev __rte_unused)
65 {
66         struct ibv_device **ibv_list;
67         struct ibv_device *ibv_match = NULL;
68         struct mlx5_vdpa_priv *priv = NULL;
69         struct ibv_context *ctx = NULL;
70         int ret;
71
72         if (mlx5_class_get(pci_dev->device.devargs) != MLX5_CLASS_VDPA) {
73                 DRV_LOG(DEBUG, "Skip probing - should be probed by other mlx5"
74                         " driver.");
75                 return 1;
76         }
77         errno = 0;
78         ibv_list = mlx5_glue->get_device_list(&ret);
79         if (!ibv_list) {
80                 rte_errno = ENOSYS;
81                 DRV_LOG(ERR, "Failed to get device list, is ib_uverbs loaded?");
82                 return -rte_errno;
83         }
84         while (ret-- > 0) {
85                 struct rte_pci_addr pci_addr;
86
87                 DRV_LOG(DEBUG, "Checking device \"%s\"..", ibv_list[ret]->name);
88                 if (mlx5_dev_to_pci_addr(ibv_list[ret]->ibdev_path, &pci_addr))
89                         continue;
90                 if (pci_dev->addr.domain != pci_addr.domain ||
91                     pci_dev->addr.bus != pci_addr.bus ||
92                     pci_dev->addr.devid != pci_addr.devid ||
93                     pci_dev->addr.function != pci_addr.function)
94                         continue;
95                 DRV_LOG(INFO, "PCI information matches for device \"%s\".",
96                         ibv_list[ret]->name);
97                 ibv_match = ibv_list[ret];
98                 break;
99         }
100         mlx5_glue->free_device_list(ibv_list);
101         if (!ibv_match) {
102                 DRV_LOG(ERR, "No matching IB device for PCI slot "
103                         "%" SCNx32 ":%" SCNx8 ":%" SCNx8 ".%" SCNx8 ".",
104                         pci_dev->addr.domain, pci_dev->addr.bus,
105                         pci_dev->addr.devid, pci_dev->addr.function);
106                 rte_errno = ENOENT;
107                 return -rte_errno;
108         }
109         ctx = mlx5_glue->dv_open_device(ibv_match);
110         if (!ctx) {
111                 DRV_LOG(ERR, "Failed to open IB device \"%s\".",
112                         ibv_match->name);
113                 rte_errno = ENODEV;
114                 return -rte_errno;
115         }
116         priv = rte_zmalloc("mlx5 vDPA device private", sizeof(*priv),
117                            RTE_CACHE_LINE_SIZE);
118         if (!priv) {
119                 DRV_LOG(ERR, "Failed to allocate private memory.");
120                 rte_errno = ENOMEM;
121                 goto error;
122         }
123         priv->ctx = ctx;
124         priv->dev_addr.pci_addr = pci_dev->addr;
125         priv->dev_addr.type = PCI_ADDR;
126         priv->id = rte_vdpa_register_device(&priv->dev_addr, &mlx5_vdpa_ops);
127         if (priv->id < 0) {
128                 DRV_LOG(ERR, "Failed to register vDPA device.");
129                 rte_errno = rte_errno ? rte_errno : EINVAL;
130                 goto error;
131         }
132         pthread_mutex_lock(&priv_list_lock);
133         TAILQ_INSERT_TAIL(&priv_list, priv, next);
134         pthread_mutex_unlock(&priv_list_lock);
135         return 0;
136
137 error:
138         if (priv)
139                 rte_free(priv);
140         if (ctx)
141                 mlx5_glue->close_device(ctx);
142         return -rte_errno;
143 }
144
145 /**
146  * DPDK callback to remove a PCI device.
147  *
148  * This function removes all vDPA devices belong to a given PCI device.
149  *
150  * @param[in] pci_dev
151  *   Pointer to the PCI device.
152  *
153  * @return
154  *   0 on success, the function cannot fail.
155  */
156 static int
157 mlx5_vdpa_pci_remove(struct rte_pci_device *pci_dev)
158 {
159         struct mlx5_vdpa_priv *priv = NULL;
160         int found = 0;
161
162         pthread_mutex_lock(&priv_list_lock);
163         TAILQ_FOREACH(priv, &priv_list, next) {
164                 if (memcmp(&priv->dev_addr.pci_addr, &pci_dev->addr,
165                            sizeof(pci_dev->addr)) == 0) {
166                         found = 1;
167                         break;
168                 }
169         }
170         if (found) {
171                 TAILQ_REMOVE(&priv_list, priv, next);
172                 mlx5_glue->close_device(priv->ctx);
173                 rte_free(priv);
174         }
175         pthread_mutex_unlock(&priv_list_lock);
176         return 0;
177 }
178
179 static const struct rte_pci_id mlx5_vdpa_pci_id_map[] = {
180         {
181                 RTE_PCI_DEVICE(PCI_VENDOR_ID_MELLANOX,
182                                PCI_DEVICE_ID_MELLANOX_CONNECTX5BF)
183         },
184         {
185                 RTE_PCI_DEVICE(PCI_VENDOR_ID_MELLANOX,
186                                PCI_DEVICE_ID_MELLANOX_CONNECTX5BFVF)
187         },
188         {
189                 RTE_PCI_DEVICE(PCI_VENDOR_ID_MELLANOX,
190                                 PCI_DEVICE_ID_MELLANOX_CONNECTX6)
191         },
192         {
193                 RTE_PCI_DEVICE(PCI_VENDOR_ID_MELLANOX,
194                                 PCI_DEVICE_ID_MELLANOX_CONNECTX6VF)
195         },
196         {
197                 RTE_PCI_DEVICE(PCI_VENDOR_ID_MELLANOX,
198                                 PCI_DEVICE_ID_MELLANOX_CONNECTX6DX)
199         },
200         {
201                 RTE_PCI_DEVICE(PCI_VENDOR_ID_MELLANOX,
202                                 PCI_DEVICE_ID_MELLANOX_CONNECTX6DXVF)
203         },
204         {
205                 .vendor_id = 0
206         }
207 };
208
209 static struct rte_pci_driver mlx5_vdpa_driver = {
210         .driver = {
211                 .name = "mlx5_vdpa",
212         },
213         .id_table = mlx5_vdpa_pci_id_map,
214         .probe = mlx5_vdpa_pci_probe,
215         .remove = mlx5_vdpa_pci_remove,
216         .drv_flags = 0,
217 };
218
219 /**
220  * Driver initialization routine.
221  */
222 RTE_INIT(rte_mlx5_vdpa_init)
223 {
224         /* Initialize common log type. */
225         mlx5_vdpa_logtype = rte_log_register("pmd.vdpa.mlx5");
226         if (mlx5_vdpa_logtype >= 0)
227                 rte_log_set_level(mlx5_vdpa_logtype, RTE_LOG_NOTICE);
228         if (mlx5_glue)
229                 rte_pci_register(&mlx5_vdpa_driver);
230 }
231
232 RTE_PMD_EXPORT_NAME(net_mlx5_vdpa, __COUNTER__);
233 RTE_PMD_REGISTER_PCI_TABLE(net_mlx5_vdpa, mlx5_vdpa_pci_id_map);
234 RTE_PMD_REGISTER_KMOD_DEP(net_mlx5_vdpa, "* ib_uverbs & mlx5_core & mlx5_ib");