xiaoxiang781216 commented on a change in pull request #5014: URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772194008
########## File path: fs/vfs/fs_timerfd.c ########## @@ -0,0 +1,824 @@ +/**************************************************************************** + * fs/vfs/fs_timerfd.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <stdio.h> +#include <poll.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> + +#include <debug.h> + +#include <nuttx/wdog.h> +#include <nuttx/irq.h> +#include <nuttx/wqueue.h> + +#include <sys/ioctl.h> +#include <sys/timerfd.h> + +#include "clock/clock.h" +#include "inode/inode.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_TIMER_FD_VFS_PATH +#define CONFIG_TIMER_FD_VFS_PATH "/var/timer" +#endif + +#ifndef CONFIG_TIMER_FD_NPOLLWAITERS +/* Maximum number of threads than can be waiting for POLL events */ +#define CONFIG_TIMER_FD_NPOLLWAITERS 2 +#endif + +#define TIMER_FD_WORK LPWORK + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct timerfd_waiter_sem_s +{ + sem_t sem; + struct timerfd_waiter_sem_s *next; +} timerfd_waiter_sem_t; + +/* This structure describes the internal state of the driver */ + +struct timerfd_priv_s +{ + sem_t exclsem; /* Enforces device exclusive access */ + timerfd_waiter_sem_t *rdsems; /* List of blocking readers */ + int clock; /* Clock to use as the timing base */ + int delay; /* If non-zero, used to reset repetitive + * timers */ + struct wdog_s wdog; /* The watchdog that provides the timing */ + struct work_s work; /* For deferred timeout operations */ + timerfd_t counter; /* timerfd counter */ + unsigned int minor; /* timerfd minor number */ + uint8_t crefs; /* References counts on timerfd (max: 255) */ + + /* The following is a list if poll structures of threads waiting for + * driver events. + */ + +#ifdef CONFIG_TIMER_FD_POLL + FAR struct pollfd *fds[CONFIG_TIMER_FD_NPOLLWAITERS]; +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int timerfd_open(FAR struct file *filep); +static int timerfd_close(FAR struct file *filep); + +static ssize_t timerfd_read(FAR struct file *filep, FAR char *buffer, + size_t len); +#ifdef CONFIG_TIMER_FD_POLL +static int timerfd_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup); + +static void timerfd_pollnotify(FAR struct timerfd_priv_s *dev, + pollevent_t eventset); +#endif + +static int timerfd_blocking_io(FAR struct timerfd_priv_s *dev, + timerfd_waiter_sem_t *sem, + FAR timerfd_waiter_sem_t **slist); + +static unsigned int timerfd_get_unique_minor(void); +static void timerfd_release_minor(unsigned int minor); + +static FAR struct timerfd_priv_s *timerfd_allocdev(void); +static void timerfd_destroy(FAR struct timerfd_priv_s *dev); + +static void timerfd_timeout_work(FAR void *arg); +static void timerfd_timeout(wdparm_t idev); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_timerfd_fops = +{ + timerfd_open, /* open */ + timerfd_close, /* close */ + timerfd_read, /* read */ + NULL, /* write */ + NULL, /* seek */ + NULL /* ioctl */ +#ifdef CONFIG_TIMER_FD_POLL + , timerfd_poll /* poll */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static FAR struct timerfd_priv_s *timerfd_allocdev(void) +{ + FAR struct timerfd_priv_s *dev; + + dev = (FAR struct timerfd_priv_s *) + kmm_zalloc(sizeof(struct timerfd_priv_s)); + if (dev) + { + /* Initialize the private structure */ + + nxsem_init(&dev->exclsem, 0, 0); + } + + return dev; +} + +static void timerfd_destroy(FAR struct timerfd_priv_s *dev) +{ + wd_cancel(&dev->wdog); + work_cancel(TIMER_FD_WORK, &dev->work); + nxsem_destroy(&dev->exclsem); + kmm_free(dev); +} + +static timerfd_t timerfd_get_counter(FAR struct timerfd_priv_s *dev) +{ + timerfd_t counter; + irqstate_t intflags; + + intflags = enter_critical_section(); + counter = dev->counter; + leave_critical_section(intflags); + + return counter; +} + +#ifdef CONFIG_TIMER_FD_POLL +static void timerfd_pollnotify(FAR struct timerfd_priv_s *dev, + pollevent_t eventset) +{ + FAR struct pollfd *fds; + int i; + + for (i = 0; i < CONFIG_TIMER_FD_NPOLLWAITERS; i++) + { + fds = dev->fds[i]; + if (fds) + { + fds->revents |= eventset & fds->events; + + if (fds->revents != 0) + { + nxsem_post(fds->sem); + } + } + } +} +#endif + +static unsigned int timerfd_get_unique_minor(void) +{ + static unsigned int minor; + + return minor++; +} + +static void timerfd_release_minor(unsigned int minor) +{ +} + +static int timerfd_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct timerfd_priv_s *priv = inode->i_private; + int ret; + + /* Get exclusive access to the device structures */ + + ret = nxsem_wait(&priv->exclsem); + if (ret < 0) + { + return ret; + } + + finfo("crefs: %d <%s>\n", priv->crefs, inode->i_name); + + if (priv->crefs >= 255) + { + /* More than 255 opens; uint8_t would overflow to zero */ + + ret = -EMFILE; + } + else + { + /* Save the new open count on success */ + + priv->crefs += 1; + ret = OK; + } + + nxsem_post(&priv->exclsem); + return ret; +} + +static int timerfd_close(FAR struct file *filep) +{ + int ret; + FAR struct inode *inode = filep->f_inode; + FAR struct timerfd_priv_s *priv = inode->i_private; + + /* devpath: TIMER_FD_VFS_PATH + /tfd (4) + %u (10) + null char (1) */ + + char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 10 + 1]; + + /* Get exclusive access to the device structures */ + + ret = nxsem_wait(&priv->exclsem); + if (ret < 0) + { + return ret; + } + + finfo("crefs: %d <%s>\n", priv->crefs, inode->i_name); + + /* Decrement the references to the driver. If the reference count will + * decrement to 0, then uninitialize the driver. + */ + + if (priv->crefs > 1) + { + /* Just decrement the reference count and release the semaphore */ + + priv->crefs--; + nxsem_post(&priv->exclsem); + return OK; + } + + /* Re-create the path to the driver. */ + + finfo("destroy\n"); + sprintf(devpath, CONFIG_TIMER_FD_VFS_PATH "/tfd%u", priv->minor); + + /* Will be unregistered later after close is done */ + + unregister_driver(devpath); + + DEBUGASSERT(priv->exclsem.semcount == 0); + timerfd_release_minor(priv->minor); + timerfd_destroy(priv); + + return OK; +} + +static int timerfd_blocking_io(FAR struct timerfd_priv_s *dev, + timerfd_waiter_sem_t *sem, + FAR timerfd_waiter_sem_t **slist) +{ + int ret; + sem->next = *slist; + *slist = sem; + + nxsem_post(&dev->exclsem); + + /* Wait for timerfd to notify */ + + ret = nxsem_wait(&sem->sem); + + if (ret < 0) + { + /* Interrupted wait, unregister semaphore + * TODO ensure that exclsem wait does not fail (ECANCELED) + */ + + nxsem_wait_uninterruptible(&dev->exclsem); + + timerfd_waiter_sem_t *cur_sem = *slist; + + if (cur_sem == sem) + { + *slist = sem->next; + } + else + { + while (cur_sem) + { + if (cur_sem->next == sem) + { + cur_sem->next = sem->next; + break; + } + } + } + + nxsem_post(&dev->exclsem); + return ret; + } + + return nxsem_wait(&dev->exclsem); +} + +static ssize_t timerfd_read(FAR struct file *filep, FAR char *buffer, + size_t len) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct timerfd_priv_s *dev = inode->i_private; + irqstate_t intflags; + ssize_t ret; + + if (len < sizeof(timerfd_t) || buffer == NULL) + { + return -EINVAL; + } + + ret = nxsem_wait(&dev->exclsem); + if (ret < 0) + { + return ret; + } + + /* Wait for an incoming event */ + + if (timerfd_get_counter(dev) == 0) + { + if (filep->f_oflags & O_NONBLOCK) + { + nxsem_post(&dev->exclsem); + return -EAGAIN; + } + + timerfd_waiter_sem_t sem; + nxsem_init(&sem.sem, 0, 0); + nxsem_set_protocol(&sem.sem, SEM_PRIO_NONE); + + do + { + ret = timerfd_blocking_io(dev, &sem, &dev->rdsems); + if (ret < 0) + { + nxsem_destroy(&sem.sem); + return ret; + } + } + while (timerfd_get_counter(dev) == 0); + + nxsem_destroy(&sem.sem); + } + + /* Device ready for read. Ensure that interrupts are disabled and we + * do not lose counts if expiration occurs after read, but before setting + * counter to zero + */ + + intflags = enter_critical_section(); + + *(FAR timerfd_t *)buffer = dev->counter; + dev->counter = 0; + + leave_critical_section(intflags); + + nxsem_post(&dev->exclsem); + return sizeof(timerfd_t); +} + +#ifdef CONFIG_TIMER_FD_POLL +static int timerfd_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct timerfd_priv_s *dev = inode->i_private; + int ret; + int i; + + ret = nxsem_wait(&dev->exclsem); + if (ret < 0) + { + return ret; + } + + ret = OK; + + if (!setup) + { + /* This is a request to tear down the poll. */ + + FAR struct pollfd **slot = (FAR struct pollfd **)fds->priv; + + /* Remove all memory of the poll setup */ + + *slot = NULL; + fds->priv = NULL; + goto errout; + } + + /* This is a request to set up the poll. Find an available + * slot for the poll structure reference + */ + + for (i = 0; i < CONFIG_TIMER_FD_NPOLLWAITERS; i++) + { + /* Find an available slot */ + + if (!dev->fds[i]) + { + /* Bind the poll structure and this slot */ + + dev->fds[i] = fds; + fds->priv = &dev->fds[i]; + break; + } + } + + if (i >= CONFIG_TIMER_FD_NPOLLWAITERS) + { + fds->priv = NULL; + ret = -EBUSY; + goto errout; + } + + /* Notify the POLLIN event if the counter is not zero */ + + if (timerfd_get_counter(dev) > 0) + { + timerfd_pollnotify(dev, POLLIN); + } + +errout: + nxsem_post(&dev->exclsem); + return ret; +} +#endif + +static void timerfd_timeout_work(FAR void *arg) +{ + FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)arg; + int ret; + + ret = nxsem_wait_uninterruptible(&dev->exclsem); + if (ret < 0) + { + wd_cancel(&dev->wdog); + return; + } + +#ifdef CONFIG_TIMER_FD_POLL + /* Notify all poll/select waiters */ + + timerfd_pollnotify(dev, POLLIN); +#endif + + /* Notify all of the waiting readers */ + + timerfd_waiter_sem_t *cur_sem = dev->rdsems; + while (cur_sem != NULL) + { + nxsem_post(&cur_sem->sem); + cur_sem = cur_sem->next; + } + + dev->rdsems = NULL; Review comment: Yes, you are right! -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
