Line data Source code
1 : /* SPDX-License-Identifier: BSD-3-Clause
2 : * Copyright (C) 2020 Intel Corporation. All rights reserved.
3 : * All rights reserved.
4 : */
5 :
6 : #include "spdk_internal/usdt.h"
7 :
8 : #include "spdk/env.h"
9 : #include "spdk/log.h"
10 : #include "spdk/queue.h"
11 : #include "spdk/util.h"
12 :
13 : #include "spdk/fd_group.h"
14 :
15 : #define SPDK_MAX_EVENT_NAME_LEN 256
16 :
17 : enum event_handler_state {
18 : /* The event_handler is added into an fd_group waiting for event,
19 : * but not currently in the execution of a wait loop.
20 : */
21 : EVENT_HANDLER_STATE_WAITING,
22 :
23 : /* The event_handler is currently in the execution of a wait loop. */
24 : EVENT_HANDLER_STATE_RUNNING,
25 :
26 : /* The event_handler was removed during the execution of a wait loop. */
27 : EVENT_HANDLER_STATE_REMOVED,
28 : };
29 :
30 : /* Taking "ehdlr" as short name for file descriptor handler of the interrupt event. */
31 : struct event_handler {
32 : TAILQ_ENTRY(event_handler) next;
33 : enum event_handler_state state;
34 :
35 : spdk_fd_fn fn;
36 : void *fn_arg;
37 : /* file descriptor of the interrupt event */
38 : int fd;
39 : uint32_t events;
40 : uint32_t fd_type;
41 : char name[SPDK_MAX_EVENT_NAME_LEN + 1];
42 : };
43 :
44 : struct spdk_fd_group {
45 : int epfd;
46 :
47 : /* Number of fds registered in this group. The epoll file descriptor of this fd group
48 : * i.e. epfd waits for interrupt event on all the fds from its interrupt sources list, as
49 : * well as from all its children fd group interrupt sources list.
50 : */
51 : uint32_t num_fds;
52 :
53 : struct spdk_fd_group *parent;
54 :
55 : /* interrupt sources list */
56 : TAILQ_HEAD(, event_handler) event_handlers;
57 : TAILQ_HEAD(, spdk_fd_group) children;
58 : TAILQ_ENTRY(spdk_fd_group) link;
59 : };
60 :
61 : int
62 0 : spdk_fd_group_get_fd(struct spdk_fd_group *fgrp)
63 : {
64 0 : return fgrp->epfd;
65 : }
66 :
67 : #ifdef __linux__
68 :
69 : static __thread struct epoll_event *g_event = NULL;
70 :
71 : int
72 : spdk_fd_group_get_epoll_event(struct epoll_event *event)
73 : {
74 : if (g_event == NULL) {
75 : return -EINVAL;
76 : }
77 : *event = *g_event;
78 : return 0;
79 : }
80 :
81 : static int
82 : _fd_group_del_all(int epfd, struct spdk_fd_group *grp)
83 : {
84 : struct event_handler *ehdlr = NULL;
85 : struct epoll_event epevent = {0};
86 : int rc;
87 : int ret = 0;
88 :
89 : TAILQ_FOREACH(ehdlr, &grp->event_handlers, next) {
90 : rc = epoll_ctl(epfd, EPOLL_CTL_DEL, ehdlr->fd, NULL);
91 : if (rc < 0) {
92 : if (errno == ENOENT) {
93 : /* This is treated as success. It happens if there are multiple
94 : * attempts to remove fds from the group.
95 : */
96 : continue;
97 : }
98 :
99 : ret = -errno;
100 : SPDK_ERRLOG("Failed to remove fd: %d from group: %s\n",
101 : ehdlr->fd, strerror(errno));
102 : goto recover;
103 : }
104 : ret++;
105 : }
106 :
107 : return ret;
108 :
109 : recover:
110 : /* We failed to remove everything. Let's try to put everything back into
111 : * the original group. */
112 : TAILQ_FOREACH(ehdlr, &grp->event_handlers, next) {
113 : epevent.events = ehdlr->events;
114 : epevent.data.ptr = ehdlr;
115 : rc = epoll_ctl(epfd, EPOLL_CTL_ADD, ehdlr->fd, &epevent);
116 : if (rc < 0) {
117 : if (errno == EEXIST) {
118 : /* This is fine. Keep going. */
119 : continue;
120 : }
121 :
122 : /* Continue on even though we've failed. But indicate
123 : * this is a fatal error. */
124 : SPDK_ERRLOG("Failed to recover fd_group_del_all: %s\n", strerror(errno));
125 : ret = -ENOTRECOVERABLE;
126 : }
127 : }
128 :
129 : return ret;
130 : }
131 :
132 : static int
133 : _fd_group_add_all(int epfd, struct spdk_fd_group *grp)
134 : {
135 : struct event_handler *ehdlr = NULL;
136 : struct epoll_event epevent = {0};
137 : int rc;
138 : int ret = 0;
139 :
140 : /* Hoist the fds from the child up into the parent */
141 : TAILQ_FOREACH(ehdlr, &grp->event_handlers, next) {
142 : epevent.events = ehdlr->events;
143 : epevent.data.ptr = ehdlr;
144 : rc = epoll_ctl(epfd, EPOLL_CTL_ADD, ehdlr->fd, &epevent);
145 : if (rc < 0) {
146 : if (errno == EEXIST) {
147 : /* This is treated as success */
148 : continue;
149 : }
150 :
151 : ret = -errno;
152 : SPDK_ERRLOG("Failed to add fd: %d to fd group: %s\n",
153 : ehdlr->fd, strerror(errno));
154 : goto recover;
155 : }
156 : ret++;
157 : }
158 :
159 : return ret;
160 :
161 : recover:
162 : /* We failed to add everything, so try to remove what we did add. */
163 : TAILQ_FOREACH(ehdlr, &grp->event_handlers, next) {
164 : rc = epoll_ctl(epfd, EPOLL_CTL_DEL, ehdlr->fd, NULL);
165 : if (rc < 0) {
166 : if (errno == ENOENT) {
167 : /* This is treated as success. */
168 : continue;
169 : }
170 :
171 :
172 : /* Continue on even though we've failed. But indicate
173 : * this is a fatal error. */
174 : SPDK_ERRLOG("Failed to recover fd_group_del_all: %s\n", strerror(errno));
175 : ret = -ENOTRECOVERABLE;
176 : }
177 : }
178 :
179 : return ret;
180 : }
181 :
182 : int
183 : spdk_fd_group_unnest(struct spdk_fd_group *parent, struct spdk_fd_group *child)
184 : {
185 : int rc;
186 :
187 : if (parent == NULL || child == NULL) {
188 : return -EINVAL;
189 : }
190 :
191 : if (child->parent != parent) {
192 : return -EINVAL;
193 : }
194 :
195 : rc = _fd_group_del_all(parent->epfd, child);
196 : if (rc < 0) {
197 : return rc;
198 : } else {
199 : assert(parent->num_fds >= (uint32_t)rc);
200 : parent->num_fds -= rc;
201 : }
202 :
203 : child->parent = NULL;
204 : TAILQ_REMOVE(&parent->children, child, link);
205 :
206 : rc = _fd_group_add_all(child->epfd, child);
207 : if (rc < 0) {
208 : return rc;
209 : } else {
210 : child->num_fds += rc;
211 : }
212 :
213 : return 0;
214 : }
215 :
216 : int
217 : spdk_fd_group_nest(struct spdk_fd_group *parent, struct spdk_fd_group *child)
218 : {
219 : int rc;
220 :
221 : if (parent == NULL || child == NULL) {
222 : return -EINVAL;
223 : }
224 :
225 : if (child->parent) {
226 : return -EINVAL;
227 : }
228 :
229 : if (parent->parent) {
230 : /* More than one layer of nesting is currently not supported */
231 : assert(false);
232 : return -ENOTSUP;
233 : }
234 :
235 : rc = _fd_group_del_all(child->epfd, child);
236 : if (rc < 0) {
237 : return rc;
238 : } else {
239 : assert(child->num_fds >= (uint32_t)rc);
240 : child->num_fds -= rc;
241 : }
242 :
243 : rc = _fd_group_add_all(parent->epfd, child);
244 : if (rc < 0) {
245 : return rc;
246 : } else {
247 : parent->num_fds += rc;
248 : }
249 :
250 : child->parent = parent;
251 : TAILQ_INSERT_TAIL(&parent->children, child, link);
252 :
253 : return 0;
254 : }
255 :
256 : void
257 : spdk_fd_group_get_default_event_handler_opts(struct spdk_event_handler_opts *opts,
258 : size_t opts_size)
259 : {
260 : if (!opts) {
261 : SPDK_ERRLOG("opts should not be NULL\n");
262 : return;
263 : }
264 :
265 : if (!opts_size) {
266 : SPDK_ERRLOG("opts_size should not be zero value\n");
267 : return;
268 : }
269 :
270 : memset(opts, 0, opts_size);
271 : opts->opts_size = opts_size;
272 :
273 : #define FIELD_OK(field) \
274 : offsetof(struct spdk_event_handler_opts, field) + sizeof(opts->field) <= opts_size
275 :
276 : #define SET_FIELD(field, value) \
277 : if (FIELD_OK(field)) { \
278 : opts->field = value; \
279 : } \
280 :
281 : SET_FIELD(events, EPOLLIN);
282 : SET_FIELD(fd_type, SPDK_FD_TYPE_DEFAULT);
283 :
284 : #undef FIELD_OK
285 : #undef SET_FIELD
286 : }
287 :
288 : static void
289 : event_handler_opts_copy(const struct spdk_event_handler_opts *src,
290 : struct spdk_event_handler_opts *dst)
291 : {
292 : if (!src->opts_size) {
293 : SPDK_ERRLOG("opts_size should not be zero value\n");
294 : assert(false);
295 : }
296 :
297 : #define FIELD_OK(field) \
298 : offsetof(struct spdk_event_handler_opts, field) + sizeof(src->field) <= src->opts_size
299 :
300 : #define SET_FIELD(field) \
301 : if (FIELD_OK(field)) { \
302 : dst->field = src->field; \
303 : } \
304 :
305 : SET_FIELD(events);
306 : SET_FIELD(fd_type);
307 :
308 : dst->opts_size = src->opts_size;
309 :
310 : /* You should not remove this statement, but need to update the assert statement
311 : * if you add a new field, and also add a corresponding SET_FIELD statement */
312 : SPDK_STATIC_ASSERT(sizeof(struct spdk_event_handler_opts) == 16, "Incorrect size");
313 :
314 : #undef FIELD_OK
315 : #undef SET_FIELD
316 : }
317 :
318 : int
319 : spdk_fd_group_add(struct spdk_fd_group *fgrp, int efd, spdk_fd_fn fn,
320 : void *arg, const char *name)
321 : {
322 : return spdk_fd_group_add_for_events(fgrp, efd, EPOLLIN, fn, arg, name);
323 : }
324 :
325 : int
326 : spdk_fd_group_add_for_events(struct spdk_fd_group *fgrp, int efd, uint32_t events,
327 : spdk_fd_fn fn, void *arg, const char *name)
328 : {
329 : struct spdk_event_handler_opts opts = {};
330 :
331 : spdk_fd_group_get_default_event_handler_opts(&opts, sizeof(opts));
332 : opts.events = events;
333 : opts.fd_type = SPDK_FD_TYPE_DEFAULT;
334 :
335 : return spdk_fd_group_add_ext(fgrp, efd, fn, arg, name, &opts);
336 : }
337 :
338 : int
339 : spdk_fd_group_add_ext(struct spdk_fd_group *fgrp, int efd, spdk_fd_fn fn, void *arg,
340 : const char *name, struct spdk_event_handler_opts *opts)
341 : {
342 : struct event_handler *ehdlr = NULL;
343 : struct epoll_event epevent = {0};
344 : struct spdk_event_handler_opts eh_opts = {};
345 : int rc;
346 : int epfd;
347 :
348 : /* parameter checking */
349 : if (fgrp == NULL || efd < 0 || fn == NULL) {
350 : return -EINVAL;
351 : }
352 :
353 : spdk_fd_group_get_default_event_handler_opts(&eh_opts, sizeof(eh_opts));
354 : if (opts) {
355 : event_handler_opts_copy(opts, &eh_opts);
356 : }
357 :
358 : /* check if there is already one function registered for this fd */
359 : TAILQ_FOREACH(ehdlr, &fgrp->event_handlers, next) {
360 : if (ehdlr->fd == efd) {
361 : return -EEXIST;
362 : }
363 : }
364 :
365 : /* create a new event src */
366 : ehdlr = calloc(1, sizeof(*ehdlr));
367 : if (ehdlr == NULL) {
368 : return -errno;
369 : }
370 :
371 : ehdlr->fd = efd;
372 : ehdlr->fn = fn;
373 : ehdlr->fn_arg = arg;
374 : ehdlr->state = EVENT_HANDLER_STATE_WAITING;
375 : ehdlr->events = eh_opts.events;
376 : ehdlr->fd_type = eh_opts.fd_type;
377 : snprintf(ehdlr->name, sizeof(ehdlr->name), "%s", name);
378 :
379 : if (fgrp->parent) {
380 : epfd = fgrp->parent->epfd;
381 : } else {
382 : epfd = fgrp->epfd;
383 : }
384 :
385 : epevent.events = ehdlr->events;
386 : epevent.data.ptr = ehdlr;
387 : rc = epoll_ctl(epfd, EPOLL_CTL_ADD, efd, &epevent);
388 : if (rc < 0) {
389 : SPDK_ERRLOG("Failed to add fd: %d to fd group(%p): %s\n",
390 : efd, fgrp, strerror(errno));
391 : free(ehdlr);
392 : return -errno;
393 : }
394 :
395 : TAILQ_INSERT_TAIL(&fgrp->event_handlers, ehdlr, next);
396 : if (fgrp->parent) {
397 : fgrp->parent->num_fds++;
398 : } else {
399 : fgrp->num_fds++;
400 : }
401 :
402 : return 0;
403 : }
404 :
405 : void
406 : spdk_fd_group_remove(struct spdk_fd_group *fgrp, int efd)
407 : {
408 : struct event_handler *ehdlr;
409 : int rc;
410 : int epfd;
411 :
412 : if (fgrp == NULL || efd < 0) {
413 : SPDK_ERRLOG("Cannot remove fd: %d from fd group(%p)\n", efd, fgrp);
414 : assert(0);
415 : return;
416 : }
417 :
418 :
419 : TAILQ_FOREACH(ehdlr, &fgrp->event_handlers, next) {
420 : if (ehdlr->fd == efd) {
421 : break;
422 : }
423 : }
424 :
425 : if (ehdlr == NULL) {
426 : SPDK_ERRLOG("fd: %d doesn't exist in fd group(%p)\n", efd, fgrp);
427 : return;
428 : }
429 :
430 : assert(ehdlr->state != EVENT_HANDLER_STATE_REMOVED);
431 :
432 : if (fgrp->parent) {
433 : epfd = fgrp->parent->epfd;
434 : } else {
435 : epfd = fgrp->epfd;
436 : }
437 :
438 : rc = epoll_ctl(epfd, EPOLL_CTL_DEL, ehdlr->fd, NULL);
439 : if (rc < 0) {
440 : SPDK_ERRLOG("Failed to remove fd: %d from fd group(%p): %s\n",
441 : ehdlr->fd, fgrp, strerror(errno));
442 : return;
443 : }
444 :
445 : if (fgrp->parent) {
446 : assert(fgrp->parent->num_fds > 0);
447 : fgrp->parent->num_fds--;
448 : } else {
449 : assert(fgrp->num_fds > 0);
450 : fgrp->num_fds--;
451 : }
452 : TAILQ_REMOVE(&fgrp->event_handlers, ehdlr, next);
453 :
454 : /* Delay ehdlr's free in case it is waiting for execution in fgrp wait loop */
455 : if (ehdlr->state == EVENT_HANDLER_STATE_RUNNING) {
456 : ehdlr->state = EVENT_HANDLER_STATE_REMOVED;
457 : } else {
458 : free(ehdlr);
459 : }
460 : }
461 :
462 : int
463 : spdk_fd_group_event_modify(struct spdk_fd_group *fgrp,
464 : int efd, int event_types)
465 : {
466 : struct epoll_event epevent;
467 : struct event_handler *ehdlr;
468 : int epfd;
469 :
470 : if (fgrp == NULL || efd < 0) {
471 : return -EINVAL;
472 : }
473 :
474 : TAILQ_FOREACH(ehdlr, &fgrp->event_handlers, next) {
475 : if (ehdlr->fd == efd) {
476 : break;
477 : }
478 : }
479 :
480 : if (ehdlr == NULL) {
481 : return -EINVAL;
482 : }
483 :
484 : assert(ehdlr->state != EVENT_HANDLER_STATE_REMOVED);
485 :
486 : ehdlr->events = event_types;
487 :
488 : if (fgrp->parent) {
489 : epfd = fgrp->parent->epfd;
490 : } else {
491 : epfd = fgrp->epfd;
492 : }
493 :
494 : epevent.events = ehdlr->events;
495 : epevent.data.ptr = ehdlr;
496 :
497 : return epoll_ctl(epfd, EPOLL_CTL_MOD, ehdlr->fd, &epevent);
498 : }
499 :
500 : int
501 : spdk_fd_group_create(struct spdk_fd_group **_egrp)
502 : {
503 : struct spdk_fd_group *fgrp;
504 :
505 : if (_egrp == NULL) {
506 : return -EINVAL;
507 : }
508 :
509 : fgrp = calloc(1, sizeof(*fgrp));
510 : if (fgrp == NULL) {
511 : return -ENOMEM;
512 : }
513 :
514 : /* init the event source head */
515 : TAILQ_INIT(&fgrp->event_handlers);
516 : TAILQ_INIT(&fgrp->children);
517 :
518 : fgrp->num_fds = 0;
519 : fgrp->epfd = epoll_create1(EPOLL_CLOEXEC);
520 : if (fgrp->epfd < 0) {
521 : free(fgrp);
522 : return -errno;
523 : }
524 :
525 : *_egrp = fgrp;
526 :
527 : return 0;
528 : }
529 :
530 : void
531 : spdk_fd_group_destroy(struct spdk_fd_group *fgrp)
532 : {
533 : if (fgrp == NULL || fgrp->num_fds > 0) {
534 : if (!fgrp) {
535 : SPDK_ERRLOG("fd_group doesn't exist.\n");
536 : } else {
537 : SPDK_ERRLOG("Cannot delete fd group(%p) as (%u) fds are still registered to it.\n",
538 : fgrp, fgrp->num_fds);
539 : }
540 : assert(0);
541 : return;
542 : }
543 :
544 : /* Check if someone tried to delete the fd group before unnesting it */
545 : if (!TAILQ_EMPTY(&fgrp->event_handlers)) {
546 : SPDK_ERRLOG("Interrupt sources list not empty.\n");
547 : assert(0);
548 : return;
549 : }
550 :
551 : assert(fgrp->parent == NULL);
552 : assert(TAILQ_EMPTY(&fgrp->children));
553 : close(fgrp->epfd);
554 : free(fgrp);
555 :
556 : return;
557 : }
558 :
559 : int
560 : spdk_fd_group_wait(struct spdk_fd_group *fgrp, int timeout)
561 : {
562 : uint32_t totalfds = fgrp->num_fds;
563 : struct epoll_event events[totalfds];
564 : struct event_handler *ehdlr;
565 : uint64_t count;
566 : int n;
567 : int nfds;
568 : int bytes_read;
569 : int read_errno;
570 :
571 : if (fgrp->parent != NULL) {
572 : if (timeout < 0) {
573 : SPDK_ERRLOG("Calling spdk_fd_group_wait on a group nested in another group without a timeout will block indefinitely.\n");
574 : assert(false);
575 : return -EINVAL;
576 : } else {
577 : SPDK_WARNLOG("Calling spdk_fd_group_wait on a group nested in another group will never find any events.\n");
578 : return 0;
579 : }
580 : }
581 :
582 : nfds = epoll_wait(fgrp->epfd, events, totalfds, timeout);
583 : if (nfds < 0) {
584 : if (errno != EINTR) {
585 : SPDK_ERRLOG("fd group(%p) epoll_wait failed: %s\n",
586 : fgrp, strerror(errno));
587 : }
588 :
589 : return -errno;
590 : } else if (nfds == 0) {
591 : return 0;
592 : }
593 :
594 : for (n = 0; n < nfds; n++) {
595 : /* find the event_handler */
596 : ehdlr = events[n].data.ptr;
597 :
598 : if (ehdlr == NULL) {
599 : continue;
600 : }
601 :
602 : /* Tag ehdlr as running state in case that it is removed
603 : * during this wait loop but before or when it get executed.
604 : */
605 : assert(ehdlr->state == EVENT_HANDLER_STATE_WAITING);
606 : ehdlr->state = EVENT_HANDLER_STATE_RUNNING;
607 : }
608 :
609 : for (n = 0; n < nfds; n++) {
610 : /* find the event_handler */
611 : ehdlr = events[n].data.ptr;
612 :
613 : if (ehdlr == NULL || ehdlr->fn == NULL) {
614 : continue;
615 : }
616 :
617 : /* It is possible that the ehdlr was removed
618 : * during this wait loop but before it get executed.
619 : */
620 : if (ehdlr->state == EVENT_HANDLER_STATE_REMOVED) {
621 : free(ehdlr);
622 : continue;
623 : }
624 :
625 : g_event = &events[n];
626 :
627 : /* read fd to reset the internal eventfd object counter value to 0 */
628 : if (ehdlr->fd_type == SPDK_FD_TYPE_EVENTFD) {
629 : bytes_read = read(ehdlr->fd, &count, sizeof(count));
630 : if (bytes_read < 0) {
631 : g_event = NULL;
632 : if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) {
633 : continue;
634 : }
635 : read_errno = errno;
636 : /* TODO: Device is buggy. Handle this properly */
637 : SPDK_ERRLOG("Failed to read fd (%d) %s\n",
638 : ehdlr->fd, strerror(errno));
639 : return -read_errno;
640 : } else if (bytes_read == 0) {
641 : SPDK_ERRLOG("Read nothing from fd (%d)\n", ehdlr->fd);
642 : g_event = NULL;
643 : return -EINVAL;
644 : }
645 : }
646 :
647 : /* call the interrupt response function */
648 : ehdlr->fn(ehdlr->fn_arg);
649 : g_event = NULL;
650 :
651 : /* It is possible that the ehdlr was removed
652 : * during this wait loop when it get executed.
653 : */
654 : if (ehdlr->state == EVENT_HANDLER_STATE_REMOVED) {
655 : free(ehdlr);
656 : } else {
657 : ehdlr->state = EVENT_HANDLER_STATE_WAITING;
658 : }
659 : }
660 :
661 : return nfds;
662 : }
663 :
664 : #else /* !__linux__ */
665 :
666 : int
667 0 : spdk_fd_group_get_epoll_event(struct epoll_event *event)
668 : {
669 0 : return -ENOTSUP;
670 : }
671 :
672 : int
673 0 : spdk_fd_group_add(struct spdk_fd_group *fgrp, int efd, spdk_fd_fn fn,
674 : void *arg, const char *name)
675 : {
676 0 : return -ENOTSUP;
677 : }
678 :
679 : int
680 0 : spdk_fd_group_add_for_events(struct spdk_fd_group *fgrp, int efd, uint32_t events, spdk_fd_fn fn,
681 : void *arg, const char *name)
682 : {
683 0 : return -ENOTSUP;
684 : }
685 :
686 : int
687 0 : spdk_fd_group_add_ext(struct spdk_fd_group *fgrp, int efd, spdk_fd_fn fn, void *arg,
688 : const char *name, struct spdk_event_handler_opts *opts)
689 : {
690 0 : return -ENOTSUP;
691 : }
692 :
693 : void
694 0 : spdk_fd_group_get_default_event_handler_opts(struct spdk_event_handler_opts *opts,
695 : size_t opts_size)
696 : {
697 0 : assert(false);
698 : }
699 :
700 : void
701 0 : spdk_fd_group_remove(struct spdk_fd_group *fgrp, int efd)
702 : {
703 0 : }
704 :
705 : int
706 0 : spdk_fd_group_event_modify(struct spdk_fd_group *fgrp,
707 : int efd, int event_types)
708 : {
709 0 : return -ENOTSUP;
710 : }
711 :
712 : int
713 8 : spdk_fd_group_create(struct spdk_fd_group **fgrp)
714 : {
715 8 : return -ENOTSUP;
716 : }
717 :
718 : void
719 0 : spdk_fd_group_destroy(struct spdk_fd_group *fgrp)
720 : {
721 0 : }
722 :
723 : int
724 0 : spdk_fd_group_wait(struct spdk_fd_group *fgrp, int timeout)
725 : {
726 0 : return -ENOTSUP;
727 : }
728 :
729 : int
730 0 : spdk_fd_group_unnest(struct spdk_fd_group *parent, struct spdk_fd_group *child)
731 : {
732 0 : return -ENOTSUP;
733 : }
734 :
735 : int
736 0 : spdk_fd_group_nest(struct spdk_fd_group *parent, struct spdk_fd_group *child)
737 : {
738 0 : return -ENOTSUP;
739 : }
740 :
741 : #endif /* __linux__ */
|