Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2010-2014 Intel Corporation
3 : : */
4 : :
5 : : #include <errno.h>
6 : : #include <stdio.h>
7 : : #include <string.h>
8 : : #include <sys/epoll.h>
9 : : #include <unistd.h>
10 : :
11 : : #include <rte_common.h>
12 : : #include <rte_log.h>
13 : : #include <rte_malloc.h>
14 : : #include <rte_string_fns.h>
15 : : #include <rte_thread.h>
16 : :
17 : : #include "fd_man.h"
18 : :
19 [ - + ]: 251 : RTE_LOG_REGISTER_SUFFIX(vhost_fdset_logtype, fdset, INFO);
20 : : #define RTE_LOGTYPE_VHOST_FDMAN vhost_fdset_logtype
21 : : #define VHOST_FDMAN_LOG(level, ...) \
22 : : RTE_LOG_LINE(level, VHOST_FDMAN, "" __VA_ARGS__)
23 : :
24 : : struct fdentry {
25 : : int fd; /* -1 indicates this entry is empty */
26 : : fd_cb rcb; /* callback when this fd is readable. */
27 : : fd_cb wcb; /* callback when this fd is writeable.*/
28 : : void *dat; /* fd context */
29 : : int busy; /* whether this entry is being used in cb. */
30 : : LIST_ENTRY(fdentry) next;
31 : : };
32 : :
33 : : struct fdset {
34 : : char name[RTE_THREAD_NAME_SIZE];
35 : : int epfd;
36 : : struct fdentry fd[MAX_FDS];
37 : : LIST_HEAD(, fdentry) fdlist;
38 : : int next_free_idx;
39 : : rte_thread_t tid;
40 : : pthread_mutex_t fd_mutex;
41 : : bool destroy;
42 : : };
43 : :
44 : : #define MAX_FDSETS 8
45 : :
46 : : static struct fdset *fdsets[MAX_FDSETS];
47 : : static pthread_mutex_t fdsets_mutex = PTHREAD_MUTEX_INITIALIZER;
48 : :
49 : : static uint32_t fdset_event_dispatch(void *arg);
50 : :
51 : : static struct fdset *
52 : 0 : fdset_lookup(const char *name)
53 : : {
54 : : int i;
55 : :
56 [ # # ]: 0 : for (i = 0; i < MAX_FDSETS; i++) {
57 : 0 : struct fdset *fdset = fdsets[i];
58 [ # # ]: 0 : if (fdset == NULL)
59 : 0 : continue;
60 : :
61 [ # # ]: 0 : if (!strncmp(fdset->name, name, RTE_THREAD_NAME_SIZE))
62 : 0 : return fdset;
63 : : }
64 : :
65 : : return NULL;
66 : : }
67 : :
68 : : static int
69 : : fdset_insert(struct fdset *fdset)
70 : : {
71 : : int i;
72 : :
73 [ # # ]: 0 : for (i = 0; i < MAX_FDSETS; i++) {
74 [ # # ]: 0 : if (fdsets[i] == NULL) {
75 : 0 : fdsets[i] = fdset;
76 : : return 0;
77 : : }
78 : : }
79 : :
80 : : return -1;
81 : : }
82 : :
83 : : struct fdset *
84 : 0 : fdset_init(const char *name)
85 : : {
86 : : struct fdset *fdset;
87 : : uint32_t val;
88 : : int i;
89 : :
90 : 0 : pthread_mutex_lock(&fdsets_mutex);
91 : 0 : fdset = fdset_lookup(name);
92 [ # # ]: 0 : if (fdset) {
93 : 0 : pthread_mutex_unlock(&fdsets_mutex);
94 : 0 : return fdset;
95 : : }
96 : :
97 : 0 : fdset = rte_zmalloc(NULL, sizeof(*fdset), 0);
98 [ # # ]: 0 : if (!fdset) {
99 : 0 : VHOST_FDMAN_LOG(ERR, "failed to alloc fdset %s", name);
100 : 0 : goto err_unlock;
101 : : }
102 : :
103 : 0 : rte_strscpy(fdset->name, name, RTE_THREAD_NAME_SIZE);
104 : :
105 : 0 : pthread_mutex_init(&fdset->fd_mutex, NULL);
106 : :
107 [ # # ]: 0 : for (i = 0; i < (int)RTE_DIM(fdset->fd); i++) {
108 : 0 : fdset->fd[i].fd = -1;
109 : 0 : fdset->fd[i].dat = NULL;
110 : : }
111 : 0 : LIST_INIT(&fdset->fdlist);
112 : :
113 : : /*
114 : : * Any non-zero value would work (see man epoll_create),
115 : : * but pass MAX_FDS for consistency.
116 : : */
117 : 0 : fdset->epfd = epoll_create(MAX_FDS);
118 [ # # ]: 0 : if (fdset->epfd < 0) {
119 : 0 : VHOST_FDMAN_LOG(ERR, "failed to create epoll for %s fdset", name);
120 : 0 : goto err_free;
121 : : }
122 : :
123 [ # # ]: 0 : if (rte_thread_create_internal_control(&fdset->tid, fdset->name,
124 : : fdset_event_dispatch, fdset)) {
125 : 0 : VHOST_FDMAN_LOG(ERR, "Failed to create %s event dispatch thread",
126 : : fdset->name);
127 : 0 : goto err_epoll;
128 : : }
129 : :
130 : : if (fdset_insert(fdset)) {
131 : 0 : VHOST_FDMAN_LOG(ERR, "Failed to insert fdset %s", name);
132 : 0 : goto err_thread;
133 : : }
134 : :
135 : 0 : pthread_mutex_unlock(&fdsets_mutex);
136 : :
137 : 0 : return fdset;
138 : :
139 : : err_thread:
140 : 0 : fdset->destroy = true;
141 : 0 : rte_thread_join(fdset->tid, &val);
142 : 0 : err_epoll:
143 : 0 : close(fdset->epfd);
144 : 0 : err_free:
145 : 0 : rte_free(fdset);
146 : 0 : err_unlock:
147 : 0 : pthread_mutex_unlock(&fdsets_mutex);
148 : :
149 : 0 : return NULL;
150 : : }
151 : :
152 : : static int
153 : 0 : fdset_insert_entry(struct fdset *pfdset, int fd, fd_cb rcb, fd_cb wcb, void *dat)
154 : : {
155 : : struct fdentry *pfdentry;
156 : :
157 [ # # ]: 0 : if (pfdset->next_free_idx >= (int)RTE_DIM(pfdset->fd))
158 : : return -1;
159 : :
160 : 0 : pfdentry = &pfdset->fd[pfdset->next_free_idx];
161 : 0 : pfdentry->fd = fd;
162 : 0 : pfdentry->rcb = rcb;
163 : 0 : pfdentry->wcb = wcb;
164 : 0 : pfdentry->dat = dat;
165 : :
166 [ # # ]: 0 : LIST_INSERT_HEAD(&pfdset->fdlist, pfdentry, next);
167 : :
168 : : /* Find next free slot */
169 : 0 : pfdset->next_free_idx++;
170 [ # # ]: 0 : for (; pfdset->next_free_idx < (int)RTE_DIM(pfdset->fd); pfdset->next_free_idx++) {
171 [ # # ]: 0 : if (pfdset->fd[pfdset->next_free_idx].fd != -1)
172 : : continue;
173 : : break;
174 : : }
175 : :
176 : : return 0;
177 : : }
178 : :
179 : : static void
180 : : fdset_remove_entry(struct fdset *pfdset, struct fdentry *pfdentry)
181 : : {
182 : : int entry_idx;
183 : :
184 : 0 : pfdentry->fd = -1;
185 : 0 : pfdentry->rcb = pfdentry->wcb = NULL;
186 : 0 : pfdentry->dat = NULL;
187 : :
188 : 0 : entry_idx = pfdentry - pfdset->fd;
189 [ # # # # ]: 0 : if (entry_idx < pfdset->next_free_idx)
190 : 0 : pfdset->next_free_idx = entry_idx;
191 : :
192 [ # # # # ]: 0 : LIST_REMOVE(pfdentry, next);
193 : 0 : }
194 : :
195 : : static struct fdentry *
196 : : fdset_find_entry_locked(struct fdset *pfdset, int fd)
197 : : {
198 : : struct fdentry *pfdentry;
199 : :
200 [ # # # # : 0 : LIST_FOREACH(pfdentry, &pfdset->fdlist, next) {
# # # # ]
201 [ # # # # : 0 : if (pfdentry->fd != fd)
# # # # ]
202 : : continue;
203 : : return pfdentry;
204 : : }
205 : :
206 : : return NULL;
207 : : }
208 : :
209 : : /**
210 : : * Register the fd in the fdset with read/write handler and context.
211 : : */
212 : : int
213 : 0 : fdset_add(struct fdset *pfdset, int fd, fd_cb rcb, fd_cb wcb, void *dat)
214 : : {
215 : : struct epoll_event ev;
216 : : struct fdentry *pfdentry;
217 : : int ret = 0;
218 : :
219 [ # # ]: 0 : if (pfdset == NULL || fd == -1) {
220 : : ret = -1;
221 : 0 : goto out;
222 : : }
223 : :
224 : 0 : pthread_mutex_lock(&pfdset->fd_mutex);
225 : 0 : ret = fdset_insert_entry(pfdset, fd, rcb, wcb, dat);
226 [ # # ]: 0 : if (ret < 0) {
227 : 0 : VHOST_FDMAN_LOG(ERR, "failed to insert fdset entry");
228 : 0 : pthread_mutex_unlock(&pfdset->fd_mutex);
229 : 0 : goto out;
230 : : }
231 : 0 : pthread_mutex_unlock(&pfdset->fd_mutex);
232 : :
233 : : ev.events = EPOLLERR;
234 [ # # ]: 0 : ev.events |= rcb ? EPOLLIN : 0;
235 [ # # ]: 0 : ev.events |= wcb ? EPOLLOUT : 0;
236 : 0 : ev.data.fd = fd;
237 : :
238 : 0 : ret = epoll_ctl(pfdset->epfd, EPOLL_CTL_ADD, fd, &ev);
239 [ # # ]: 0 : if (ret < 0) {
240 : 0 : VHOST_FDMAN_LOG(ERR, "could not add %d fd to %d epfd: %s",
241 : : fd, pfdset->epfd, strerror(errno));
242 : 0 : goto out_remove;
243 : : }
244 : :
245 : : return 0;
246 : : out_remove:
247 : 0 : pthread_mutex_lock(&pfdset->fd_mutex);
248 : : pfdentry = fdset_find_entry_locked(pfdset, fd);
249 [ # # ]: 0 : if (pfdentry)
250 : : fdset_remove_entry(pfdset, pfdentry);
251 : 0 : pthread_mutex_unlock(&pfdset->fd_mutex);
252 : : out:
253 : : return ret;
254 : : }
255 : :
256 : : static void
257 : 0 : fdset_del_locked(struct fdset *pfdset, struct fdentry *pfdentry)
258 : : {
259 [ # # ]: 0 : if (epoll_ctl(pfdset->epfd, EPOLL_CTL_DEL, pfdentry->fd, NULL) == -1) {
260 [ # # ]: 0 : if (errno == EBADF) /* File might have already been closed. */
261 : 0 : VHOST_FDMAN_LOG(DEBUG, "could not remove %d fd from %d epfd: %s",
262 : : pfdentry->fd, pfdset->epfd, strerror(errno));
263 : : else
264 : 0 : VHOST_FDMAN_LOG(ERR, "could not remove %d fd from %d epfd: %s",
265 : : pfdentry->fd, pfdset->epfd, strerror(errno));
266 : : }
267 : :
268 : : fdset_remove_entry(pfdset, pfdentry);
269 : 0 : }
270 : :
271 : : void
272 : 0 : fdset_del(struct fdset *pfdset, int fd)
273 : : {
274 : : struct fdentry *pfdentry;
275 : :
276 [ # # ]: 0 : if (pfdset == NULL || fd == -1)
277 : : return;
278 : :
279 : : do {
280 : 0 : pthread_mutex_lock(&pfdset->fd_mutex);
281 : : pfdentry = fdset_find_entry_locked(pfdset, fd);
282 [ # # # # ]: 0 : if (pfdentry != NULL && pfdentry->busy == 0) {
283 : 0 : fdset_del_locked(pfdset, pfdentry);
284 : : pfdentry = NULL;
285 : : }
286 : 0 : pthread_mutex_unlock(&pfdset->fd_mutex);
287 [ # # ]: 0 : } while (pfdentry != NULL);
288 : : }
289 : :
290 : : /**
291 : : * Unregister the fd from the fdset.
292 : : *
293 : : * If parameters are invalid, return directly -2.
294 : : * And check whether fd is busy, if yes, return -1.
295 : : * Otherwise, try to delete the fd from fdset and
296 : : * return true.
297 : : */
298 : : int
299 : 0 : fdset_try_del(struct fdset *pfdset, int fd)
300 : : {
301 : : struct fdentry *pfdentry;
302 : :
303 [ # # ]: 0 : if (pfdset == NULL || fd == -1)
304 : : return -2;
305 : :
306 : 0 : pthread_mutex_lock(&pfdset->fd_mutex);
307 : : pfdentry = fdset_find_entry_locked(pfdset, fd);
308 [ # # # # ]: 0 : if (pfdentry != NULL && pfdentry->busy != 0) {
309 : 0 : pthread_mutex_unlock(&pfdset->fd_mutex);
310 : 0 : return -1;
311 : : }
312 : :
313 [ # # ]: 0 : if (pfdentry != NULL)
314 : 0 : fdset_del_locked(pfdset, pfdentry);
315 : :
316 : 0 : pthread_mutex_unlock(&pfdset->fd_mutex);
317 : 0 : return 0;
318 : : }
319 : :
320 : : /**
321 : : * This functions runs in infinite blocking loop until there is no fd in
322 : : * pfdset. It calls corresponding r/w handler if there is event on the fd.
323 : : *
324 : : * Before the callback is called, we set the flag to busy status; If other
325 : : * thread(now rte_vhost_driver_unregister) calls fdset_del concurrently, it
326 : : * will wait until the flag is reset to zero(which indicates the callback is
327 : : * finished), then it could free the context after fdset_del.
328 : : */
329 : : static uint32_t
330 : 0 : fdset_event_dispatch(void *arg)
331 : : {
332 : : int i;
333 : : fd_cb rcb, wcb;
334 : : void *dat;
335 : : int fd, numfds;
336 : : int remove1, remove2;
337 : : struct fdset *pfdset = arg;
338 : :
339 [ # # ]: 0 : if (pfdset == NULL)
340 : : return 0;
341 : :
342 : : while (1) {
343 : : struct epoll_event events[MAX_FDS];
344 : : struct fdentry *pfdentry;
345 : :
346 : 0 : numfds = epoll_wait(pfdset->epfd, events, RTE_DIM(events), 1000);
347 [ # # ]: 0 : if (numfds < 0)
348 : 0 : continue;
349 : :
350 [ # # ]: 0 : for (i = 0; i < numfds; i++) {
351 : 0 : pthread_mutex_lock(&pfdset->fd_mutex);
352 : :
353 : 0 : fd = events[i].data.fd;
354 : : pfdentry = fdset_find_entry_locked(pfdset, fd);
355 [ # # ]: 0 : if (pfdentry == NULL) {
356 : 0 : pthread_mutex_unlock(&pfdset->fd_mutex);
357 : 0 : continue;
358 : : }
359 : :
360 : 0 : remove1 = remove2 = 0;
361 : :
362 : 0 : rcb = pfdentry->rcb;
363 : 0 : wcb = pfdentry->wcb;
364 : 0 : dat = pfdentry->dat;
365 : 0 : pfdentry->busy = 1;
366 : :
367 : 0 : pthread_mutex_unlock(&pfdset->fd_mutex);
368 : :
369 [ # # # # ]: 0 : if (rcb && events[i].events & (EPOLLIN | EPOLLERR | EPOLLHUP))
370 : 0 : rcb(fd, dat, &remove1);
371 [ # # # # ]: 0 : if (wcb && events[i].events & (EPOLLOUT | EPOLLERR | EPOLLHUP))
372 : 0 : wcb(fd, dat, &remove2);
373 : 0 : pfdentry->busy = 0;
374 : : /*
375 : : * fdset_del needs to check busy flag.
376 : : * We don't allow fdset_del to be called in callback
377 : : * directly.
378 : : */
379 : : /*
380 : : * A concurrent fdset_del may have been waiting for the
381 : : * fdentry not to be busy, so we can't call
382 : : * fdset_del_locked().
383 : : */
384 [ # # # # ]: 0 : if (remove1 || remove2)
385 : 0 : fdset_del(pfdset, fd);
386 : : }
387 : :
388 [ # # ]: 0 : if (pfdset->destroy)
389 : : break;
390 : : }
391 : :
392 : 0 : return 0;
393 : : }
|