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