]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | /* SPDX-License-Identifier: BSD-3-Clause |
2 | * Copyright(c) 2010-2014 Intel Corporation. | |
3 | * Copyright(c) 2014 6WIND S.A. | |
4 | */ | |
5 | ||
6 | #include <stdio.h> | |
7 | #include <string.h> | |
8 | #include <inttypes.h> | |
9 | #include <sys/queue.h> | |
10 | ||
11 | #include <rte_compat.h> | |
12 | #include <rte_bus.h> | |
13 | #include <rte_class.h> | |
14 | #include <rte_dev.h> | |
15 | #include <rte_devargs.h> | |
16 | #include <rte_debug.h> | |
17 | #include <rte_errno.h> | |
18 | #include <rte_kvargs.h> | |
19 | #include <rte_log.h> | |
20 | #include <rte_spinlock.h> | |
21 | #include <rte_malloc.h> | |
9f95a23c | 22 | #include <rte_string_fns.h> |
11fdf7f2 TL |
23 | |
24 | #include "eal_private.h" | |
9f95a23c | 25 | #include "hotplug_mp.h" |
11fdf7f2 TL |
26 | |
27 | /** | |
28 | * The device event callback description. | |
29 | * | |
30 | * It contains callback address to be registered by user application, | |
31 | * the pointer to the parameters for callback, and the device name. | |
32 | */ | |
33 | struct dev_event_callback { | |
34 | TAILQ_ENTRY(dev_event_callback) next; /**< Callbacks list */ | |
35 | rte_dev_event_cb_fn cb_fn; /**< Callback address */ | |
36 | void *cb_arg; /**< Callback parameter */ | |
37 | char *dev_name; /**< Callback device name, NULL is for all device */ | |
38 | uint32_t active; /**< Callback is executing */ | |
39 | }; | |
40 | ||
41 | /** @internal Structure to keep track of registered callbacks */ | |
42 | TAILQ_HEAD(dev_event_cb_list, dev_event_callback); | |
43 | ||
44 | /* The device event callback list for all registered callbacks. */ | |
45 | static struct dev_event_cb_list dev_event_cbs; | |
46 | ||
47 | /* spinlock for device callbacks */ | |
48 | static rte_spinlock_t dev_event_lock = RTE_SPINLOCK_INITIALIZER; | |
49 | ||
50 | struct dev_next_ctx { | |
51 | struct rte_dev_iterator *it; | |
52 | const char *bus_str; | |
53 | const char *cls_str; | |
54 | }; | |
55 | ||
56 | #define CTX(it, bus_str, cls_str) \ | |
57 | (&(const struct dev_next_ctx){ \ | |
58 | .it = it, \ | |
59 | .bus_str = bus_str, \ | |
60 | .cls_str = cls_str, \ | |
61 | }) | |
62 | ||
63 | #define ITCTX(ptr) \ | |
64 | (((struct dev_next_ctx *)(intptr_t)ptr)->it) | |
65 | ||
66 | #define BUSCTX(ptr) \ | |
67 | (((struct dev_next_ctx *)(intptr_t)ptr)->bus_str) | |
68 | ||
69 | #define CLSCTX(ptr) \ | |
70 | (((struct dev_next_ctx *)(intptr_t)ptr)->cls_str) | |
71 | ||
72 | static int cmp_dev_name(const struct rte_device *dev, const void *_name) | |
73 | { | |
74 | const char *name = _name; | |
75 | ||
76 | return strcmp(dev->name, name); | |
77 | } | |
78 | ||
9f95a23c TL |
79 | int |
80 | rte_dev_is_probed(const struct rte_device *dev) | |
11fdf7f2 | 81 | { |
9f95a23c TL |
82 | /* The field driver should be set only when the probe is successful. */ |
83 | return dev->driver != NULL; | |
84 | } | |
85 | ||
86 | /* helper function to build devargs, caller should free the memory */ | |
87 | static int | |
88 | build_devargs(const char *busname, const char *devname, | |
89 | const char *drvargs, char **devargs) | |
90 | { | |
91 | int length; | |
11fdf7f2 | 92 | |
9f95a23c TL |
93 | length = snprintf(NULL, 0, "%s:%s,%s", busname, devname, drvargs); |
94 | if (length < 0) | |
11fdf7f2 | 95 | return -EINVAL; |
11fdf7f2 | 96 | |
9f95a23c TL |
97 | *devargs = malloc(length + 1); |
98 | if (*devargs == NULL) | |
99 | return -ENOMEM; | |
100 | ||
101 | length = snprintf(*devargs, length + 1, "%s:%s,%s", | |
102 | busname, devname, drvargs); | |
103 | if (length < 0) { | |
104 | free(*devargs); | |
11fdf7f2 TL |
105 | return -EINVAL; |
106 | } | |
11fdf7f2 | 107 | |
9f95a23c | 108 | return 0; |
11fdf7f2 TL |
109 | } |
110 | ||
9f95a23c TL |
111 | int |
112 | rte_eal_hotplug_add(const char *busname, const char *devname, | |
113 | const char *drvargs) | |
11fdf7f2 | 114 | { |
9f95a23c TL |
115 | |
116 | char *devargs; | |
11fdf7f2 TL |
117 | int ret; |
118 | ||
9f95a23c TL |
119 | ret = build_devargs(busname, devname, drvargs, &devargs); |
120 | if (ret != 0) | |
121 | return ret; | |
11fdf7f2 | 122 | |
9f95a23c TL |
123 | ret = rte_dev_probe(devargs); |
124 | free(devargs); | |
11fdf7f2 | 125 | |
11fdf7f2 TL |
126 | return ret; |
127 | } | |
128 | ||
9f95a23c TL |
129 | /* probe device at local process. */ |
130 | int | |
131 | local_dev_probe(const char *devargs, struct rte_device **new_dev) | |
11fdf7f2 | 132 | { |
11fdf7f2 TL |
133 | struct rte_device *dev; |
134 | struct rte_devargs *da; | |
135 | int ret; | |
136 | ||
9f95a23c | 137 | *new_dev = NULL; |
11fdf7f2 TL |
138 | da = calloc(1, sizeof(*da)); |
139 | if (da == NULL) | |
140 | return -ENOMEM; | |
141 | ||
9f95a23c | 142 | ret = rte_devargs_parse(da, devargs); |
11fdf7f2 TL |
143 | if (ret) |
144 | goto err_devarg; | |
145 | ||
9f95a23c TL |
146 | if (da->bus->plug == NULL) { |
147 | RTE_LOG(ERR, EAL, "Function plug not supported by bus (%s)\n", | |
148 | da->bus->name); | |
149 | ret = -ENOTSUP; | |
150 | goto err_devarg; | |
151 | } | |
152 | ||
153 | ret = rte_devargs_insert(&da); | |
11fdf7f2 TL |
154 | if (ret) |
155 | goto err_devarg; | |
156 | ||
9f95a23c TL |
157 | /* the rte_devargs will be referenced in the matching rte_device */ |
158 | ret = da->bus->scan(); | |
11fdf7f2 TL |
159 | if (ret) |
160 | goto err_devarg; | |
161 | ||
9f95a23c | 162 | dev = da->bus->find_device(NULL, cmp_dev_name, da->name); |
11fdf7f2 TL |
163 | if (dev == NULL) { |
164 | RTE_LOG(ERR, EAL, "Cannot find device (%s)\n", | |
9f95a23c | 165 | da->name); |
11fdf7f2 TL |
166 | ret = -ENODEV; |
167 | goto err_devarg; | |
168 | } | |
9f95a23c TL |
169 | /* Since there is a matching device, it is now its responsibility |
170 | * to manage the devargs we've just inserted. From this point | |
171 | * those devargs shouldn't be removed manually anymore. | |
172 | */ | |
11fdf7f2 | 173 | |
9f95a23c TL |
174 | ret = dev->bus->plug(dev); |
175 | if (ret && !rte_dev_is_probed(dev)) { /* if hasn't ever succeeded */ | |
11fdf7f2 TL |
176 | RTE_LOG(ERR, EAL, "Driver cannot attach the device (%s)\n", |
177 | dev->name); | |
9f95a23c | 178 | return ret; |
11fdf7f2 | 179 | } |
9f95a23c TL |
180 | |
181 | *new_dev = dev; | |
182 | return ret; | |
11fdf7f2 TL |
183 | |
184 | err_devarg: | |
9f95a23c | 185 | if (rte_devargs_remove(da) != 0) { |
11fdf7f2 TL |
186 | free(da->args); |
187 | free(da); | |
188 | } | |
189 | return ret; | |
190 | } | |
191 | ||
9f95a23c TL |
192 | int |
193 | rte_dev_probe(const char *devargs) | |
11fdf7f2 | 194 | { |
9f95a23c | 195 | struct eal_dev_mp_req req; |
11fdf7f2 TL |
196 | struct rte_device *dev; |
197 | int ret; | |
198 | ||
9f95a23c TL |
199 | memset(&req, 0, sizeof(req)); |
200 | req.t = EAL_DEV_REQ_TYPE_ATTACH; | |
201 | strlcpy(req.devargs, devargs, EAL_DEV_MP_DEV_ARGS_MAX_LEN); | |
202 | ||
203 | if (rte_eal_process_type() != RTE_PROC_PRIMARY) { | |
204 | /** | |
205 | * If in secondary process, just send IPC request to | |
206 | * primary process. | |
207 | */ | |
208 | ret = eal_dev_hotplug_request_to_primary(&req); | |
209 | if (ret != 0) { | |
210 | RTE_LOG(ERR, EAL, | |
211 | "Failed to send hotplug request to primary\n"); | |
212 | return -ENOMSG; | |
213 | } | |
214 | if (req.result != 0) | |
215 | RTE_LOG(ERR, EAL, | |
216 | "Failed to hotplug add device\n"); | |
217 | return req.result; | |
218 | } | |
219 | ||
220 | /* attach a shared device from primary start from here: */ | |
221 | ||
222 | /* primary attach the new device itself. */ | |
223 | ret = local_dev_probe(devargs, &dev); | |
224 | ||
225 | if (ret != 0) { | |
226 | RTE_LOG(ERR, EAL, | |
227 | "Failed to attach device on primary process\n"); | |
228 | ||
229 | /** | |
230 | * it is possible that secondary process failed to attached a | |
231 | * device that primary process have during initialization, | |
232 | * so for -EEXIST case, we still need to sync with secondary | |
233 | * process. | |
234 | */ | |
235 | if (ret != -EEXIST) | |
236 | return ret; | |
237 | } | |
238 | ||
239 | /* primary send attach sync request to secondary. */ | |
240 | ret = eal_dev_hotplug_request_to_secondary(&req); | |
241 | ||
242 | /* if any communication error, we need to rollback. */ | |
243 | if (ret != 0) { | |
244 | RTE_LOG(ERR, EAL, | |
245 | "Failed to send hotplug add request to secondary\n"); | |
246 | ret = -ENOMSG; | |
247 | goto rollback; | |
248 | } | |
249 | ||
250 | /** | |
251 | * if any secondary failed to attach, we need to consider if rollback | |
252 | * is necessary. | |
253 | */ | |
254 | if (req.result != 0) { | |
255 | RTE_LOG(ERR, EAL, | |
256 | "Failed to attach device on secondary process\n"); | |
257 | ret = req.result; | |
258 | ||
259 | /* for -EEXIST, we don't need to rollback. */ | |
260 | if (ret == -EEXIST) | |
261 | return ret; | |
262 | goto rollback; | |
263 | } | |
264 | ||
265 | return 0; | |
266 | ||
267 | rollback: | |
268 | req.t = EAL_DEV_REQ_TYPE_ATTACH_ROLLBACK; | |
269 | ||
270 | /* primary send rollback request to secondary. */ | |
271 | if (eal_dev_hotplug_request_to_secondary(&req) != 0) | |
272 | RTE_LOG(WARNING, EAL, | |
273 | "Failed to rollback device attach on secondary." | |
274 | "Devices in secondary may not sync with primary\n"); | |
275 | ||
276 | /* primary rollback itself. */ | |
277 | if (local_dev_remove(dev) != 0) | |
278 | RTE_LOG(WARNING, EAL, | |
279 | "Failed to rollback device attach on primary." | |
280 | "Devices in secondary may not sync with primary\n"); | |
281 | ||
282 | return ret; | |
283 | } | |
284 | ||
285 | int | |
286 | rte_eal_hotplug_remove(const char *busname, const char *devname) | |
287 | { | |
288 | struct rte_device *dev; | |
289 | struct rte_bus *bus; | |
290 | ||
11fdf7f2 TL |
291 | bus = rte_bus_find_by_name(busname); |
292 | if (bus == NULL) { | |
293 | RTE_LOG(ERR, EAL, "Cannot find bus (%s)\n", busname); | |
294 | return -ENOENT; | |
295 | } | |
296 | ||
11fdf7f2 TL |
297 | dev = bus->find_device(NULL, cmp_dev_name, devname); |
298 | if (dev == NULL) { | |
299 | RTE_LOG(ERR, EAL, "Cannot find plugged device (%s)\n", devname); | |
300 | return -EINVAL; | |
301 | } | |
302 | ||
9f95a23c TL |
303 | return rte_dev_remove(dev); |
304 | } | |
305 | ||
306 | /* remove device at local process. */ | |
307 | int | |
308 | local_dev_remove(struct rte_device *dev) | |
309 | { | |
310 | int ret; | |
311 | ||
312 | if (dev->bus->unplug == NULL) { | |
313 | RTE_LOG(ERR, EAL, "Function unplug not supported by bus (%s)\n", | |
314 | dev->bus->name); | |
315 | return -ENOTSUP; | |
11fdf7f2 TL |
316 | } |
317 | ||
9f95a23c TL |
318 | ret = dev->bus->unplug(dev); |
319 | if (ret) { | |
11fdf7f2 TL |
320 | RTE_LOG(ERR, EAL, "Driver cannot detach the device (%s)\n", |
321 | dev->name); | |
9f95a23c TL |
322 | return ret; |
323 | } | |
324 | ||
325 | return 0; | |
326 | } | |
327 | ||
328 | int | |
329 | rte_dev_remove(struct rte_device *dev) | |
330 | { | |
331 | struct eal_dev_mp_req req; | |
332 | char *devargs; | |
333 | int ret; | |
334 | ||
335 | if (!rte_dev_is_probed(dev)) { | |
336 | RTE_LOG(ERR, EAL, "Device is not probed\n"); | |
337 | return -ENOENT; | |
338 | } | |
339 | ||
340 | ret = build_devargs(dev->bus->name, dev->name, "", &devargs); | |
341 | if (ret != 0) | |
342 | return ret; | |
343 | ||
344 | memset(&req, 0, sizeof(req)); | |
345 | req.t = EAL_DEV_REQ_TYPE_DETACH; | |
346 | strlcpy(req.devargs, devargs, EAL_DEV_MP_DEV_ARGS_MAX_LEN); | |
347 | free(devargs); | |
348 | ||
349 | if (rte_eal_process_type() != RTE_PROC_PRIMARY) { | |
350 | /** | |
351 | * If in secondary process, just send IPC request to | |
352 | * primary process. | |
353 | */ | |
354 | ret = eal_dev_hotplug_request_to_primary(&req); | |
355 | if (ret != 0) { | |
356 | RTE_LOG(ERR, EAL, | |
357 | "Failed to send hotplug request to primary\n"); | |
358 | return -ENOMSG; | |
359 | } | |
360 | if (req.result != 0) | |
361 | RTE_LOG(ERR, EAL, | |
362 | "Failed to hotplug remove device\n"); | |
363 | return req.result; | |
364 | } | |
365 | ||
366 | /* detach a device from primary start from here: */ | |
367 | ||
368 | /* primary send detach sync request to secondary */ | |
369 | ret = eal_dev_hotplug_request_to_secondary(&req); | |
370 | ||
371 | /** | |
372 | * if communication error, we need to rollback, because it is possible | |
373 | * part of the secondary processes still detached it successfully. | |
374 | */ | |
375 | if (ret != 0) { | |
376 | RTE_LOG(ERR, EAL, | |
377 | "Failed to send device detach request to secondary\n"); | |
378 | ret = -ENOMSG; | |
379 | goto rollback; | |
380 | } | |
381 | ||
382 | /** | |
383 | * if any secondary failed to detach, we need to consider if rollback | |
384 | * is necessary. | |
385 | */ | |
386 | if (req.result != 0) { | |
387 | RTE_LOG(ERR, EAL, | |
388 | "Failed to detach device on secondary process\n"); | |
389 | ret = req.result; | |
390 | /** | |
391 | * if -ENOENT, we don't need to rollback, since devices is | |
392 | * already detached on secondary process. | |
393 | */ | |
394 | if (ret != -ENOENT) | |
395 | goto rollback; | |
396 | } | |
397 | ||
398 | /* primary detach the device itself. */ | |
399 | ret = local_dev_remove(dev); | |
400 | ||
401 | /* if primary failed, still need to consider if rollback is necessary */ | |
402 | if (ret != 0) { | |
403 | RTE_LOG(ERR, EAL, | |
404 | "Failed to detach device on primary process\n"); | |
405 | /* if -ENOENT, we don't need to rollback */ | |
406 | if (ret == -ENOENT) | |
407 | return ret; | |
408 | goto rollback; | |
409 | } | |
410 | ||
411 | return 0; | |
412 | ||
413 | rollback: | |
414 | req.t = EAL_DEV_REQ_TYPE_DETACH_ROLLBACK; | |
415 | ||
416 | /* primary send rollback request to secondary. */ | |
417 | if (eal_dev_hotplug_request_to_secondary(&req) != 0) | |
418 | RTE_LOG(WARNING, EAL, | |
419 | "Failed to rollback device detach on secondary." | |
420 | "Devices in secondary may not sync with primary\n"); | |
421 | ||
11fdf7f2 TL |
422 | return ret; |
423 | } | |
424 | ||
425 | int __rte_experimental | |
426 | rte_dev_event_callback_register(const char *device_name, | |
427 | rte_dev_event_cb_fn cb_fn, | |
428 | void *cb_arg) | |
429 | { | |
430 | struct dev_event_callback *event_cb; | |
431 | int ret; | |
432 | ||
433 | if (!cb_fn) | |
434 | return -EINVAL; | |
435 | ||
436 | rte_spinlock_lock(&dev_event_lock); | |
437 | ||
438 | if (TAILQ_EMPTY(&dev_event_cbs)) | |
439 | TAILQ_INIT(&dev_event_cbs); | |
440 | ||
441 | TAILQ_FOREACH(event_cb, &dev_event_cbs, next) { | |
442 | if (event_cb->cb_fn == cb_fn && event_cb->cb_arg == cb_arg) { | |
443 | if (device_name == NULL && event_cb->dev_name == NULL) | |
444 | break; | |
445 | if (device_name == NULL || event_cb->dev_name == NULL) | |
446 | continue; | |
447 | if (!strcmp(event_cb->dev_name, device_name)) | |
448 | break; | |
449 | } | |
450 | } | |
451 | ||
452 | /* create a new callback. */ | |
453 | if (event_cb == NULL) { | |
454 | event_cb = malloc(sizeof(struct dev_event_callback)); | |
455 | if (event_cb != NULL) { | |
456 | event_cb->cb_fn = cb_fn; | |
457 | event_cb->cb_arg = cb_arg; | |
458 | event_cb->active = 0; | |
459 | if (!device_name) { | |
460 | event_cb->dev_name = NULL; | |
461 | } else { | |
462 | event_cb->dev_name = strdup(device_name); | |
463 | if (event_cb->dev_name == NULL) { | |
464 | ret = -ENOMEM; | |
465 | goto error; | |
466 | } | |
467 | } | |
468 | TAILQ_INSERT_TAIL(&dev_event_cbs, event_cb, next); | |
469 | } else { | |
470 | RTE_LOG(ERR, EAL, | |
471 | "Failed to allocate memory for device " | |
472 | "event callback."); | |
473 | ret = -ENOMEM; | |
474 | goto error; | |
475 | } | |
476 | } else { | |
477 | RTE_LOG(ERR, EAL, | |
478 | "The callback is already exist, no need " | |
479 | "to register again.\n"); | |
480 | ret = -EEXIST; | |
481 | } | |
482 | ||
483 | rte_spinlock_unlock(&dev_event_lock); | |
484 | return 0; | |
485 | error: | |
486 | free(event_cb); | |
487 | rte_spinlock_unlock(&dev_event_lock); | |
488 | return ret; | |
489 | } | |
490 | ||
491 | int __rte_experimental | |
492 | rte_dev_event_callback_unregister(const char *device_name, | |
493 | rte_dev_event_cb_fn cb_fn, | |
494 | void *cb_arg) | |
495 | { | |
496 | int ret = 0; | |
497 | struct dev_event_callback *event_cb, *next; | |
498 | ||
499 | if (!cb_fn) | |
500 | return -EINVAL; | |
501 | ||
502 | rte_spinlock_lock(&dev_event_lock); | |
503 | /*walk through the callbacks and remove all that match. */ | |
504 | for (event_cb = TAILQ_FIRST(&dev_event_cbs); event_cb != NULL; | |
505 | event_cb = next) { | |
506 | ||
507 | next = TAILQ_NEXT(event_cb, next); | |
508 | ||
509 | if (device_name != NULL && event_cb->dev_name != NULL) { | |
510 | if (!strcmp(event_cb->dev_name, device_name)) { | |
511 | if (event_cb->cb_fn != cb_fn || | |
512 | (cb_arg != (void *)-1 && | |
513 | event_cb->cb_arg != cb_arg)) | |
514 | continue; | |
515 | } | |
516 | } else if (device_name != NULL) { | |
517 | continue; | |
518 | } | |
519 | ||
520 | /* | |
521 | * if this callback is not executing right now, | |
522 | * then remove it. | |
523 | */ | |
524 | if (event_cb->active == 0) { | |
525 | TAILQ_REMOVE(&dev_event_cbs, event_cb, next); | |
526 | free(event_cb); | |
527 | ret++; | |
528 | } else { | |
529 | continue; | |
530 | } | |
531 | } | |
532 | rte_spinlock_unlock(&dev_event_lock); | |
533 | return ret; | |
534 | } | |
535 | ||
9f95a23c TL |
536 | void __rte_experimental |
537 | rte_dev_event_callback_process(const char *device_name, | |
538 | enum rte_dev_event_type event) | |
11fdf7f2 TL |
539 | { |
540 | struct dev_event_callback *cb_lst; | |
541 | ||
542 | if (device_name == NULL) | |
543 | return; | |
544 | ||
545 | rte_spinlock_lock(&dev_event_lock); | |
546 | ||
547 | TAILQ_FOREACH(cb_lst, &dev_event_cbs, next) { | |
548 | if (cb_lst->dev_name) { | |
549 | if (strcmp(cb_lst->dev_name, device_name)) | |
550 | continue; | |
551 | } | |
552 | cb_lst->active = 1; | |
553 | rte_spinlock_unlock(&dev_event_lock); | |
554 | cb_lst->cb_fn(device_name, event, | |
555 | cb_lst->cb_arg); | |
556 | rte_spinlock_lock(&dev_event_lock); | |
557 | cb_lst->active = 0; | |
558 | } | |
559 | rte_spinlock_unlock(&dev_event_lock); | |
560 | } | |
561 | ||
562 | __rte_experimental | |
563 | int | |
564 | rte_dev_iterator_init(struct rte_dev_iterator *it, | |
565 | const char *dev_str) | |
566 | { | |
567 | struct rte_devargs devargs; | |
568 | struct rte_class *cls = NULL; | |
569 | struct rte_bus *bus = NULL; | |
570 | ||
571 | /* Having both bus_str and cls_str NULL is illegal, | |
572 | * marking this iterator as invalid unless | |
573 | * everything goes well. | |
574 | */ | |
575 | it->bus_str = NULL; | |
576 | it->cls_str = NULL; | |
577 | ||
578 | devargs.data = dev_str; | |
579 | if (rte_devargs_layers_parse(&devargs, dev_str)) | |
580 | goto get_out; | |
581 | ||
582 | bus = devargs.bus; | |
583 | cls = devargs.cls; | |
584 | /* The string should have at least | |
585 | * one layer specified. | |
586 | */ | |
587 | if (bus == NULL && cls == NULL) { | |
588 | RTE_LOG(ERR, EAL, | |
589 | "Either bus or class must be specified.\n"); | |
590 | rte_errno = EINVAL; | |
591 | goto get_out; | |
592 | } | |
593 | if (bus != NULL && bus->dev_iterate == NULL) { | |
594 | RTE_LOG(ERR, EAL, "Bus %s not supported\n", bus->name); | |
595 | rte_errno = ENOTSUP; | |
596 | goto get_out; | |
597 | } | |
598 | if (cls != NULL && cls->dev_iterate == NULL) { | |
599 | RTE_LOG(ERR, EAL, "Class %s not supported\n", cls->name); | |
600 | rte_errno = ENOTSUP; | |
601 | goto get_out; | |
602 | } | |
603 | it->bus_str = devargs.bus_str; | |
604 | it->cls_str = devargs.cls_str; | |
605 | it->dev_str = dev_str; | |
606 | it->bus = bus; | |
607 | it->cls = cls; | |
608 | it->device = NULL; | |
609 | it->class_device = NULL; | |
610 | get_out: | |
611 | return -rte_errno; | |
612 | } | |
613 | ||
614 | static char * | |
615 | dev_str_sane_copy(const char *str) | |
616 | { | |
617 | size_t end; | |
618 | char *copy; | |
619 | ||
620 | end = strcspn(str, ",/"); | |
621 | if (str[end] == ',') { | |
622 | copy = strdup(&str[end + 1]); | |
623 | } else { | |
624 | /* '/' or '\0' */ | |
625 | copy = strdup(""); | |
626 | } | |
627 | if (copy == NULL) { | |
628 | rte_errno = ENOMEM; | |
629 | } else { | |
630 | char *slash; | |
631 | ||
632 | slash = strchr(copy, '/'); | |
633 | if (slash != NULL) | |
634 | slash[0] = '\0'; | |
635 | } | |
636 | return copy; | |
637 | } | |
638 | ||
639 | static int | |
640 | class_next_dev_cmp(const struct rte_class *cls, | |
641 | const void *ctx) | |
642 | { | |
643 | struct rte_dev_iterator *it; | |
644 | const char *cls_str = NULL; | |
645 | void *dev; | |
646 | ||
647 | if (cls->dev_iterate == NULL) | |
648 | return 1; | |
649 | it = ITCTX(ctx); | |
650 | cls_str = CLSCTX(ctx); | |
651 | dev = it->class_device; | |
652 | /* it->cls_str != NULL means a class | |
653 | * was specified in the devstr. | |
654 | */ | |
655 | if (it->cls_str != NULL && cls != it->cls) | |
656 | return 1; | |
657 | /* If an error occurred previously, | |
658 | * no need to test further. | |
659 | */ | |
660 | if (rte_errno != 0) | |
661 | return -1; | |
662 | dev = cls->dev_iterate(dev, cls_str, it); | |
663 | it->class_device = dev; | |
664 | return dev == NULL; | |
665 | } | |
666 | ||
667 | static int | |
668 | bus_next_dev_cmp(const struct rte_bus *bus, | |
669 | const void *ctx) | |
670 | { | |
671 | struct rte_device *dev = NULL; | |
672 | struct rte_class *cls = NULL; | |
673 | struct rte_dev_iterator *it; | |
674 | const char *bus_str = NULL; | |
675 | ||
676 | if (bus->dev_iterate == NULL) | |
677 | return 1; | |
678 | it = ITCTX(ctx); | |
679 | bus_str = BUSCTX(ctx); | |
680 | dev = it->device; | |
681 | /* it->bus_str != NULL means a bus | |
682 | * was specified in the devstr. | |
683 | */ | |
684 | if (it->bus_str != NULL && bus != it->bus) | |
685 | return 1; | |
686 | /* If an error occurred previously, | |
687 | * no need to test further. | |
688 | */ | |
689 | if (rte_errno != 0) | |
690 | return -1; | |
691 | if (it->cls_str == NULL) { | |
692 | dev = bus->dev_iterate(dev, bus_str, it); | |
693 | goto end; | |
694 | } | |
695 | /* cls_str != NULL */ | |
696 | if (dev == NULL) { | |
697 | next_dev_on_bus: | |
698 | dev = bus->dev_iterate(dev, bus_str, it); | |
699 | it->device = dev; | |
700 | } | |
701 | if (dev == NULL) | |
702 | return 1; | |
703 | if (it->cls != NULL) | |
704 | cls = TAILQ_PREV(it->cls, rte_class_list, next); | |
705 | cls = rte_class_find(cls, class_next_dev_cmp, ctx); | |
706 | if (cls != NULL) { | |
707 | it->cls = cls; | |
708 | goto end; | |
709 | } | |
710 | goto next_dev_on_bus; | |
711 | end: | |
712 | it->device = dev; | |
713 | return dev == NULL; | |
714 | } | |
715 | __rte_experimental | |
716 | struct rte_device * | |
717 | rte_dev_iterator_next(struct rte_dev_iterator *it) | |
718 | { | |
719 | struct rte_bus *bus = NULL; | |
720 | int old_errno = rte_errno; | |
721 | char *bus_str = NULL; | |
722 | char *cls_str = NULL; | |
723 | ||
724 | rte_errno = 0; | |
725 | if (it->bus_str == NULL && it->cls_str == NULL) { | |
726 | /* Invalid iterator. */ | |
727 | rte_errno = EINVAL; | |
728 | return NULL; | |
729 | } | |
730 | if (it->bus != NULL) | |
731 | bus = TAILQ_PREV(it->bus, rte_bus_list, next); | |
732 | if (it->bus_str != NULL) { | |
733 | bus_str = dev_str_sane_copy(it->bus_str); | |
734 | if (bus_str == NULL) | |
735 | goto out; | |
736 | } | |
737 | if (it->cls_str != NULL) { | |
738 | cls_str = dev_str_sane_copy(it->cls_str); | |
739 | if (cls_str == NULL) | |
740 | goto out; | |
741 | } | |
742 | while ((bus = rte_bus_find(bus, bus_next_dev_cmp, | |
743 | CTX(it, bus_str, cls_str)))) { | |
744 | if (it->device != NULL) { | |
745 | it->bus = bus; | |
746 | goto out; | |
747 | } | |
748 | if (it->bus_str != NULL || | |
749 | rte_errno != 0) | |
750 | break; | |
751 | } | |
752 | if (rte_errno == 0) | |
753 | rte_errno = old_errno; | |
754 | out: | |
755 | free(bus_str); | |
756 | free(cls_str); | |
757 | return it->device; | |
758 | } | |
9f95a23c TL |
759 | |
760 | int | |
761 | rte_dev_dma_map(struct rte_device *dev, void *addr, uint64_t iova, | |
762 | size_t len) | |
763 | { | |
764 | if (dev->bus->dma_map == NULL || len == 0) { | |
765 | rte_errno = ENOTSUP; | |
766 | return -1; | |
767 | } | |
768 | /* Memory must be registered through rte_extmem_* APIs */ | |
769 | if (rte_mem_virt2memseg_list(addr) == NULL) { | |
770 | rte_errno = EINVAL; | |
771 | return -1; | |
772 | } | |
773 | ||
774 | return dev->bus->dma_map(dev, addr, iova, len); | |
775 | } | |
776 | ||
777 | int | |
778 | rte_dev_dma_unmap(struct rte_device *dev, void *addr, uint64_t iova, | |
779 | size_t len) | |
780 | { | |
781 | if (dev->bus->dma_unmap == NULL || len == 0) { | |
782 | rte_errno = ENOTSUP; | |
783 | return -1; | |
784 | } | |
785 | /* Memory must be registered through rte_extmem_* APIs */ | |
786 | if (rte_mem_virt2memseg_list(addr) == NULL) { | |
787 | rte_errno = EINVAL; | |
788 | return -1; | |
789 | } | |
790 | ||
791 | return dev->bus->dma_unmap(dev, addr, iova, len); | |
792 | } |