fs/epoll: add asynchronous epoll control support

In current implementation, the asynchronous call "epoll_ctl()" unable
to wakeup the thread if pending on "epoll_wait()", the newly
added/delete FD cannot be used in the current waiting list,
this patch uses a reserved file object to wake up pending poll internal,
re-traverse the waiting list when a new event comes.

Signed-off-by: chao.an <anchao@xiaomi.com>
This commit is contained in:
chao.an 2020-12-23 10:45:10 +08:00 committed by Xiang Xiao
parent 0114f7c58f
commit 39146ee367

View File

@ -48,6 +48,8 @@
#include <string.h>
#include <debug.h>
#include <nuttx/clock.h>
#include <nuttx/fs/fs.h>
#include <nuttx/kmalloc.h>
/****************************************************************************
@ -58,10 +60,38 @@ struct epoll_head
{
int size;
int occupied;
struct file fp;
struct inode in;
FAR epoll_data_t *data;
FAR struct pollfd *poll;
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int epoll_poll(FAR struct file *filep,
FAR struct pollfd *fds, bool setup);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations g_epoll_ops =
{
.poll = epoll_poll
};
/****************************************************************************
* Private Functions
****************************************************************************/
static int epoll_poll(FAR struct file *filep,
FAR struct pollfd *fds, bool setup)
{
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
@ -79,15 +109,25 @@ struct epoll_head
int epoll_create(int size)
{
FAR struct epoll_head *eph =
(FAR struct epoll_head *)kmm_malloc(sizeof(struct epoll_head) +
sizeof(epoll_data_t) * size +
sizeof(struct pollfd) * size);
FAR struct epoll_head *eph;
int reserve = size + 1;
eph = (FAR struct epoll_head *)
kmm_zalloc(sizeof(struct epoll_head) +
sizeof(epoll_data_t) * reserve +
sizeof(struct pollfd) * reserve);
eph->size = size;
eph->occupied = 0;
eph->data = (FAR epoll_data_t *)(eph + 1);
eph->poll = (FAR struct pollfd *)(eph->data + size);
eph->poll = (FAR struct pollfd *)(eph->data + reserve);
INODE_SET_DRIVER(&eph->in);
eph->in.u.i_ops = &g_epoll_ops;
eph->fp.f_inode = &eph->in;
eph->in.i_private = eph;
eph->poll[0].ptr = &eph->fp;
eph->poll[0].events = POLLIN | POLLFILE;
/* REVISIT: This will not work on machines where:
* sizeof(struct epoll_head *) > sizeof(int)
@ -175,7 +215,7 @@ int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev)
return -1;
}
for (i = 0; i < eph->occupied; i++)
for (i = 1; i <= eph->occupied; i++)
{
if (eph->poll[i].fd == fd)
{
@ -184,14 +224,14 @@ int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev)
}
}
eph->data[eph->occupied] = ev->data;
eph->data[++eph->occupied] = ev->data;
eph->poll[eph->occupied].events = ev->events | POLLERR | POLLHUP;
eph->poll[eph->occupied++].fd = fd;
eph->poll[eph->occupied].fd = fd;
break;
case EPOLL_CTL_DEL:
for (i = 0; i < eph->occupied; i++)
for (i = 1; i <= eph->occupied; i++)
{
if (eph->poll[i].fd == fd)
{
@ -214,8 +254,7 @@ int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev)
case EPOLL_CTL_MOD:
finfo("%08x CTL MOD(%d): fd=%d ev=%08" PRIx32 "\n",
epfd, eph->occupied, fd, ev->events);
for (i = 0; i < eph->occupied; i++)
for (i = 1; i <= eph->occupied; i++)
{
if (eph->poll[i].fd == fd)
{
@ -233,6 +272,12 @@ int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev)
return -1;
}
if (eph->poll[0].sem)
{
eph->poll[0].revents |= eph->poll[0].events;
nxsem_post(eph->poll[0].sem);
}
return 0;
}
@ -248,48 +293,64 @@ int epoll_pwait(int epfd, FAR struct epoll_event *evs,
*/
FAR struct epoll_head *eph = (FAR struct epoll_head *)((intptr_t)epfd);
struct timespec expire;
struct timespec curr;
struct timespec diff;
int counter;
int rc;
int i;
if (timeout >= 0)
{
expire.tv_sec = timeout / 1000;
expire.tv_nsec = timeout % 1000 * 1000;
#ifdef CONFIG_CLOCK_MONOTONIC
clock_gettime(CLOCK_MONOTONIC, &curr);
#else
clock_gettime(CLOCK_REALTIME, &curr);
#endif
clock_timespec_add(&curr, &expire, &expire);
}
again:
if (timeout < 0)
{
rc = ppoll(eph->poll, eph->occupied, NULL, sigmask);
rc = ppoll(eph->poll, eph->occupied + 1, NULL, sigmask);
}
else
{
struct timespec timeout_ts;
#ifdef CONFIG_CLOCK_MONOTONIC
clock_gettime(CLOCK_MONOTONIC, &curr);
#else
clock_gettime(CLOCK_REALTIME, &curr);
#endif
clock_timespec_subtract(&expire, &curr, &diff);
timeout_ts.tv_sec = timeout / 1000;
timeout_ts.tv_nsec = timeout % 1000 * 1000;
rc = ppoll(eph->poll, eph->occupied, &timeout_ts, sigmask);
rc = ppoll(eph->poll, eph->occupied + 1, &diff, sigmask);
}
if (rc <= 0)
{
if (rc < 0)
{
ferr("ERROR: %08x poll fail: %d for %d, %d msecs\n",
epfd, rc, eph->occupied, timeout);
for (i = 0; i < eph->occupied; i++)
{
ferr(" %02d: fd=%d\n", i, eph->poll[i].fd);
}
}
return rc;
}
/* Iterate over non NULL event fds */
else if (eph->poll[0].revents != 0)
{
if (--rc == 0)
{
goto again;
}
}
if (rc > maxevents)
{
rc = maxevents;
}
for (i = 0, counter = 0; i < rc && counter < eph->occupied; counter++)
/* Iterate over non NULL event fds */
for (i = 0, counter = 1; i < rc && counter <= eph->occupied; counter++)
{
if (eph->poll[counter].revents != 0)
{