xref: /dpdk/lib/vhost/fd_man.c (revision 1e5bf7b6188d07f91bbc7dcab651942af784191a)
199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson  * Copyright(c) 2010-2014 Intel Corporation
399a2dd95SBruce Richardson  */
499a2dd95SBruce Richardson 
50c18e4fbSMaxime Coquelin #include <errno.h>
699a2dd95SBruce Richardson #include <stdio.h>
70c18e4fbSMaxime Coquelin #include <string.h>
80e38b42bSDavid Marchand #include <sys/epoll.h>
999a2dd95SBruce Richardson #include <unistd.h>
1099a2dd95SBruce Richardson 
1199a2dd95SBruce Richardson #include <rte_common.h>
1299a2dd95SBruce Richardson #include <rte_log.h>
13e68a6feaSMaxime Coquelin #include <rte_malloc.h>
14e68a6feaSMaxime Coquelin #include <rte_string_fns.h>
15e68a6feaSMaxime Coquelin #include <rte_thread.h>
1699a2dd95SBruce Richardson 
1799a2dd95SBruce Richardson #include "fd_man.h"
1899a2dd95SBruce Richardson 
19a526461bSStephen Hemminger RTE_LOG_REGISTER_SUFFIX(vhost_fdset_logtype, fdset, INFO);
20a526461bSStephen Hemminger #define RTE_LOGTYPE_VHOST_FDMAN vhost_fdset_logtype
2197433132SDavid Marchand #define VHOST_FDMAN_LOG(level, ...) \
2297433132SDavid Marchand 	RTE_LOG_LINE(level, VHOST_FDMAN, "" __VA_ARGS__)
2399a2dd95SBruce Richardson 
24e68a6feaSMaxime Coquelin struct fdentry {
25e68a6feaSMaxime Coquelin 	int fd;		/* -1 indicates this entry is empty */
26e68a6feaSMaxime Coquelin 	fd_cb rcb;	/* callback when this fd is readable. */
27e68a6feaSMaxime Coquelin 	fd_cb wcb;	/* callback when this fd is writeable.*/
28e68a6feaSMaxime Coquelin 	void *dat;	/* fd context */
29e68a6feaSMaxime Coquelin 	int busy;	/* whether this entry is being used in cb. */
300e38b42bSDavid Marchand 	LIST_ENTRY(fdentry) next;
31e68a6feaSMaxime Coquelin };
32e68a6feaSMaxime Coquelin 
33e68a6feaSMaxime Coquelin struct fdset {
34e68a6feaSMaxime Coquelin 	char name[RTE_THREAD_NAME_SIZE];
350e38b42bSDavid Marchand 	int epfd;
36e68a6feaSMaxime Coquelin 	struct fdentry fd[MAX_FDS];
370e38b42bSDavid Marchand 	LIST_HEAD(, fdentry) fdlist;
380e38b42bSDavid Marchand 	int next_free_idx;
39e68a6feaSMaxime Coquelin 	rte_thread_t tid;
40e68a6feaSMaxime Coquelin 	pthread_mutex_t fd_mutex;
41e68a6feaSMaxime Coquelin 	bool destroy;
42e68a6feaSMaxime Coquelin };
43e68a6feaSMaxime Coquelin 
44e68a6feaSMaxime Coquelin #define MAX_FDSETS 8
45e68a6feaSMaxime Coquelin 
46e68a6feaSMaxime Coquelin static struct fdset *fdsets[MAX_FDSETS];
47e68a6feaSMaxime Coquelin static pthread_mutex_t fdsets_mutex = PTHREAD_MUTEX_INITIALIZER;
48e68a6feaSMaxime Coquelin 
490e38b42bSDavid Marchand static uint32_t fdset_event_dispatch(void *arg);
500e38b42bSDavid Marchand 
51e68a6feaSMaxime Coquelin static struct fdset *
fdset_lookup(const char * name)52e68a6feaSMaxime Coquelin fdset_lookup(const char *name)
53e68a6feaSMaxime Coquelin {
54e68a6feaSMaxime Coquelin 	int i;
55e68a6feaSMaxime Coquelin 
56e68a6feaSMaxime Coquelin 	for (i = 0; i < MAX_FDSETS; i++) {
57e68a6feaSMaxime Coquelin 		struct fdset *fdset = fdsets[i];
58e68a6feaSMaxime Coquelin 		if (fdset == NULL)
59e68a6feaSMaxime Coquelin 			continue;
60e68a6feaSMaxime Coquelin 
61e68a6feaSMaxime Coquelin 		if (!strncmp(fdset->name, name, RTE_THREAD_NAME_SIZE))
62e68a6feaSMaxime Coquelin 			return fdset;
63e68a6feaSMaxime Coquelin 	}
64e68a6feaSMaxime Coquelin 
65e68a6feaSMaxime Coquelin 	return NULL;
66e68a6feaSMaxime Coquelin }
67e68a6feaSMaxime Coquelin 
68e68a6feaSMaxime Coquelin static int
fdset_insert(struct fdset * fdset)69e68a6feaSMaxime Coquelin fdset_insert(struct fdset *fdset)
70e68a6feaSMaxime Coquelin {
71e68a6feaSMaxime Coquelin 	int i;
72e68a6feaSMaxime Coquelin 
73e68a6feaSMaxime Coquelin 	for (i = 0; i < MAX_FDSETS; i++) {
74e68a6feaSMaxime Coquelin 		if (fdsets[i] == NULL) {
75e68a6feaSMaxime Coquelin 			fdsets[i] = fdset;
76e68a6feaSMaxime Coquelin 			return 0;
77e68a6feaSMaxime Coquelin 		}
78e68a6feaSMaxime Coquelin 	}
79e68a6feaSMaxime Coquelin 
80e68a6feaSMaxime Coquelin 	return -1;
81e68a6feaSMaxime Coquelin }
82e68a6feaSMaxime Coquelin 
83e68a6feaSMaxime Coquelin struct fdset *
fdset_init(const char * name)84e68a6feaSMaxime Coquelin fdset_init(const char *name)
850c18e4fbSMaxime Coquelin {
86e68a6feaSMaxime Coquelin 	struct fdset *fdset;
87e68a6feaSMaxime Coquelin 	uint32_t val;
8899a2dd95SBruce Richardson 	int i;
8999a2dd95SBruce Richardson 
90e68a6feaSMaxime Coquelin 	pthread_mutex_lock(&fdsets_mutex);
91e68a6feaSMaxime Coquelin 	fdset = fdset_lookup(name);
92e68a6feaSMaxime Coquelin 	if (fdset) {
93e68a6feaSMaxime Coquelin 		pthread_mutex_unlock(&fdsets_mutex);
94e68a6feaSMaxime Coquelin 		return fdset;
95e68a6feaSMaxime Coquelin 	}
96e68a6feaSMaxime Coquelin 
97e68a6feaSMaxime Coquelin 	fdset = rte_zmalloc(NULL, sizeof(*fdset), 0);
98e68a6feaSMaxime Coquelin 	if (!fdset) {
990e38b42bSDavid Marchand 		VHOST_FDMAN_LOG(ERR, "failed to alloc fdset %s", name);
100e68a6feaSMaxime Coquelin 		goto err_unlock;
101e68a6feaSMaxime Coquelin 	}
102e68a6feaSMaxime Coquelin 
103e68a6feaSMaxime Coquelin 	rte_strscpy(fdset->name, name, RTE_THREAD_NAME_SIZE);
104e68a6feaSMaxime Coquelin 
105e68a6feaSMaxime Coquelin 	pthread_mutex_init(&fdset->fd_mutex, NULL);
10699a2dd95SBruce Richardson 
1070e38b42bSDavid Marchand 	for (i = 0; i < (int)RTE_DIM(fdset->fd); i++) {
108e68a6feaSMaxime Coquelin 		fdset->fd[i].fd = -1;
109e68a6feaSMaxime Coquelin 		fdset->fd[i].dat = NULL;
11099a2dd95SBruce Richardson 	}
1110e38b42bSDavid Marchand 	LIST_INIT(&fdset->fdlist);
1127945769cSMaxime Coquelin 
1130e38b42bSDavid Marchand 	/*
1140e38b42bSDavid Marchand 	 * Any non-zero value would work (see man epoll_create),
1150e38b42bSDavid Marchand 	 * but pass MAX_FDS for consistency.
1160e38b42bSDavid Marchand 	 */
1170e38b42bSDavid Marchand 	fdset->epfd = epoll_create(MAX_FDS);
1180e38b42bSDavid Marchand 	if (fdset->epfd < 0) {
1190e38b42bSDavid Marchand 		VHOST_FDMAN_LOG(ERR, "failed to create epoll for %s fdset", name);
120e68a6feaSMaxime Coquelin 		goto err_free;
121e68a6feaSMaxime Coquelin 	}
122e68a6feaSMaxime Coquelin 
123e68a6feaSMaxime Coquelin 	if (rte_thread_create_internal_control(&fdset->tid, fdset->name,
124e68a6feaSMaxime Coquelin 					fdset_event_dispatch, fdset)) {
125e68a6feaSMaxime Coquelin 		VHOST_FDMAN_LOG(ERR, "Failed to create %s event dispatch thread",
126e68a6feaSMaxime Coquelin 				fdset->name);
1270e38b42bSDavid Marchand 		goto err_epoll;
128e68a6feaSMaxime Coquelin 	}
129e68a6feaSMaxime Coquelin 
130e68a6feaSMaxime Coquelin 	if (fdset_insert(fdset)) {
131e68a6feaSMaxime Coquelin 		VHOST_FDMAN_LOG(ERR, "Failed to insert fdset %s", name);
132e68a6feaSMaxime Coquelin 		goto err_thread;
133e68a6feaSMaxime Coquelin 	}
134e68a6feaSMaxime Coquelin 
135e68a6feaSMaxime Coquelin 	pthread_mutex_unlock(&fdsets_mutex);
136e68a6feaSMaxime Coquelin 
137e68a6feaSMaxime Coquelin 	return fdset;
138e68a6feaSMaxime Coquelin 
139e68a6feaSMaxime Coquelin err_thread:
140e68a6feaSMaxime Coquelin 	fdset->destroy = true;
141e68a6feaSMaxime Coquelin 	rte_thread_join(fdset->tid, &val);
1420e38b42bSDavid Marchand err_epoll:
1430e38b42bSDavid Marchand 	close(fdset->epfd);
144e68a6feaSMaxime Coquelin err_free:
145e68a6feaSMaxime Coquelin 	rte_free(fdset);
146e68a6feaSMaxime Coquelin err_unlock:
147e68a6feaSMaxime Coquelin 	pthread_mutex_unlock(&fdsets_mutex);
148e68a6feaSMaxime Coquelin 
149e68a6feaSMaxime Coquelin 	return NULL;
15099a2dd95SBruce Richardson }
15199a2dd95SBruce Richardson 
1520e38b42bSDavid Marchand static int
fdset_insert_entry(struct fdset * pfdset,int fd,fd_cb rcb,fd_cb wcb,void * dat)1530e38b42bSDavid Marchand fdset_insert_entry(struct fdset *pfdset, int fd, fd_cb rcb, fd_cb wcb, void *dat)
1540e38b42bSDavid Marchand {
1550e38b42bSDavid Marchand 	struct fdentry *pfdentry;
1560e38b42bSDavid Marchand 
1570e38b42bSDavid Marchand 	if (pfdset->next_free_idx >= (int)RTE_DIM(pfdset->fd))
1580e38b42bSDavid Marchand 		return -1;
1590e38b42bSDavid Marchand 
1600e38b42bSDavid Marchand 	pfdentry = &pfdset->fd[pfdset->next_free_idx];
1610e38b42bSDavid Marchand 	pfdentry->fd  = fd;
1620e38b42bSDavid Marchand 	pfdentry->rcb = rcb;
1630e38b42bSDavid Marchand 	pfdentry->wcb = wcb;
1640e38b42bSDavid Marchand 	pfdentry->dat = dat;
1650e38b42bSDavid Marchand 
1660e38b42bSDavid Marchand 	LIST_INSERT_HEAD(&pfdset->fdlist, pfdentry, next);
1670e38b42bSDavid Marchand 
1680e38b42bSDavid Marchand 	/* Find next free slot */
1690e38b42bSDavid Marchand 	pfdset->next_free_idx++;
1700e38b42bSDavid Marchand 	for (; pfdset->next_free_idx < (int)RTE_DIM(pfdset->fd); pfdset->next_free_idx++) {
1710e38b42bSDavid Marchand 		if (pfdset->fd[pfdset->next_free_idx].fd != -1)
1720e38b42bSDavid Marchand 			continue;
1730e38b42bSDavid Marchand 		break;
1740e38b42bSDavid Marchand 	}
1750e38b42bSDavid Marchand 
1760e38b42bSDavid Marchand 	return 0;
1770e38b42bSDavid Marchand }
1780e38b42bSDavid Marchand 
1790e38b42bSDavid Marchand static void
fdset_remove_entry(struct fdset * pfdset,struct fdentry * pfdentry)1800e38b42bSDavid Marchand fdset_remove_entry(struct fdset *pfdset, struct fdentry *pfdentry)
1810e38b42bSDavid Marchand {
1820e38b42bSDavid Marchand 	int entry_idx;
1830e38b42bSDavid Marchand 
1840e38b42bSDavid Marchand 	pfdentry->fd = -1;
1850e38b42bSDavid Marchand 	pfdentry->rcb = pfdentry->wcb = NULL;
1860e38b42bSDavid Marchand 	pfdentry->dat = NULL;
1870e38b42bSDavid Marchand 
1880e38b42bSDavid Marchand 	entry_idx = pfdentry - pfdset->fd;
1890e38b42bSDavid Marchand 	if (entry_idx < pfdset->next_free_idx)
1900e38b42bSDavid Marchand 		pfdset->next_free_idx = entry_idx;
1910e38b42bSDavid Marchand 
1920e38b42bSDavid Marchand 	LIST_REMOVE(pfdentry, next);
1930e38b42bSDavid Marchand }
1940e38b42bSDavid Marchand 
1950e38b42bSDavid Marchand static struct fdentry *
fdset_find_entry_locked(struct fdset * pfdset,int fd)1960e38b42bSDavid Marchand fdset_find_entry_locked(struct fdset *pfdset, int fd)
1970e38b42bSDavid Marchand {
1980e38b42bSDavid Marchand 	struct fdentry *pfdentry;
1990e38b42bSDavid Marchand 
2000e38b42bSDavid Marchand 	LIST_FOREACH(pfdentry, &pfdset->fdlist, next) {
2010e38b42bSDavid Marchand 		if (pfdentry->fd != fd)
2020e38b42bSDavid Marchand 			continue;
2030e38b42bSDavid Marchand 		return pfdentry;
2040e38b42bSDavid Marchand 	}
2050e38b42bSDavid Marchand 
2060e38b42bSDavid Marchand 	return NULL;
2070e38b42bSDavid Marchand }
2080e38b42bSDavid Marchand 
20999a2dd95SBruce Richardson /**
21099a2dd95SBruce Richardson  * Register the fd in the fdset with read/write handler and context.
21199a2dd95SBruce Richardson  */
212e68a6feaSMaxime Coquelin int
fdset_add(struct fdset * pfdset,int fd,fd_cb rcb,fd_cb wcb,void * dat)213e68a6feaSMaxime Coquelin fdset_add(struct fdset *pfdset, int fd, fd_cb rcb, fd_cb wcb, void *dat)
214e68a6feaSMaxime Coquelin {
2150e38b42bSDavid Marchand 	struct epoll_event ev;
2160e38b42bSDavid Marchand 	struct fdentry *pfdentry;
2170e38b42bSDavid Marchand 	int ret = 0;
218e68a6feaSMaxime Coquelin 
2190e38b42bSDavid Marchand 	if (pfdset == NULL || fd == -1) {
2200e38b42bSDavid Marchand 		ret = -1;
2210e38b42bSDavid Marchand 		goto out;
22299a2dd95SBruce Richardson 	}
22399a2dd95SBruce Richardson 
2240e38b42bSDavid Marchand 	pthread_mutex_lock(&pfdset->fd_mutex);
2250e38b42bSDavid Marchand 	ret = fdset_insert_entry(pfdset, fd, rcb, wcb, dat);
2260e38b42bSDavid Marchand 	if (ret < 0) {
2270e38b42bSDavid Marchand 		VHOST_FDMAN_LOG(ERR, "failed to insert fdset entry");
2280e38b42bSDavid Marchand 		pthread_mutex_unlock(&pfdset->fd_mutex);
2290e38b42bSDavid Marchand 		goto out;
2300e38b42bSDavid Marchand 	}
2310e38b42bSDavid Marchand 	pthread_mutex_unlock(&pfdset->fd_mutex);
2320e38b42bSDavid Marchand 
2330e38b42bSDavid Marchand 	ev.events = EPOLLERR;
2340e38b42bSDavid Marchand 	ev.events |= rcb ? EPOLLIN : 0;
2350e38b42bSDavid Marchand 	ev.events |= wcb ? EPOLLOUT : 0;
2360e38b42bSDavid Marchand 	ev.data.fd = fd;
2370e38b42bSDavid Marchand 
2380e38b42bSDavid Marchand 	ret = epoll_ctl(pfdset->epfd, EPOLL_CTL_ADD, fd, &ev);
2390e38b42bSDavid Marchand 	if (ret < 0) {
2400e38b42bSDavid Marchand 		VHOST_FDMAN_LOG(ERR, "could not add %d fd to %d epfd: %s",
2410e38b42bSDavid Marchand 			fd, pfdset->epfd, strerror(errno));
2420e38b42bSDavid Marchand 		goto out_remove;
2430e38b42bSDavid Marchand 	}
2440e38b42bSDavid Marchand 
2456bdc1460SMaxime Coquelin 	return 0;
2460e38b42bSDavid Marchand out_remove:
2470e38b42bSDavid Marchand 	pthread_mutex_lock(&pfdset->fd_mutex);
2480e38b42bSDavid Marchand 	pfdentry = fdset_find_entry_locked(pfdset, fd);
2490e38b42bSDavid Marchand 	if (pfdentry)
2500e38b42bSDavid Marchand 		fdset_remove_entry(pfdset, pfdentry);
2510e38b42bSDavid Marchand 	pthread_mutex_unlock(&pfdset->fd_mutex);
2520e38b42bSDavid Marchand out:
2530e38b42bSDavid Marchand 	return ret;
2540e38b42bSDavid Marchand }
2550e38b42bSDavid Marchand 
2560e38b42bSDavid Marchand static void
fdset_del_locked(struct fdset * pfdset,struct fdentry * pfdentry)2570e38b42bSDavid Marchand fdset_del_locked(struct fdset *pfdset, struct fdentry *pfdentry)
2580e38b42bSDavid Marchand {
259*1e5bf7b6SMaxime Coquelin 	if (epoll_ctl(pfdset->epfd, EPOLL_CTL_DEL, pfdentry->fd, NULL) == -1) {
260*1e5bf7b6SMaxime Coquelin 		if (errno == EBADF) /* File might have already been closed. */
261*1e5bf7b6SMaxime Coquelin 			VHOST_FDMAN_LOG(DEBUG, "could not remove %d fd from %d epfd: %s",
262*1e5bf7b6SMaxime Coquelin 				pfdentry->fd, pfdset->epfd, strerror(errno));
263*1e5bf7b6SMaxime Coquelin 		else
2640e38b42bSDavid Marchand 			VHOST_FDMAN_LOG(ERR, "could not remove %d fd from %d epfd: %s",
2650e38b42bSDavid Marchand 				pfdentry->fd, pfdset->epfd, strerror(errno));
266*1e5bf7b6SMaxime Coquelin 	}
2670e38b42bSDavid Marchand 
2680e38b42bSDavid Marchand 	fdset_remove_entry(pfdset, pfdentry);
2690e38b42bSDavid Marchand }
2700e38b42bSDavid Marchand 
2710e38b42bSDavid Marchand void
fdset_del(struct fdset * pfdset,int fd)27299a2dd95SBruce Richardson fdset_del(struct fdset *pfdset, int fd)
27399a2dd95SBruce Richardson {
2740e38b42bSDavid Marchand 	struct fdentry *pfdentry;
27599a2dd95SBruce Richardson 
27699a2dd95SBruce Richardson 	if (pfdset == NULL || fd == -1)
2770e38b42bSDavid Marchand 		return;
27899a2dd95SBruce Richardson 
27999a2dd95SBruce Richardson 	do {
28099a2dd95SBruce Richardson 		pthread_mutex_lock(&pfdset->fd_mutex);
2810e38b42bSDavid Marchand 		pfdentry = fdset_find_entry_locked(pfdset, fd);
2820e38b42bSDavid Marchand 		if (pfdentry != NULL && pfdentry->busy == 0) {
2830e38b42bSDavid Marchand 			fdset_del_locked(pfdset, pfdentry);
2840e38b42bSDavid Marchand 			pfdentry = NULL;
28599a2dd95SBruce Richardson 		}
28699a2dd95SBruce Richardson 		pthread_mutex_unlock(&pfdset->fd_mutex);
2870e38b42bSDavid Marchand 	} while (pfdentry != NULL);
28899a2dd95SBruce Richardson }
28999a2dd95SBruce Richardson 
29099a2dd95SBruce Richardson /**
29199a2dd95SBruce Richardson  *  Unregister the fd from the fdset.
29299a2dd95SBruce Richardson  *
29399a2dd95SBruce Richardson  *  If parameters are invalid, return directly -2.
29499a2dd95SBruce Richardson  *  And check whether fd is busy, if yes, return -1.
29599a2dd95SBruce Richardson  *  Otherwise, try to delete the fd from fdset and
29699a2dd95SBruce Richardson  *  return true.
29799a2dd95SBruce Richardson  */
29899a2dd95SBruce Richardson int
fdset_try_del(struct fdset * pfdset,int fd)29999a2dd95SBruce Richardson fdset_try_del(struct fdset *pfdset, int fd)
30099a2dd95SBruce Richardson {
3010e38b42bSDavid Marchand 	struct fdentry *pfdentry;
30299a2dd95SBruce Richardson 
30399a2dd95SBruce Richardson 	if (pfdset == NULL || fd == -1)
30499a2dd95SBruce Richardson 		return -2;
30599a2dd95SBruce Richardson 
30699a2dd95SBruce Richardson 	pthread_mutex_lock(&pfdset->fd_mutex);
3070e38b42bSDavid Marchand 	pfdentry = fdset_find_entry_locked(pfdset, fd);
3080e38b42bSDavid Marchand 	if (pfdentry != NULL && pfdentry->busy != 0) {
30999a2dd95SBruce Richardson 		pthread_mutex_unlock(&pfdset->fd_mutex);
31099a2dd95SBruce Richardson 		return -1;
31199a2dd95SBruce Richardson 	}
31299a2dd95SBruce Richardson 
3130e38b42bSDavid Marchand 	if (pfdentry != NULL)
3140e38b42bSDavid Marchand 		fdset_del_locked(pfdset, pfdentry);
31599a2dd95SBruce Richardson 
31699a2dd95SBruce Richardson 	pthread_mutex_unlock(&pfdset->fd_mutex);
31799a2dd95SBruce Richardson 	return 0;
31899a2dd95SBruce Richardson }
31999a2dd95SBruce Richardson 
32099a2dd95SBruce Richardson /**
32199a2dd95SBruce Richardson  * This functions runs in infinite blocking loop until there is no fd in
32299a2dd95SBruce Richardson  * pfdset. It calls corresponding r/w handler if there is event on the fd.
32399a2dd95SBruce Richardson  *
32499a2dd95SBruce Richardson  * Before the callback is called, we set the flag to busy status; If other
32599a2dd95SBruce Richardson  * thread(now rte_vhost_driver_unregister) calls fdset_del concurrently, it
32699a2dd95SBruce Richardson  * will wait until the flag is reset to zero(which indicates the callback is
32799a2dd95SBruce Richardson  * finished), then it could free the context after fdset_del.
32899a2dd95SBruce Richardson  */
329e68a6feaSMaxime Coquelin static uint32_t
fdset_event_dispatch(void * arg)33099a2dd95SBruce Richardson fdset_event_dispatch(void *arg)
33199a2dd95SBruce Richardson {
33299a2dd95SBruce Richardson 	int i;
33399a2dd95SBruce Richardson 	fd_cb rcb, wcb;
33499a2dd95SBruce Richardson 	void *dat;
33599a2dd95SBruce Richardson 	int fd, numfds;
33699a2dd95SBruce Richardson 	int remove1, remove2;
33799a2dd95SBruce Richardson 	struct fdset *pfdset = arg;
33899a2dd95SBruce Richardson 
33999a2dd95SBruce Richardson 	if (pfdset == NULL)
3401c1abf17SThomas Monjalon 		return 0;
34199a2dd95SBruce Richardson 
34299a2dd95SBruce Richardson 	while (1) {
3430e38b42bSDavid Marchand 		struct epoll_event events[MAX_FDS];
3440e38b42bSDavid Marchand 		struct fdentry *pfdentry;
34599a2dd95SBruce Richardson 
3460e38b42bSDavid Marchand 		numfds = epoll_wait(pfdset->epfd, events, RTE_DIM(events), 1000);
3470e38b42bSDavid Marchand 		if (numfds < 0)
34899a2dd95SBruce Richardson 			continue;
34999a2dd95SBruce Richardson 
35099a2dd95SBruce Richardson 		for (i = 0; i < numfds; i++) {
35199a2dd95SBruce Richardson 			pthread_mutex_lock(&pfdset->fd_mutex);
35299a2dd95SBruce Richardson 
3530e38b42bSDavid Marchand 			fd = events[i].data.fd;
3540e38b42bSDavid Marchand 			pfdentry = fdset_find_entry_locked(pfdset, fd);
3550e38b42bSDavid Marchand 			if (pfdentry == NULL) {
35699a2dd95SBruce Richardson 				pthread_mutex_unlock(&pfdset->fd_mutex);
35799a2dd95SBruce Richardson 				continue;
35899a2dd95SBruce Richardson 			}
35999a2dd95SBruce Richardson 
36099a2dd95SBruce Richardson 			remove1 = remove2 = 0;
36199a2dd95SBruce Richardson 
36299a2dd95SBruce Richardson 			rcb = pfdentry->rcb;
36399a2dd95SBruce Richardson 			wcb = pfdentry->wcb;
36499a2dd95SBruce Richardson 			dat = pfdentry->dat;
36599a2dd95SBruce Richardson 			pfdentry->busy = 1;
36699a2dd95SBruce Richardson 
36799a2dd95SBruce Richardson 			pthread_mutex_unlock(&pfdset->fd_mutex);
36899a2dd95SBruce Richardson 
3690e38b42bSDavid Marchand 			if (rcb && events[i].events & (EPOLLIN | EPOLLERR | EPOLLHUP))
37099a2dd95SBruce Richardson 				rcb(fd, dat, &remove1);
3710e38b42bSDavid Marchand 			if (wcb && events[i].events & (EPOLLOUT | EPOLLERR | EPOLLHUP))
37299a2dd95SBruce Richardson 				wcb(fd, dat, &remove2);
37399a2dd95SBruce Richardson 			pfdentry->busy = 0;
37499a2dd95SBruce Richardson 			/*
37599a2dd95SBruce Richardson 			 * fdset_del needs to check busy flag.
37699a2dd95SBruce Richardson 			 * We don't allow fdset_del to be called in callback
37799a2dd95SBruce Richardson 			 * directly.
37899a2dd95SBruce Richardson 			 */
37999a2dd95SBruce Richardson 			/*
3800e38b42bSDavid Marchand 			 * A concurrent fdset_del may have been waiting for the
3810e38b42bSDavid Marchand 			 * fdentry not to be busy, so we can't call
3820e38b42bSDavid Marchand 			 * fdset_del_locked().
38399a2dd95SBruce Richardson 			 */
3840e38b42bSDavid Marchand 			if (remove1 || remove2)
3850e38b42bSDavid Marchand 				fdset_del(pfdset, fd);
38699a2dd95SBruce Richardson 		}
387e68a6feaSMaxime Coquelin 
388e68a6feaSMaxime Coquelin 		if (pfdset->destroy)
389e68a6feaSMaxime Coquelin 			break;
39099a2dd95SBruce Richardson 	}
39199a2dd95SBruce Richardson 
3921c1abf17SThomas Monjalon 	return 0;
39399a2dd95SBruce Richardson }
394