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