123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- /*
- * Copyright (c) 2020 Tobias Svehagen
- *
- * SPDX-License-Identifier: Apache-2.0
- */
- #include <zephyr.h>
- #include <wait_q.h>
- #include <posix/sys/eventfd.h>
- #include <net/socket.h>
- #include <ksched.h>
- struct eventfd {
- struct k_poll_signal read_sig;
- struct k_poll_signal write_sig;
- struct k_spinlock lock;
- _wait_q_t wait_q;
- 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)
- {
- if (pfd->events & ZSOCK_POLLIN) {
- if (*pev == pev_end) {
- errno = ENOMEM;
- return -1;
- }
- (*pev)->obj = &efd->read_sig;
- (*pev)->type = K_POLL_TYPE_SIGNAL;
- (*pev)->mode = K_POLL_MODE_NOTIFY_ONLY;
- (*pev)->state = K_POLL_STATE_NOT_READY;
- (*pev)++;
- }
- if (pfd->events & ZSOCK_POLLOUT) {
- if (*pev == pev_end) {
- errno = ENOMEM;
- return -1;
- }
- (*pev)->obj = &efd->write_sig;
- (*pev)->type = K_POLL_TYPE_SIGNAL;
- (*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_POLLIN) {
- if ((*pev)->state != K_POLL_STATE_NOT_READY) {
- pfd->revents |= ZSOCK_POLLIN;
- }
- (*pev)++;
- }
- if (pfd->events & ZSOCK_POLLOUT) {
- if ((*pev)->state != K_POLL_STATE_NOT_READY) {
- pfd->revents |= ZSOCK_POLLOUT;
- }
- (*pev)++;
- }
- return 0;
- }
- static ssize_t eventfd_read_op(void *obj, void *buf, size_t sz)
- {
- struct eventfd *efd = obj;
- int result = 0;
- eventfd_t count = 0;
- k_spinlock_key_t key;
- if (sz < sizeof(eventfd_t)) {
- errno = EINVAL;
- return -1;
- }
- for (;;) {
- key = k_spin_lock(&efd->lock);
- if ((efd->flags & EFD_NONBLOCK) && efd->cnt == 0) {
- result = EAGAIN;
- break;
- } else if (efd->cnt == 0) {
- z_pend_curr(&efd->lock, key, &efd->wait_q, K_FOREVER);
- } else {
- count = (efd->flags & EFD_SEMAPHORE) ? 1 : efd->cnt;
- efd->cnt -= count;
- if (efd->cnt == 0) {
- k_poll_signal_reset(&efd->read_sig);
- }
- k_poll_signal_raise(&efd->write_sig, 0);
- break;
- }
- }
- if (z_unpend_all(&efd->wait_q) != 0) {
- z_reschedule(&efd->lock, key);
- } else {
- k_spin_unlock(&efd->lock, key);
- }
- if (result != 0) {
- errno = result;
- return -1;
- }
- *(eventfd_t *)buf = count;
- return sizeof(eventfd_t);
- }
- static ssize_t eventfd_write_op(void *obj, const void *buf, size_t sz)
- {
- struct eventfd *efd = obj;
- int result = 0;
- eventfd_t count;
- bool overflow;
- k_spinlock_key_t key;
- if (sz < sizeof(eventfd_t)) {
- errno = EINVAL;
- return -1;
- }
- count = *((eventfd_t *)buf);
- if (count == UINT64_MAX) {
- errno = EINVAL;
- return -1;
- }
- if (count == 0) {
- return sizeof(eventfd_t);
- }
- for (;;) {
- key = k_spin_lock(&efd->lock);
- overflow = UINT64_MAX - count <= efd->cnt;
- if ((efd->flags & EFD_NONBLOCK) && overflow) {
- result = EAGAIN;
- break;
- } else if (overflow) {
- z_pend_curr(&efd->lock, key, &efd->wait_q, K_FOREVER);
- } else {
- efd->cnt += count;
- if (efd->cnt == (UINT64_MAX - 1)) {
- k_poll_signal_reset(&efd->write_sig);
- }
- k_poll_signal_raise(&efd->read_sig, 0);
- break;
- }
- }
- if (z_unpend_all(&efd->wait_q) != 0) {
- z_reschedule(&efd->lock, key);
- } else {
- k_spin_unlock(&efd->lock, key);
- }
- if (result != 0) {
- errno = result;
- return -1;
- }
- return sizeof(eventfd_t);
- }
- static int eventfd_close_op(void *obj)
- {
- struct eventfd *efd = (struct eventfd *)obj;
- efd->flags = 0;
- return 0;
- }
- 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_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,
- .close = eventfd_close_op,
- .ioctl = eventfd_ioctl_op,
- };
- int eventfd(unsigned int initval, int flags)
- {
- struct eventfd *efd = NULL;
- int fd = -1;
- int i;
- if (flags & ~EFD_FLAGS_SET) {
- errno = EINVAL;
- return -1;
- }
- k_mutex_lock(&eventfd_mtx, K_FOREVER);
- for (i = 0; i < ARRAY_SIZE(efds); ++i) {
- if (!(efds[i].flags & EFD_IN_USE)) {
- efd = &efds[i];
- break;
- }
- }
- if (efd == NULL) {
- errno = ENOMEM;
- goto exit_mtx;
- }
- fd = z_reserve_fd();
- if (fd < 0) {
- goto exit_mtx;
- }
- efd->flags = EFD_IN_USE | flags;
- efd->cnt = initval;
- k_poll_signal_init(&efd->write_sig);
- k_poll_signal_init(&efd->read_sig);
- z_waitq_init(&efd->wait_q);
- if (initval != 0) {
- k_poll_signal_raise(&efd->read_sig, 0);
- }
- k_poll_signal_raise(&efd->write_sig, 0);
- z_finalize_fd(fd, efd, &eventfd_fd_vtable);
- exit_mtx:
- k_mutex_unlock(&eventfd_mtx);
- return fd;
- }
|