fs_epoll: serveral epoll issues fix

1. fs_epoll: try again when epoll_teardown() return 0
when poll_notify() called larger than twice when epoll_wait() blocked
in the eph->sem, the semcount will be larger than 1 when epoll_wait()
unblocked and will return 0 directly at the next epoll_wait.
So retry to wait the eph->sem again when epoll_teardown return 0.

2. fs_epoll: poll_setup the fd again even this fd got non-expected event
Some poll implementations need call poll_setup again when their internal
states changed (e.g., local socket), so should add the fd to the epoll
teardown list and poll_setup again at the next epoll_wait even this fd
got the user non-expected event.

Signed-off-by: Bowen Wang <wangbowen6@xiaomi.com>
This commit is contained in:
Bowen Wang 2023-09-22 15:17:15 +08:00 committed by Xiang Xiao
parent 22d11aafa2
commit 2f9c082f8f
3 changed files with 135 additions and 28 deletions

View File

@ -48,9 +48,11 @@
struct epoll_node_s struct epoll_node_s
{ {
struct list_node node; struct list_node node;
epoll_data_t data; epoll_data_t data;
struct pollfd pfd; bool notified;
struct pollfd pfd;
FAR struct epoll_head_s *eph;
}; };
typedef struct epoll_node_s epoll_node_t; typedef struct epoll_node_s epoll_node_t;
@ -262,6 +264,20 @@ static int epoll_do_create(int size, int flags)
return fd; return fd;
} }
/****************************************************************************
* Name: epoll_setup
*
* Description:
* Setup all the fd.
*
* Input Parameters:
* eph - The epoll head pointer
*
* Returned Value:
* Positive on success, negative on fail
*
****************************************************************************/
static int epoll_setup(FAR epoll_head_t *eph) static int epoll_setup(FAR epoll_head_t *eph)
{ {
FAR epoll_node_t *tepn; FAR epoll_node_t *tepn;
@ -280,6 +296,7 @@ static int epoll_setup(FAR epoll_head_t *eph)
* cover the situation several poll event pending on one fd. * cover the situation several poll event pending on one fd.
*/ */
epn->notified = false;
epn->pfd.revents = 0; epn->pfd.revents = 0;
ret = poll_fdsetup(epn->pfd.fd, &epn->pfd, true); ret = poll_fdsetup(epn->pfd.fd, &epn->pfd, true);
if (ret < 0) if (ret < 0)
@ -297,6 +314,24 @@ static int epoll_setup(FAR epoll_head_t *eph)
return ret; return ret;
} }
/****************************************************************************
* Name: epoll_teardown
*
* Description:
* Teardown all the notifed fd and check the notified fd's event with user
* expected event.
*
* Input Parameters:
* eph - The epoll head pointer
* evs - The epoll events array
* maxevents - The epoll events array size
*
* Returned Value:
* Return the number of fd that notifed and the events is also user
* expected.
*
****************************************************************************/
static int epoll_teardown(FAR epoll_head_t *eph, FAR struct epoll_event *evs, static int epoll_teardown(FAR epoll_head_t *eph, FAR struct epoll_event *evs,
int maxevents) int maxevents)
{ {
@ -308,12 +343,22 @@ static int epoll_teardown(FAR epoll_head_t *eph, FAR struct epoll_event *evs,
list_for_every_entry_safe(&eph->setup, epn, tepn, epoll_node_t, node) list_for_every_entry_safe(&eph->setup, epn, tepn, epoll_node_t, node)
{ {
/* Only check the notifed fd */
if (!epn->notified)
{
continue;
}
/* Teradown all the notified fd */
poll_fdsetup(epn->pfd.fd, &epn->pfd, false);
list_delete(&epn->node);
if (epn->pfd.revents != 0 && i < maxevents) if (epn->pfd.revents != 0 && i < maxevents)
{ {
evs[i].data = epn->data; evs[i].data = epn->data;
evs[i++].events = epn->pfd.revents; evs[i++].events = epn->pfd.revents;
poll_fdsetup(epn->pfd.fd, &epn->pfd, false);
list_delete(&epn->node);
if ((epn->pfd.events & EPOLLONESHOT) != 0) if ((epn->pfd.events & EPOLLONESHOT) != 0)
{ {
list_add_tail(&eph->oneshot, &epn->node); list_add_tail(&eph->oneshot, &epn->node);
@ -323,12 +368,47 @@ static int epoll_teardown(FAR epoll_head_t *eph, FAR struct epoll_event *evs,
list_add_tail(&eph->teardown, &epn->node); list_add_tail(&eph->teardown, &epn->node);
} }
} }
else
{
list_add_tail(&eph->teardown, &epn->node);
}
} }
nxmutex_unlock(&eph->lock); nxmutex_unlock(&eph->lock);
return i; return i;
} }
/****************************************************************************
* Name: epoll_default_cb
*
* Description:
* The default epoll callback function, this function do the final step of
* poll notification.
*
* Input Parameters:
* fds - The fds
*
* Returned Value:
* None
*
****************************************************************************/
static void epoll_default_cb(FAR struct pollfd *fds)
{
FAR epoll_node_t *epn = fds->arg;
int semcount = 0;
epn->notified = true;
if (fds->revents != 0)
{
nxsem_get_value(&epn->eph->sem, &semcount);
if (semcount < 1)
{
nxsem_post(&epn->eph->sem);
}
}
}
/**************************************************************************** /****************************************************************************
* Public Functions * Public Functions
****************************************************************************/ ****************************************************************************/
@ -472,11 +552,13 @@ int epoll_ctl(int epfd, int op, int fd, FAR struct epoll_event *ev)
} }
epn = container_of(list_remove_head(&eph->free), epoll_node_t, node); epn = container_of(list_remove_head(&eph->free), epoll_node_t, node);
epn->eph = eph;
epn->data = ev->data; epn->data = ev->data;
epn->pfd.events = ev->events; epn->notified = false;
epn->pfd.events = ev->events | POLLALWAYS;
epn->pfd.fd = fd; epn->pfd.fd = fd;
epn->pfd.arg = &eph->sem; epn->pfd.arg = epn;
epn->pfd.cb = poll_default_cb; epn->pfd.cb = epoll_default_cb;
epn->pfd.revents = 0; epn->pfd.revents = 0;
ret = poll_fdsetup(fd, &epn->pfd, true); ret = poll_fdsetup(fd, &epn->pfd, true);
@ -530,12 +612,13 @@ int epoll_ctl(int epfd, int op, int fd, FAR struct epoll_event *ev)
{ {
if (epn->pfd.fd == fd) if (epn->pfd.fd == fd)
{ {
if (epn->pfd.events != ev->events) if (epn->pfd.events != (ev->events | POLLALWAYS))
{ {
poll_fdsetup(fd, &epn->pfd, false); poll_fdsetup(fd, &epn->pfd, false);
epn->notified = false;
epn->data = ev->data; epn->data = ev->data;
epn->pfd.events = ev->events; epn->pfd.events = ev->events | POLLALWAYS;
epn->pfd.fd = fd; epn->pfd.fd = fd;
epn->pfd.revents = 0; epn->pfd.revents = 0;
@ -554,10 +637,11 @@ int epoll_ctl(int epfd, int op, int fd, FAR struct epoll_event *ev)
{ {
if (epn->pfd.fd == fd) if (epn->pfd.fd == fd)
{ {
if (epn->pfd.events != ev->events) if (epn->pfd.events != (ev->events | POLLALWAYS))
{ {
epn->notified = false;
epn->data = ev->data; epn->data = ev->data;
epn->pfd.events = ev->events; epn->pfd.events = ev->events | POLLALWAYS;
epn->pfd.fd = fd; epn->pfd.fd = fd;
epn->pfd.revents = 0; epn->pfd.revents = 0;
@ -579,8 +663,9 @@ int epoll_ctl(int epfd, int op, int fd, FAR struct epoll_event *ev)
{ {
if (epn->pfd.fd == fd) if (epn->pfd.fd == fd)
{ {
epn->notified = false;
epn->data = ev->data; epn->data = ev->data;
epn->pfd.events = ev->events; epn->pfd.events = ev->events | POLLALWAYS;
epn->pfd.fd = fd; epn->pfd.fd = fd;
epn->pfd.revents = 0; epn->pfd.revents = 0;
@ -630,6 +715,7 @@ int epoll_pwait(int epfd, FAR struct epoll_event *evs,
return ERROR; return ERROR;
} }
retry:
ret = epoll_setup(eph); ret = epoll_setup(eph);
if (ret < 0) if (ret < 0)
{ {
@ -642,7 +728,7 @@ int epoll_pwait(int epfd, FAR struct epoll_event *evs,
if (timeout == 0) if (timeout == 0)
{ {
ret = OK; ret = -ETIMEDOUT;
} }
else if (timeout > 0) else if (timeout > 0)
{ {
@ -658,10 +744,6 @@ int epoll_pwait(int epfd, FAR struct epoll_event *evs,
#endif #endif
ret = nxsem_tickwait(&eph->sem, ticks); ret = nxsem_tickwait(&eph->sem, ticks);
if (ret == -ETIMEDOUT)
{
ret = OK;
}
} }
else else
{ {
@ -669,12 +751,22 @@ int epoll_pwait(int epfd, FAR struct epoll_event *evs,
} }
nxsig_procmask(SIG_SETMASK, &oldsigmask, NULL); nxsig_procmask(SIG_SETMASK, &oldsigmask, NULL);
if (ret < 0) if (ret < 0 && ret != -ETIMEDOUT)
{ {
goto err; goto err;
} }
else /* ret >= 0 or ret == -ETIMEDOUT */
{
int num = epoll_teardown(eph, evs, maxevents);
if (num == 0 && ret >= 0)
{
goto retry;
}
return epoll_teardown(eph, evs, maxevents); ret = num;
}
return ret;
err: err:
set_errno(-ret); set_errno(-ret);
@ -704,6 +796,7 @@ int epoll_wait(int epfd, FAR struct epoll_event *evs,
return ERROR; return ERROR;
} }
retry:
ret = epoll_setup(eph); ret = epoll_setup(eph);
if (ret < 0) if (ret < 0)
{ {
@ -714,7 +807,7 @@ int epoll_wait(int epfd, FAR struct epoll_event *evs,
if (timeout == 0) if (timeout == 0)
{ {
ret = OK; ret = -ETIMEDOUT;
} }
else if (timeout > 0) else if (timeout > 0)
{ {
@ -730,22 +823,28 @@ int epoll_wait(int epfd, FAR struct epoll_event *evs,
#endif #endif
ret = nxsem_tickwait(&eph->sem, ticks); ret = nxsem_tickwait(&eph->sem, ticks);
if (ret == -ETIMEDOUT)
{
ret = OK;
}
} }
else else
{ {
ret = nxsem_wait(&eph->sem); ret = nxsem_wait(&eph->sem);
} }
if (ret < 0) if (ret < 0 && ret != -ETIMEDOUT)
{ {
goto err; goto err;
} }
else /* ret >= 0 or ret == -ETIMEDOUT */
{
int num = epoll_teardown(eph, evs, maxevents);
if (num == 0 && ret >= 0)
{
goto retry;
}
return epoll_teardown(eph, evs, maxevents); ret = num;
}
return ret;
err: err:
set_errno(-ret); set_errno(-ret);

View File

@ -264,7 +264,8 @@ void poll_notify(FAR struct pollfd **afds, int nfds, pollevent_t eventset)
fds->revents &= ~POLLOUT; fds->revents &= ~POLLOUT;
} }
if (fds->revents != 0 && fds->cb != NULL) if ((fds->revents != 0 || (fds->events & POLLALWAYS) != 0) &&
fds->cb != NULL)
{ {
finfo("Report events: %08" PRIx32 "\n", fds->revents); finfo("Report events: %08" PRIx32 "\n", fds->revents);
fds->cb(fds); fds->cb(fds);

View File

@ -61,6 +61,11 @@
* Device has been disconnected (revents only). * Device has been disconnected (revents only).
* POLLNVAL * POLLNVAL
* Invalid fd member (revents only). * Invalid fd member (revents only).
*
* POLLALWAYS
* Indicate that should ALWAYS call the poll callback whether the
* drvier notified the user expected event or not, and this value is
* used inside kernal only (events only).
*/ */
#define POLLIN (0x01) /* NuttX does not make priority distinctions */ #define POLLIN (0x01) /* NuttX does not make priority distinctions */
@ -78,6 +83,8 @@
#define POLLRDHUP (0x10) /* NuttX does not support shutdown(fd, SHUT_RD) */ #define POLLRDHUP (0x10) /* NuttX does not support shutdown(fd, SHUT_RD) */
#define POLLNVAL (0x20) #define POLLNVAL (0x20)
#define POLLALWAYS (0x10000) /* For not conflict with Linux */
/**************************************************************************** /****************************************************************************
* Public Type Definitions * Public Type Definitions
****************************************************************************/ ****************************************************************************/