xref: /spdk/lib/util/fd_group.c (revision 9544fe07aad355262fcaa65dc27f9965a8ea4617)
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 #define SPDK_MAX_EVENT_NAME_LEN 256
43 
44 enum event_handler_state {
45 	/* The event_handler is added into an fd_group waiting for event,
46 	 * but not currently in the execution of a wait loop.
47 	 */
48 	EVENT_HANDLER_STATE_WAITING,
49 
50 	/* The event_handler is currently in the execution of a wait loop. */
51 	EVENT_HANDLER_STATE_RUNNING,
52 
53 	/* The event_handler was removed during the execution of a wait loop. */
54 	EVENT_HANDLER_STATE_REMOVED,
55 };
56 
57 /* file descriptor of the interrupt event */
58 
59 /* Taking "ehdlr" as short name for file descriptor handler of the interrupt event. */
60 struct event_handler {
61 	TAILQ_ENTRY(event_handler)	next;
62 	enum event_handler_state	state;
63 
64 	spdk_fd_fn			fn;
65 	void				*fn_arg;
66 	/* file descriptor of the interrupt event */
67 	int				fd;
68 	char				name[SPDK_MAX_EVENT_NAME_LEN + 1];
69 };
70 
71 struct spdk_fd_group {
72 	int epfd;
73 	int num_fds; /* Number of fds registered in this group. */
74 
75 	/* interrupt sources list */
76 	TAILQ_HEAD(, event_handler) event_handlers;
77 };
78 
79 int
80 spdk_fd_group_get_fd(struct spdk_fd_group *fgrp)
81 {
82 	return fgrp->epfd;
83 }
84 
85 #ifdef __linux__
86 
87 int
88 spdk_fd_group_add(struct spdk_fd_group *fgrp, int efd, spdk_fd_fn fn,
89 		  void *arg, const char *name)
90 {
91 	struct event_handler *ehdlr = NULL;
92 	struct epoll_event epevent = {0};
93 	int rc;
94 
95 	/* parameter checking */
96 	if (fgrp == NULL || efd < 0 || fn == NULL) {
97 		return -EINVAL;
98 	}
99 
100 	/* check if there is already one function registered for this fd */
101 	TAILQ_FOREACH(ehdlr, &fgrp->event_handlers, next) {
102 		if (ehdlr->fd == efd) {
103 			return -EEXIST;
104 		}
105 	}
106 
107 	/* create a new event src */
108 	ehdlr = calloc(1, sizeof(*ehdlr));
109 	if (ehdlr == NULL) {
110 		return -errno;
111 	}
112 
113 	ehdlr->fd = efd;
114 	ehdlr->fn = fn;
115 	ehdlr->fn_arg = arg;
116 	ehdlr->state = EVENT_HANDLER_STATE_WAITING;
117 	snprintf(ehdlr->name, sizeof(ehdlr->name), "%s", name);
118 
119 	epevent.events = EPOLLIN;
120 	epevent.data.ptr = ehdlr;
121 	rc = epoll_ctl(fgrp->epfd, EPOLL_CTL_ADD, efd, &epevent);
122 	if (rc < 0) {
123 		free(ehdlr);
124 		return -errno;
125 	}
126 
127 	TAILQ_INSERT_TAIL(&fgrp->event_handlers, ehdlr, next);
128 	fgrp->num_fds++;
129 
130 	return 0;
131 }
132 
133 void
134 spdk_fd_group_remove(struct spdk_fd_group *fgrp, int efd)
135 {
136 	struct event_handler *ehdlr;
137 	int rc;
138 
139 	if (fgrp == NULL || efd < 0) {
140 		SPDK_ERRLOG("Invalid to remvoe efd(%d) from fd_group(%p).\n", efd, fgrp);
141 		assert(0);
142 		return;
143 	}
144 
145 
146 	TAILQ_FOREACH(ehdlr, &fgrp->event_handlers, next) {
147 		if (ehdlr->fd == efd) {
148 			break;
149 		}
150 	}
151 
152 	if (ehdlr == NULL) {
153 		SPDK_ERRLOG("efd(%d) is not existed in fgrp(%p)\n", efd, fgrp);
154 		return;
155 	}
156 
157 	assert(ehdlr->state != EVENT_HANDLER_STATE_REMOVED);
158 
159 	rc = epoll_ctl(fgrp->epfd, EPOLL_CTL_DEL, ehdlr->fd, NULL);
160 	if (rc < 0) {
161 		SPDK_ERRLOG("Failed to delete the fd(%d) from the epoll group(%p)\n", efd, fgrp);
162 		return;
163 	}
164 
165 	assert(fgrp->num_fds > 0);
166 	fgrp->num_fds--;
167 	TAILQ_REMOVE(&fgrp->event_handlers, ehdlr, next);
168 
169 	/* Delay ehdlr's free in case it is waiting for execution in fgrp wait loop */
170 	if (ehdlr->state == EVENT_HANDLER_STATE_RUNNING) {
171 		ehdlr->state = EVENT_HANDLER_STATE_REMOVED;
172 	} else {
173 		free(ehdlr);
174 	}
175 }
176 
177 int
178 spdk_fd_group_event_modify(struct spdk_fd_group *fgrp,
179 			   int efd, int event_types)
180 {
181 	struct epoll_event epevent;
182 	struct event_handler *ehdlr;
183 
184 	if (fgrp == NULL || efd < 0) {
185 		return -EINVAL;
186 	}
187 
188 	TAILQ_FOREACH(ehdlr, &fgrp->event_handlers, next) {
189 		if (ehdlr->fd == efd) {
190 			break;
191 		}
192 	}
193 
194 	if (ehdlr == NULL) {
195 		return -EINVAL;
196 	}
197 
198 	assert(ehdlr->state != EVENT_HANDLER_STATE_REMOVED);
199 
200 	epevent.events = event_types;
201 	epevent.data.ptr = ehdlr;
202 
203 	return epoll_ctl(fgrp->epfd, EPOLL_CTL_MOD, ehdlr->fd, &epevent);
204 }
205 
206 int
207 spdk_fd_group_create(struct spdk_fd_group **_egrp)
208 {
209 	struct spdk_fd_group *fgrp;
210 
211 	if (_egrp == NULL) {
212 		return -EINVAL;
213 	}
214 
215 	fgrp = calloc(1, sizeof(*fgrp));
216 	if (fgrp == NULL) {
217 		return -ENOMEM;
218 	}
219 
220 	/* init the event source head */
221 	TAILQ_INIT(&fgrp->event_handlers);
222 
223 	fgrp->num_fds = 0;
224 	fgrp->epfd = epoll_create1(EPOLL_CLOEXEC);
225 	if (fgrp->epfd < 0) {
226 		free(fgrp);
227 		return -errno;
228 	}
229 
230 	*_egrp = fgrp;
231 
232 	return 0;
233 }
234 
235 void
236 spdk_fd_group_destroy(struct spdk_fd_group *fgrp)
237 {
238 	if (fgrp == NULL || fgrp->num_fds > 0) {
239 		SPDK_ERRLOG("Invalid fd_group(%p) to destroy.\n", fgrp);
240 		assert(0);
241 		return;
242 	}
243 
244 	close(fgrp->epfd);
245 	free(fgrp);
246 
247 	return;
248 }
249 
250 int
251 spdk_fd_group_wait(struct spdk_fd_group *fgrp, int timeout)
252 {
253 	int totalfds = fgrp->num_fds;
254 	struct epoll_event events[totalfds];
255 	struct event_handler *ehdlr;
256 	int n;
257 	int nfds;
258 
259 	nfds = epoll_wait(fgrp->epfd, events, totalfds, timeout);
260 	if (nfds < 0) {
261 		if (errno != EINTR) {
262 			SPDK_ERRLOG("fgrp epoll_wait returns with fail. errno is %d\n", errno);
263 		}
264 
265 		return -errno;
266 	} else if (nfds == 0) {
267 		return 0;
268 	}
269 
270 	for (n = 0; n < nfds; n++) {
271 		/* find the event_handler */
272 		ehdlr = events[n].data.ptr;
273 
274 		if (ehdlr == NULL) {
275 			continue;
276 		}
277 
278 		/* Tag ehdlr as running state in case that it is removed
279 		 * during this wait loop but before or when it get executed.
280 		 */
281 		assert(ehdlr->state == EVENT_HANDLER_STATE_WAITING);
282 		ehdlr->state = EVENT_HANDLER_STATE_RUNNING;
283 	}
284 
285 	for (n = 0; n < nfds; n++) {
286 		/* find the event_handler */
287 		ehdlr = events[n].data.ptr;
288 
289 		if (ehdlr == NULL || ehdlr->fn == NULL) {
290 			continue;
291 		}
292 
293 		/* It is possible that the ehdlr was removed
294 		 * during this wait loop but before it get executed.
295 		 */
296 		if (ehdlr->state == EVENT_HANDLER_STATE_REMOVED) {
297 			free(ehdlr);
298 			continue;
299 		}
300 
301 		/* call the interrupt response function */
302 		ehdlr->fn(ehdlr->fn_arg);
303 
304 		/* It is possible that the ehdlr was removed
305 		 * during this wait loop when it get executed.
306 		 */
307 		if (ehdlr->state == EVENT_HANDLER_STATE_REMOVED) {
308 			free(ehdlr);
309 		} else {
310 			ehdlr->state = EVENT_HANDLER_STATE_WAITING;
311 		}
312 	}
313 
314 	return nfds;
315 }
316 
317 #else
318 
319 int
320 spdk_fd_group_add(struct spdk_fd_group *fgrp, int efd, spdk_fd_fn fn,
321 		  void *arg, const char *name)
322 {
323 	return -ENOTSUP;
324 }
325 
326 void
327 spdk_fd_group_remove(struct spdk_fd_group *fgrp, int efd)
328 {
329 }
330 
331 int
332 spdk_fd_group_event_modify(struct spdk_fd_group *fgrp,
333 			   int efd, int event_types)
334 {
335 	return -ENOTSUP;
336 }
337 
338 int
339 spdk_fd_group_create(struct spdk_fd_group **fgrp)
340 {
341 	return -ENOTSUP;
342 }
343 
344 void
345 spdk_fd_group_destroy(struct spdk_fd_group *fgrp)
346 {
347 }
348 
349 int
350 spdk_fd_group_wait(struct spdk_fd_group *fgrp, int timeout)
351 {
352 	return -ENOTSUP;
353 }
354 
355 #endif
356