xref: /spdk/lib/util/fd_group.c (revision c12cb8fe35297bfebf155ee658660da0160fbc12)
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