xref: /spdk/lib/util/fd_group.c (revision 2f5c602574a98ede645991abe279a96e19c50196)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/queue.h"
35 #include "spdk/log.h"
36 #include "spdk/fd_group.h"
37 
38 #ifdef __linux__
39 #include <sys/epoll.h>
40 #endif
41 
42 enum event_handler_state {
43 	/* The event_handler is added into an fd_group waiting for event,
44 	 * but not currently in the execution of a wait loop.
45 	 */
46 	EVENT_HANDLER_STATE_WAITING,
47 
48 	/* The event_handler is currently in the execution of a wait loop. */
49 	EVENT_HANDLER_STATE_RUNNING,
50 
51 	/* The event_handler was removed during the execution of a wait loop. */
52 	EVENT_HANDLER_STATE_REMOVED,
53 };
54 
55 /* file descriptor of the interrupt event */
56 
57 /* Taking "ehdlr" as short name for file descriptor handler of the interrupt event. */
58 struct event_handler {
59 	TAILQ_ENTRY(event_handler)	next;
60 	enum event_handler_state	state;
61 
62 	spdk_fd_fn			fn;
63 	void				*fn_arg;
64 	/* file descriptor of the interrupt event */
65 	int				fd;
66 };
67 
68 struct spdk_fd_group {
69 	int epfd;
70 	int num_fds; /* Number of fds registered in this group. */
71 
72 	/* interrupt sources list */
73 	TAILQ_HEAD(, event_handler) event_handlers;
74 };
75 
76 int
77 spdk_fd_group_get_fd(struct spdk_fd_group *fgrp)
78 {
79 	return fgrp->epfd;
80 }
81 
82 #ifdef __linux__
83 
84 int
85 spdk_fd_group_add(struct spdk_fd_group *fgrp,
86 		  int efd, spdk_fd_fn fn, void *arg)
87 {
88 	struct event_handler *ehdlr = NULL;
89 	struct epoll_event epevent = {0};
90 	int rc;
91 
92 	/* parameter checking */
93 	if (fgrp == NULL || efd < 0 || fn == NULL) {
94 		return -EINVAL;
95 	}
96 
97 	/* check if there is already one function registered for this fd */
98 	TAILQ_FOREACH(ehdlr, &fgrp->event_handlers, next) {
99 		if (ehdlr->fd == efd) {
100 			return -EEXIST;
101 		}
102 	}
103 
104 	/* create a new event src */
105 	ehdlr = calloc(1, sizeof(*ehdlr));
106 	if (ehdlr == NULL) {
107 		return -errno;
108 	}
109 
110 	ehdlr->fd = efd;
111 	ehdlr->fn = fn;
112 	ehdlr->fn_arg = arg;
113 	ehdlr->state = EVENT_HANDLER_STATE_WAITING;
114 
115 	epevent.events = EPOLLIN;
116 	epevent.data.ptr = ehdlr;
117 	rc = epoll_ctl(fgrp->epfd, EPOLL_CTL_ADD, efd, &epevent);
118 	if (rc < 0) {
119 		free(ehdlr);
120 		return -errno;
121 	}
122 
123 	TAILQ_INSERT_TAIL(&fgrp->event_handlers, ehdlr, next);
124 	fgrp->num_fds++;
125 
126 	return 0;
127 }
128 
129 void
130 spdk_fd_group_remove(struct spdk_fd_group *fgrp, int efd)
131 {
132 	struct event_handler *ehdlr;
133 	int rc;
134 
135 	if (fgrp == NULL || efd < 0) {
136 		SPDK_ERRLOG("Invalid to remvoe efd(%d) from fd_group(%p).\n", efd, fgrp);
137 		assert(0);
138 		return;
139 	}
140 
141 
142 	TAILQ_FOREACH(ehdlr, &fgrp->event_handlers, next) {
143 		if (ehdlr->fd == efd) {
144 			break;
145 		}
146 	}
147 
148 	if (ehdlr == NULL) {
149 		SPDK_ERRLOG("efd(%d) is not existed in fgrp(%p)\n", efd, fgrp);
150 		return;
151 	}
152 
153 	assert(ehdlr->state != EVENT_HANDLER_STATE_REMOVED);
154 
155 	rc = epoll_ctl(fgrp->epfd, EPOLL_CTL_DEL, ehdlr->fd, NULL);
156 	if (rc < 0) {
157 		SPDK_ERRLOG("Failed to delete the fd(%d) from the epoll group(%p)\n", efd, fgrp);
158 		return;
159 	}
160 
161 	assert(fgrp->num_fds > 0);
162 	fgrp->num_fds--;
163 	TAILQ_REMOVE(&fgrp->event_handlers, ehdlr, next);
164 
165 	/* Delay ehdlr's free in case it is waiting for execution in fgrp wait loop */
166 	if (ehdlr->state == EVENT_HANDLER_STATE_RUNNING) {
167 		ehdlr->state = EVENT_HANDLER_STATE_REMOVED;
168 	} else {
169 		free(ehdlr);
170 	}
171 }
172 
173 int
174 spdk_fd_group_event_modify(struct spdk_fd_group *fgrp,
175 			   int efd, int event_types)
176 {
177 	struct epoll_event epevent;
178 	struct event_handler *ehdlr;
179 
180 	if (fgrp == NULL || efd < 0) {
181 		return -EINVAL;
182 	}
183 
184 	TAILQ_FOREACH(ehdlr, &fgrp->event_handlers, next) {
185 		if (ehdlr->fd == efd) {
186 			break;
187 		}
188 	}
189 
190 	if (ehdlr == NULL) {
191 		return -EINVAL;
192 	}
193 
194 	assert(ehdlr->state != EVENT_HANDLER_STATE_REMOVED);
195 
196 	epevent.events = event_types;
197 	epevent.data.ptr = ehdlr;
198 
199 	return epoll_ctl(fgrp->epfd, EPOLL_CTL_MOD, ehdlr->fd, &epevent);
200 }
201 
202 int
203 spdk_fd_group_create(struct spdk_fd_group **_egrp)
204 {
205 	struct spdk_fd_group *fgrp;
206 
207 	if (_egrp == NULL) {
208 		return -EINVAL;
209 	}
210 
211 	fgrp = calloc(1, sizeof(*fgrp));
212 	if (fgrp == NULL) {
213 		return -ENOMEM;
214 	}
215 
216 	/* init the event source head */
217 	TAILQ_INIT(&fgrp->event_handlers);
218 
219 	fgrp->num_fds = 0;
220 	fgrp->epfd = epoll_create1(EPOLL_CLOEXEC);
221 	if (fgrp->epfd < 0) {
222 		free(fgrp);
223 		return -errno;
224 	}
225 
226 	*_egrp = fgrp;
227 
228 	return 0;
229 }
230 
231 void
232 spdk_fd_group_destroy(struct spdk_fd_group *fgrp)
233 {
234 	if (fgrp == NULL || fgrp->num_fds > 0) {
235 		SPDK_ERRLOG("Invalid fd_group(%p) to destroy.\n", fgrp);
236 		assert(0);
237 		return;
238 	}
239 
240 	close(fgrp->epfd);
241 	free(fgrp);
242 
243 	return;
244 }
245 
246 int
247 spdk_fd_group_wait(struct spdk_fd_group *fgrp, int timeout)
248 {
249 	int totalfds = fgrp->num_fds;
250 	struct epoll_event events[totalfds];
251 	struct event_handler *ehdlr;
252 	int n;
253 	int nfds;
254 
255 	nfds = epoll_wait(fgrp->epfd, events, totalfds, timeout);
256 	if (nfds < 0) {
257 		if (errno != EINTR) {
258 			SPDK_ERRLOG("fgrp epoll_wait returns with fail. errno is %d\n", errno);
259 		}
260 
261 		return -errno;
262 	} else if (nfds == 0) {
263 		return 0;
264 	}
265 
266 	for (n = 0; n < nfds; n++) {
267 		/* find the event_handler */
268 		ehdlr = events[n].data.ptr;
269 
270 		if (ehdlr == NULL) {
271 			continue;
272 		}
273 
274 		/* Tag ehdlr as running state in case that it is removed
275 		 * during this wait loop but before or when it get executed.
276 		 */
277 		assert(ehdlr->state == EVENT_HANDLER_STATE_WAITING);
278 		ehdlr->state = EVENT_HANDLER_STATE_RUNNING;
279 	}
280 
281 	for (n = 0; n < nfds; n++) {
282 		/* find the event_handler */
283 		ehdlr = events[n].data.ptr;
284 
285 		if (ehdlr == NULL || ehdlr->fn == NULL) {
286 			continue;
287 		}
288 
289 		/* It is possible that the ehdlr was removed
290 		 * during this wait loop but before it get executed.
291 		 */
292 		if (ehdlr->state == EVENT_HANDLER_STATE_REMOVED) {
293 			free(ehdlr);
294 			continue;
295 		}
296 
297 		/* call the interrupt response function */
298 		ehdlr->fn(ehdlr->fn_arg);
299 
300 		/* It is possible that the ehdlr was removed
301 		 * during this wait loop when it get executed.
302 		 */
303 		if (ehdlr->state == EVENT_HANDLER_STATE_REMOVED) {
304 			free(ehdlr);
305 		} else {
306 			ehdlr->state = EVENT_HANDLER_STATE_WAITING;
307 		}
308 	}
309 
310 	return nfds;
311 }
312 
313 #else
314 
315 int
316 spdk_fd_group_add(struct spdk_fd_group *fgrp,
317 		  int efd, spdk_fd_fn fn, void *arg)
318 {
319 	return -ENOTSUP;
320 }
321 
322 void
323 spdk_fd_group_remove(struct spdk_fd_group *fgrp, int efd)
324 {
325 }
326 
327 int
328 spdk_fd_group_event_modify(struct spdk_fd_group *fgrp,
329 			   int efd, int event_types)
330 {
331 	return -ENOTSUP;
332 }
333 
334 int
335 spdk_fd_group_create(struct spdk_fd_group **fgrp)
336 {
337 	return -ENOTSUP;
338 }
339 
340 void
341 spdk_fd_group_destroy(struct spdk_fd_group *fgrp)
342 {
343 }
344 
345 int
346 spdk_fd_group_wait(struct spdk_fd_group *fgrp, int timeout)
347 {
348 	return -ENOTSUP;
349 }
350 
351 #endif
352