Skip to content

lib: posix: Add support for eventfd #22863

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions arch/posix/include/posix_cheats.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,11 @@ void zephyr_app_main(void);
#define stat zap_stat
#define mkdir zap_mkdir

/* eventfd */
#define eventfd zap_eventfd
#define eventfd_read zap_eventfd_read
#define eventfd_write zap_eventfd_write

#endif /* CONFIG_POSIX_API */

#endif /* ZEPHYR_ARCH_POSIX_INCLUDE_POSIX_CHEATS_H_ */
91 changes: 91 additions & 0 deletions include/posix/sys/eventfd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2020 Tobias Svehagen
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_POSIX_SYS_EVENTFD_H_
#define ZEPHYR_INCLUDE_POSIX_SYS_EVENTFD_H_

#include <sys/fdtable.h>
#include <sys/types.h>

#include <fcntl.h>

#ifdef __cplusplus
extern "C" {
#endif

#define EFD_IN_USE 0x1
#define EFD_SEMAPHORE 0x2
#define EFD_NONBLOCK O_NONBLOCK
#define EFD_FLAGS_SET (EFD_SEMAPHORE | EFD_NONBLOCK)

typedef u64_t eventfd_t;

/**
* @brief Create a file descriptor for event notification
*
* The returned file descriptor can be used with POSIX read/write calls or
* with the eventfd_read/eventfd_write functions.
*
* It also supports polling and by including an eventfd in a call to poll,
* it is possible to signal and wake the polling thread by simply writing to
* the eventfd.
*
* When using read() and write() on an eventfd, the size must always be at
* least 8 bytes or the operation will fail with EINVAL.
*
* @return New eventfd file descriptor on success, -1 on error
*/
int eventfd(unsigned int initval, int flags);

/**
* @brief Read from an eventfd
*
* If call is successful, the value parameter will have the value 1
*
* @param fd File descriptor
* @param value Pointer for storing the read value
*
* @return 0 on success, -1 on error
*/
static inline int eventfd_read(int fd, eventfd_t *value)
{
const struct fd_op_vtable *efd_vtable;
ssize_t ret;
void *obj;

obj = z_get_fd_obj_and_vtable(fd, &efd_vtable);

ret = efd_vtable->read(obj, value, sizeof(*value));

return ret == sizeof(eventfd_t) ? 0 : -1;
}

/**
* @brief Write to an eventfd
*
* @param fd File descriptor
* @param value Value to write
*
* @return 0 on success, -1 on error
*/
static inline int eventfd_write(int fd, eventfd_t value)
{
const struct fd_op_vtable *efd_vtable;
ssize_t ret;
void *obj;

obj = z_get_fd_obj_and_vtable(fd, &efd_vtable);

ret = efd_vtable->write(obj, &value, sizeof(value));

return ret == sizeof(eventfd_t) ? 0 : -1;
}

#ifdef __cplusplus
}
#endif

#endif /* ZEPHYR_INCLUDE_POSIX_SYS_EVENTFD_H_ */
1 change: 1 addition & 0 deletions lib/posix/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ zephyr_library_sources_ifdef(CONFIG_PTHREAD_IPC semaphore.c)
zephyr_library_sources_ifdef(CONFIG_PTHREAD_IPC pthread_key.c)
zephyr_library_sources_ifdef(CONFIG_POSIX_MQUEUE mqueue.c)
zephyr_library_sources_ifdef(CONFIG_POSIX_FS fs.c)
zephyr_library_sources_ifdef(CONFIG_EVENTFD eventfd.c)

