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