dma/idxd: add bus device probing
[dpdk.git] / drivers / dma / idxd / idxd_bus.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2021 Intel Corporation
3  */
4
5 #include <dirent.h>
6 #include <fcntl.h>
7 #include <unistd.h>
8 #include <sys/mman.h>
9 #include <libgen.h>
10
11 #include <rte_bus.h>
12 #include <rte_eal.h>
13 #include <rte_log.h>
14 #include <rte_dmadev_pmd.h>
15 #include <rte_string_fns.h>
16
17 #include "idxd_internal.h"
18
19 /* default value for DSA paths, but allow override in environment for testing */
20 #define DSA_DEV_PATH "/dev/dsa"
21 #define DSA_SYSFS_PATH "/sys/bus/dsa/devices"
22
23 static unsigned int devcount;
24
25 /** unique identifier for a DSA device/WQ instance */
26 struct dsa_wq_addr {
27         uint16_t device_id;
28         uint16_t wq_id;
29 };
30
31 /** a DSA device instance */
32 struct rte_dsa_device {
33         struct rte_device device;           /**< Inherit core device */
34         TAILQ_ENTRY(rte_dsa_device) next;   /**< next dev in list */
35
36         char wq_name[32];                   /**< the workqueue name/number e.g. wq0.1 */
37         struct dsa_wq_addr addr;            /**< Identifies the specific WQ */
38 };
39
40 /* forward prototypes */
41 struct dsa_bus;
42 static int dsa_scan(void);
43 static int dsa_probe(void);
44 static struct rte_device *dsa_find_device(const struct rte_device *start,
45                 rte_dev_cmp_t cmp,  const void *data);
46 static enum rte_iova_mode dsa_get_iommu_class(void);
47 static int dsa_addr_parse(const char *name, void *addr);
48
49 /** List of devices */
50 TAILQ_HEAD(dsa_device_list, rte_dsa_device);
51
52 /**
53  * Structure describing the DSA bus
54  */
55 struct dsa_bus {
56         struct rte_bus bus;               /**< Inherit the generic class */
57         struct rte_driver driver;         /**< Driver struct for devices to point to */
58         struct dsa_device_list device_list;  /**< List of PCI devices */
59 };
60
61 struct dsa_bus dsa_bus = {
62         .bus = {
63                 .scan = dsa_scan,
64                 .probe = dsa_probe,
65                 .find_device = dsa_find_device,
66                 .get_iommu_class = dsa_get_iommu_class,
67                 .parse = dsa_addr_parse,
68         },
69         .driver = {
70                 .name = "dmadev_idxd"
71         },
72         .device_list = TAILQ_HEAD_INITIALIZER(dsa_bus.device_list),
73 };
74
75 static inline const char *
76 dsa_get_dev_path(void)
77 {
78         const char *path = getenv("DSA_DEV_PATH");
79         return path ? path : DSA_DEV_PATH;
80 }
81
82 static inline const char *
83 dsa_get_sysfs_path(void)
84 {
85         const char *path = getenv("DSA_SYSFS_PATH");
86         return path ? path : DSA_SYSFS_PATH;
87 }
88
89 static void *
90 idxd_bus_mmap_wq(struct rte_dsa_device *dev)
91 {
92         void *addr;
93         char path[PATH_MAX];
94         int fd;
95
96         snprintf(path, sizeof(path), "%s/%s", dsa_get_dev_path(), dev->wq_name);
97         fd = open(path, O_RDWR);
98         if (fd < 0) {
99                 IDXD_PMD_ERR("Failed to open device path: %s", path);
100                 return NULL;
101         }
102
103         addr = mmap(NULL, 0x1000, PROT_WRITE, MAP_SHARED, fd, 0);
104         close(fd);
105         if (addr == MAP_FAILED) {
106                 IDXD_PMD_ERR("Failed to mmap device %s", path);
107                 return NULL;
108         }
109
110         return addr;
111 }
112
113 static int
114 read_wq_string(struct rte_dsa_device *dev, const char *filename,
115                 char *value, size_t valuelen)
116 {
117         char sysfs_node[PATH_MAX];
118         int len;
119         int fd;
120
121         snprintf(sysfs_node, sizeof(sysfs_node), "%s/%s/%s",
122                         dsa_get_sysfs_path(), dev->wq_name, filename);
123         fd = open(sysfs_node, O_RDONLY);
124         if (fd < 0) {
125                 IDXD_PMD_ERR("%s(): opening file '%s' failed: %s",
126                                 __func__, sysfs_node, strerror(errno));
127                 return -1;
128         }
129
130         len = read(fd, value, valuelen - 1);
131         close(fd);
132         if (len < 0) {
133                 IDXD_PMD_ERR("%s(): error reading file '%s': %s",
134                                 __func__, sysfs_node, strerror(errno));
135                 return -1;
136         }
137         value[len] = '\0';
138         return 0;
139 }
140
141 static int
142 read_wq_int(struct rte_dsa_device *dev, const char *filename,
143                 int *value)
144 {
145         char sysfs_node[PATH_MAX];
146         FILE *f;
147         int ret = 0;
148
149         snprintf(sysfs_node, sizeof(sysfs_node), "%s/%s/%s",
150                         dsa_get_sysfs_path(), dev->wq_name, filename);
151         f = fopen(sysfs_node, "r");
152         if (f == NULL) {
153                 IDXD_PMD_ERR("%s(): opening file '%s' failed: %s",
154                                 __func__, sysfs_node, strerror(errno));
155                 return -1;
156         }
157
158         if (fscanf(f, "%d", value) != 1) {
159                 IDXD_PMD_ERR("%s(): error reading file '%s': %s",
160                                 __func__, sysfs_node, strerror(errno));
161                 ret = -1;
162         }
163
164         fclose(f);
165         return ret;
166 }
167
168 static int
169 read_device_int(struct rte_dsa_device *dev, const char *filename,
170                 int *value)
171 {
172         char sysfs_node[PATH_MAX];
173         FILE *f;
174         int ret = 0;
175
176         snprintf(sysfs_node, sizeof(sysfs_node), "%s/dsa%d/%s",
177                         dsa_get_sysfs_path(), dev->addr.device_id, filename);
178         f = fopen(sysfs_node, "r");
179         if (f == NULL) {
180                 IDXD_PMD_ERR("%s(): opening file '%s' failed: %s",
181                                 __func__, sysfs_node, strerror(errno));
182                 return -1;
183         }
184
185         if (fscanf(f, "%d", value) != 1) {
186                 IDXD_PMD_ERR("%s(): error reading file '%s': %s",
187                                 __func__, sysfs_node, strerror(errno));
188                 ret = -1;
189         }
190
191         fclose(f);
192         return ret;
193 }
194
195 static int
196 idxd_probe_dsa(struct rte_dsa_device *dev)
197 {
198         struct idxd_dmadev idxd = {0};
199         int ret = 0;
200
201         IDXD_PMD_INFO("Probing device %s on numa node %d",
202                         dev->wq_name, dev->device.numa_node);
203         if (read_wq_int(dev, "size", &ret) < 0)
204                 return -1;
205         idxd.max_batches = ret;
206         if (read_wq_int(dev, "max_batch_size", &ret) < 0)
207                 return -1;
208         idxd.max_batch_size = ret;
209         idxd.qid = dev->addr.wq_id;
210         idxd.sva_support = 1;
211
212         idxd.portal = idxd_bus_mmap_wq(dev);
213         if (idxd.portal == NULL) {
214                 IDXD_PMD_ERR("WQ mmap failed");
215                 return -ENOENT;
216         }
217
218         return 0;
219 }
220
221 static int
222 is_for_this_process_use(const char *name)
223 {
224         char *runtime_dir = strdup(rte_eal_get_runtime_dir());
225         char *prefix = basename(runtime_dir);
226         int prefixlen = strlen(prefix);
227         int retval = 0;
228
229         if (strncmp(name, "dpdk_", 5) == 0)
230                 retval = 1;
231         if (strncmp(name, prefix, prefixlen) == 0 && name[prefixlen] == '_')
232                 retval = 1;
233
234         free(runtime_dir);
235         return retval;
236 }
237
238 static int
239 dsa_probe(void)
240 {
241         struct rte_dsa_device *dev;
242
243         TAILQ_FOREACH(dev, &dsa_bus.device_list, next) {
244                 char type[64], name[64];
245
246                 if (read_wq_string(dev, "type", type, sizeof(type)) < 0 ||
247                                 read_wq_string(dev, "name", name, sizeof(name)) < 0)
248                         continue;
249
250                 if (strncmp(type, "user", 4) == 0 && is_for_this_process_use(name)) {
251                         dev->device.driver = &dsa_bus.driver;
252                         idxd_probe_dsa(dev);
253                         continue;
254                 }
255                 IDXD_PMD_DEBUG("WQ '%s', not allocated to DPDK", dev->wq_name);
256         }
257
258         return 0;
259 }
260
261 static int
262 dsa_scan(void)
263 {
264         const char *path = dsa_get_dev_path();
265         struct dirent *wq;
266         DIR *dev_dir;
267
268         dev_dir = opendir(path);
269         if (dev_dir == NULL) {
270                 if (errno == ENOENT)
271                         return 0; /* no bus, return without error */
272                 IDXD_PMD_ERR("%s(): opendir '%s' failed: %s",
273                                 __func__, path, strerror(errno));
274                 return -1;
275         }
276
277         while ((wq = readdir(dev_dir)) != NULL) {
278                 struct rte_dsa_device *dev;
279                 int numa_node = -1;
280
281                 if (strncmp(wq->d_name, "wq", 2) != 0)
282                         continue;
283                 if (strnlen(wq->d_name, sizeof(dev->wq_name)) == sizeof(dev->wq_name)) {
284                         IDXD_PMD_ERR("%s(): wq name too long: '%s', skipping",
285                                         __func__, wq->d_name);
286                         continue;
287                 }
288                 IDXD_PMD_DEBUG("%s(): found %s/%s", __func__, path, wq->d_name);
289
290                 dev = malloc(sizeof(*dev));
291                 if (dsa_addr_parse(wq->d_name, &dev->addr) < 0) {
292                         IDXD_PMD_ERR("Error parsing WQ name: %s", wq->d_name);
293                         free(dev);
294                         continue;
295                 }
296                 dev->device.bus = &dsa_bus.bus;
297                 strlcpy(dev->wq_name, wq->d_name, sizeof(dev->wq_name));
298                 TAILQ_INSERT_TAIL(&dsa_bus.device_list, dev, next);
299                 devcount++;
300
301                 read_device_int(dev, "numa_node", &numa_node);
302                 dev->device.numa_node = numa_node;
303                 dev->device.name = dev->wq_name;
304         }
305
306         closedir(dev_dir);
307         return 0;
308 }
309
310 static struct rte_device *
311 dsa_find_device(const struct rte_device *start, rte_dev_cmp_t cmp,
312                          const void *data)
313 {
314         struct rte_dsa_device *dev = TAILQ_FIRST(&dsa_bus.device_list);
315
316         /* the rte_device struct must be at start of dsa structure */
317         RTE_BUILD_BUG_ON(offsetof(struct rte_dsa_device, device) != 0);
318
319         if (start != NULL) /* jump to start point if given */
320                 dev = TAILQ_NEXT((const struct rte_dsa_device *)start, next);
321         while (dev != NULL) {
322                 if (cmp(&dev->device, data) == 0)
323                         return &dev->device;
324                 dev = TAILQ_NEXT(dev, next);
325         }
326         return NULL;
327 }
328
329 static enum rte_iova_mode
330 dsa_get_iommu_class(void)
331 {
332         /* if there are no devices, report don't care, otherwise VA mode */
333         return devcount > 0 ? RTE_IOVA_VA : RTE_IOVA_DC;
334 }
335
336 static int
337 dsa_addr_parse(const char *name, void *addr)
338 {
339         struct dsa_wq_addr *wq = addr;
340         unsigned int device_id, wq_id;
341
342         if (sscanf(name, "wq%u.%u", &device_id, &wq_id) != 2) {
343                 IDXD_PMD_DEBUG("Parsing WQ name failed: %s", name);
344                 return -1;
345         }
346
347         wq->device_id = device_id;
348         wq->wq_id = wq_id;
349         return 0;
350 }
351
352 RTE_REGISTER_BUS(dsa, dsa_bus.bus);