zephyr_library_include_directories(
${ZEPHYR_BASE}/kernel/include
Expand Down
16 changes: 16 additions & 0 deletions lib/posix/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,19 @@ config APP_LINK_WITH_POSIX_SUBSYS
depends on POSIX_API
help
Add POSIX subsystem header files to the 'app' include path.

config EVENTFD
bool "Enable support for eventfd"
depends on !ARCH_POSIX
help
Enable support for event file descriptors, eventfd. An eventfd can
be used as an event wait/notify mechanism together with POSIX calls
like read, write and poll.

config EVENTFD_MAX
int "Maximum number of eventfd's"
depends on EVENTFD
default 1
range 1 4096
help
The maximum number of supported event file descriptors.
241 changes: 241 additions & 0 deletions lib/posix/eventfd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
/*
* Copyright (c) 2020 Tobias Svehagen
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr.h>

#include <sys/eventfd.h>

#include <net/socket.h>

struct eventfd {
struct k_sem read_sem;
struct k_sem write_sem;
eventfd_t cnt;
int flags;
};

K_MUTEX_DEFINE(eventfd_mtx);
static struct eventfd efds[CONFIG_EVENTFD_MAX];

static int eventfd_poll_prepare(struct eventfd *efd,
struct zsock_pollfd *pfd,
struct k_poll_event **pev,
struct k_poll_event *pev_end)
{
ARG_UNUSED(efd);

if (pfd->events & ZSOCK_POLLIN) {
if (*pev == pev_end) {
errno = ENOMEM;
return -1;
}

(*pev)->obj = &efd->read_sem;
(*pev)->type = K_POLL_TYPE_SEM_AVAILABLE;
(*pev)->mode = K_POLL_MODE_NOTIFY_ONLY;
(*pev)->state = K_POLL_STATE_NOT_READY;
(*pev)++;
}

return 0;
}

static int eventfd_poll_update(struct eventfd *efd,
struct zsock_pollfd *pfd,
struct k_poll_event **pev)
{
ARG_UNUSED(efd);

if (pfd->events & ZSOCK_POLLOUT) {
pfd->revents |= ZSOCK_POLLOUT;
}

if (pfd->events & ZSOCK_POLLIN) {
if ((*pev)->state != K_POLL_STATE_NOT_READY) {
pfd->revents |= ZSOCK_POLLIN;
}
(*pev)++;
}

return 0;
}

static ssize_t eventfd_read_op(void *obj, void *buf, size_t sz)
{
struct eventfd *efd = obj;
eventfd_t count;
int ret = 0;

if (sz < sizeof(eventfd_t)) {
errno = EINVAL;
return -1;
}

if (efd->cnt == 0) {
if (efd->flags & EFD_NONBLOCK) {
ret = -EAGAIN;
} else {
ret = k_sem_take(&efd->read_sem, K_FOREVER);
}
}

if (ret < 0) {
errno = -ret;
return -1;
}

count = (efd->flags & EFD_SEMAPHORE) ? 1 : efd->cnt;
efd->cnt -= count;
*(eventfd_t *)buf = count;
k_sem_give(&efd->write_sem);

return sizeof(eventfd_t);
}

static ssize_t eventfd_write_op(void *obj, const void *buf, size_t sz)
{
eventfd_t count;
int ret = 0;

struct eventfd *efd = obj;

if (sz < sizeof(eventfd_t)) {
errno = EINVAL;
return -1;
}

count = *((eventfd_t *)buf);

if (count == UINT64_MAX) {
errno = EINVAL;
return -1;
}

if (UINT64_MAX - count <= efd->cnt) {
if (efd->flags & EFD_NONBLOCK) {
ret = -EAGAIN;
} else {
ret = k_sem_take(&efd->write_sem, K_FOREVER);
}
}

if (ret < 0) {
errno = -ret;
return -1;
}

efd->cnt += count;
k_sem_give(&efd->read_sem);

return sizeof(eventfd_t);
}

static int eventfd_ioctl_op(void *obj, unsigned int request, va_list args)
{
struct eventfd *efd = (struct eventfd *)obj;

switch (request) {
case F_GETFL:
return efd->flags & EFD_FLAGS_SET;

case F_SETFL: {
int flags;

flags = va_arg(args, int);

if (flags & ~EFD_FLAGS_SET) {
errno = EINVAL;
return -1;
}

efd->flags = flags;

return 0;
}

case ZFD_IOCTL_CLOSE:
efd->flags = 0;
return 0;

case ZFD_IOCTL_POLL_PREPARE: {
struct zsock_pollfd *pfd;
struct k_poll_event **pev;
struct k_poll_event *pev_end;

pfd = va_arg(args, struct zsock_pollfd *);
pev = va_arg(args, struct k_poll_event **);
pev_end = va_arg(args, struct k_poll_event *);

return eventfd_poll_prepare(obj, pfd, pev, pev_end);
}

case ZFD_IOCTL_POLL_UPDATE: {
struct zsock_pollfd *pfd;
struct k_poll_event **pev;

pfd = va_arg(args, struct zsock_pollfd *);
pev = va_arg(args, struct k_poll_event **);

return eventfd_poll_update(obj, pfd, pev);
}

default:
errno = EOPNOTSUPP;
return -1;
}
}

static const struct fd_op_vtable eventfd_fd_vtable = {
.read = eventfd_read_op,
.write = eventfd_write_op,
.ioctl = eventfd_ioctl_op,
};

int eventfd(unsigned int initval, int flags)
{
int i, fd;
void *obj = NULL;

if (flags & ~EFD_FLAGS_SET) {
errno = EINVAL;
return -1;
}

k_mutex_lock(&eventfd_mtx, K_FOREVER);

fd = z_reserve_fd();
if (fd < 0) {
k_mutex_unlock(&eventfd_mtx);
return -1;
}

for (i = 0; i < ARRAY_SIZE(efds); ++i) {
if (efds[i].flags & EFD_IN_USE) {
continue;
}

obj = &efds[i];
efds[i].flags = EFD_IN_USE | flags;
efds[i].cnt = 0;
k_sem_init(&efds[i].read_sem, 0, UINT32_MAX);
k_sem_init(&efds[i].write_sem, 0, UINT32_MAX);

break;
}

if (obj == NULL) {
z_free_fd(fd);
errno = ENOMEM;
k_mutex_unlock(&eventfd_mtx);
return -1;
}

z_finalize_fd(fd, obj, &eventfd_fd_vtable);

k_mutex_unlock(&eventfd_mtx);

return fd;
}
9 changes: 9 additions & 0 deletions samples/posix/eventfd/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.13.1)

find_package(Zephyr HINTS $ENV{ZEPHYR_BASE})
project(eventfd)

#target_include_directories(app PRIVATE ${ZEPHYR_BASE}/include/posix)
target_sources(app PRIVATE src/main.c)
4 changes: 4 additions & 0 deletions samples/posix/eventfd/Makefile.posix
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# This makefile builds the sample for a POSIX system, like Linux

eventfd: src/main.c
$(CC) $^ -o $@
7 changes: 7 additions & 0 deletions samples/posix/eventfd/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# General config
CONFIG_NEWLIB_LIBC=y
CONFIG_POSIX_API=y
CONFIG_EVENTFD=y

# Networking config
CONFIG_NETWORKING=y
Loading