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 : 185 : const char *path = getenv("DSA_DEV_PATH");
82 [ + - - - ]: 185 : 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 : : int retval = 0;
265 : : int prefixlen;
266 : : char *prefix;
267 : :
268 [ # # ]: 0 : if (runtime_dir == NULL)
269 : : return retval;
270 : :
271 : 0 : prefix = basename(runtime_dir);
272 : 0 : prefixlen = strlen(prefix);
273 : :
274 [ # # ]: 0 : if (strncmp(name, "dpdk_", 5) == 0)
275 : : retval = 1;
276 [ # # # # ]: 0 : if (strncmp(name, prefix, prefixlen) == 0 && name[prefixlen] == '_')
277 : : retval = 1;
278 : :
279 [ # # # # ]: 0 : if (retval && dsa_bus.bus.conf.scan_mode != RTE_BUS_SCAN_UNDEFINED) {
280 [ # # ]: 0 : if (dsa_bus.bus.conf.scan_mode == RTE_BUS_SCAN_ALLOWLIST)
281 : 0 : retval = search_devargs(dev->device.name);
282 : : else
283 : 0 : retval = !search_devargs(dev->device.name);
284 : : }
285 : :
286 : 0 : free(runtime_dir);
287 : 0 : return retval;
288 : : }
289 : :
290 : : static int
291 : 180 : dsa_probe(void)
292 : : {
293 : : struct rte_dsa_device *dev;
294 : :
295 [ - + ]: 180 : TAILQ_FOREACH(dev, &dsa_bus.device_list, next) {
296 : : char type[64], name[64];
297 : :
298 [ # # # # ]: 0 : if (read_wq_string(dev, "type", type, sizeof(type)) < 0 ||
299 : 0 : read_wq_string(dev, "name", name, sizeof(name)) < 0)
300 : 0 : continue;
301 : :
302 [ # # # # ]: 0 : if (strncmp(type, "user", 4) == 0 &&
303 : 0 : is_for_this_process_use(dev, name)) {
304 : 0 : dev->device.driver = &dsa_bus.driver;
305 : 0 : idxd_probe_dsa(dev);
306 : 0 : continue;
307 : : }
308 : 0 : IDXD_PMD_DEBUG("WQ '%s', not allocated to DPDK", dev->wq_name);
309 : : }
310 : :
311 : 180 : return 0;
312 : : }
313 : :
314 : : static int
315 : 185 : dsa_scan(void)
316 : : {
317 : : const char *path = dsa_get_dev_path();
318 : : struct dirent *wq;
319 : : DIR *dev_dir;
320 : :
321 : 185 : dev_dir = opendir(path);
322 [ + - ]: 185 : if (dev_dir == NULL) {
323 [ - + ]: 185 : if (errno == ENOENT)
324 : : return 0; /* no bus, return without error */
325 : 0 : IDXD_PMD_ERR("%s(): opendir '%s' failed: %s",
326 : : __func__, path, strerror(errno));
327 : 0 : return -1;
328 : : }
329 : :
330 [ # # ]: 0 : while ((wq = readdir(dev_dir)) != NULL) {
331 : : struct rte_dsa_device *dev;
332 : 0 : int numa_node = SOCKET_ID_ANY;
333 : :
334 [ # # ]: 0 : if (strncmp(wq->d_name, "wq", 2) != 0)
335 : 0 : continue;
336 [ # # ]: 0 : if (strnlen(wq->d_name, sizeof(dev->wq_name)) == sizeof(dev->wq_name)) {
337 : 0 : IDXD_PMD_ERR("%s(): wq name too long: '%s', skipping",
338 : : __func__, wq->d_name);
339 : 0 : continue;
340 : : }
341 : 0 : IDXD_PMD_DEBUG("%s(): found %s/%s", __func__, path, wq->d_name);
342 : :
343 : 0 : dev = malloc(sizeof(*dev));
344 [ # # ]: 0 : if (dev == NULL) {
345 : 0 : closedir(dev_dir);
346 : 0 : return -ENOMEM;
347 : : }
348 [ # # ]: 0 : if (dsa_addr_parse(wq->d_name, &dev->addr) < 0) {
349 : 0 : IDXD_PMD_ERR("Error parsing WQ name: %s", wq->d_name);
350 : 0 : free(dev);
351 : 0 : continue;
352 : : }
353 : 0 : dev->device.bus = &dsa_bus.bus;
354 : 0 : strlcpy(dev->wq_name, wq->d_name, sizeof(dev->wq_name));
355 : 0 : TAILQ_INSERT_TAIL(&dsa_bus.device_list, dev, next);
356 : 0 : devcount++;
357 : :
358 : 0 : read_device_int(dev, "numa_node", &numa_node);
359 : 0 : dev->device.numa_node = numa_node;
360 : 0 : dev->device.name = dev->wq_name;
361 : : }
362 : :
363 : 0 : closedir(dev_dir);
364 : 0 : return 0;
365 : : }
366 : :
367 : : static struct rte_device *
368 : 0 : dsa_find_device(const struct rte_device *start, rte_dev_cmp_t cmp,
369 : : const void *data)
370 : : {
371 : 0 : struct rte_dsa_device *dev = TAILQ_FIRST(&dsa_bus.device_list);
372 : :
373 : : /* the rte_device struct must be at start of dsa structure */
374 : : RTE_BUILD_BUG_ON(offsetof(struct rte_dsa_device, device) != 0);
375 : :
376 [ # # ]: 0 : if (start != NULL) /* jump to start point if given */
377 : 0 : dev = TAILQ_NEXT((const struct rte_dsa_device *)start, next);
378 [ # # ]: 0 : while (dev != NULL) {
379 [ # # ]: 0 : if (cmp(&dev->device, data) == 0)
380 : 0 : return &dev->device;
381 : 0 : dev = TAILQ_NEXT(dev, next);
382 : : }
383 : : return NULL;
384 : : }
385 : :
386 : : static enum rte_iova_mode
387 : 185 : dsa_get_iommu_class(void)
388 : : {
389 : : /* if there are no devices, report don't care, otherwise VA mode */
390 [ + - ]: 185 : return devcount > 0 ? RTE_IOVA_VA : RTE_IOVA_DC;
391 : : }
392 : :
393 : : static int
394 : 15 : dsa_addr_parse(const char *name, void *addr)
395 : : {
396 : : struct dsa_wq_addr *wq = addr;
397 : : unsigned int device_id, wq_id;
398 : :
399 [ + - ]: 15 : if (sscanf(name, "wq%u.%u", &device_id, &wq_id) != 2) {
400 : 15 : IDXD_PMD_DEBUG("Parsing WQ name failed: %s", name);
401 : 15 : return -1;
402 : : }
403 : :
404 [ # # ]: 0 : if (wq != NULL) {
405 : 0 : wq->device_id = device_id;
406 : 0 : wq->wq_id = wq_id;
407 : : }
408 : :
409 : : return 0;
410 : : }
411 : :
412 : 252 : RTE_REGISTER_BUS(dsa, dsa_bus.bus);
|