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