You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2021/12/17 00:42:23 UTC

[GitHub] [incubator-nuttx] pkarashchenko opened a new pull request #5014: fs/vfs: Add file descriptor based timers support

pkarashchenko opened a new pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014


   WIP
   ## Summary
   Add `timerfd` support
   
   ## Impact
   
   ## Testing
   
   


-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771827304



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,840 @@
+/****************************************************************************
+ * 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 "/dev"
+#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 TIMERFDWORK 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 */

Review comment:
       BTW, you may need add a simple function to get the left time before alarm like wd_gettime.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772147351



##########
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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter */
+
+  dev->counter++;
+
+  work_queue(TIMER_FD_WORK, &dev->work, timerfd_timeout_work, dev, 0);
+
+  /* If this is a repetitive timer, then restart the watchdog */
+
+  if (dev->delay)
+    {
+      wd_start(&dev->wdog, dev->delay, timerfd_timeout, idev);
+    }
+
+  leave_critical_section(intflags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int timerfd_create(int clockid, int flags)
+{
+  FAR struct timerfd_priv_s *new_dev;
+  int new_fd;
+  int ret;
+
+  /* Sanity checks. */
+
+  if (clockid != CLOCK_REALTIME
+#ifdef CONFIG_CLOCK_MONOTONIC
+      && clockid != CLOCK_MONOTONIC
+      && clockid != CLOCK_BOOTTIME
+#endif /* CONFIG_CLOCK_MONOTONIC */
+      )
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* devpath: TIMER_FD_VFS_PATH + /tfd (4) + %u (10) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 10 + 1];
+
+  /* Allocate instance data for this driver */
+
+  new_dev = timerfd_allocdev();
+  if (new_dev == NULL)
+    {
+      /* Failed to allocate new device */
+
+      ret = ENOMEM;
+      goto errout;
+    }
+
+  /* Initialize the timer instance */
+
+  new_dev->clock = clockid;
+  new_dev->crefs = 1;
+  new_dev->delay = 0;
+  new_dev->counter = 0;
+
+  /* Request a unique minor device number */
+
+  new_dev->minor = timerfd_get_unique_minor();
+
+  /* Get device path */
+
+  sprintf(devpath, CONFIG_TIMER_FD_VFS_PATH "/tfd%u", new_dev->minor);
+
+  /* Register the driver */
+
+  ret = register_driver(devpath, &g_timerfd_fops, 0444, new_dev);
+  if (ret < 0)
+    {
+      ferr("Failed to register new device %s: %d\n", devpath, ret);
+      ret = -ret;

Review comment:
       I do not mind, but probably will need to update those as well in next PR




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772154784



##########
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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter */
+
+  dev->counter++;
+
+  work_queue(TIMER_FD_WORK, &dev->work, timerfd_timeout_work, dev, 0);
+
+  /* If this is a repetitive timer, then restart the watchdog */
+
+  if (dev->delay)
+    {
+      wd_start(&dev->wdog, dev->delay, timerfd_timeout, idev);
+    }
+
+  leave_critical_section(intflags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int timerfd_create(int clockid, int flags)
+{
+  FAR struct timerfd_priv_s *new_dev;
+  int new_fd;
+  int ret;
+
+  /* Sanity checks. */
+
+  if (clockid != CLOCK_REALTIME
+#ifdef CONFIG_CLOCK_MONOTONIC
+      && clockid != CLOCK_MONOTONIC
+      && clockid != CLOCK_BOOTTIME
+#endif /* CONFIG_CLOCK_MONOTONIC */
+      )
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* devpath: TIMER_FD_VFS_PATH + /tfd (4) + %u (10) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 10 + 1];
+
+  /* Allocate instance data for this driver */
+
+  new_dev = timerfd_allocdev();
+  if (new_dev == NULL)
+    {
+      /* Failed to allocate new device */
+
+      ret = ENOMEM;
+      goto errout;
+    }
+
+  /* Initialize the timer instance */
+
+  new_dev->clock = clockid;
+  new_dev->crefs = 1;
+  new_dev->delay = 0;
+  new_dev->counter = 0;
+
+  /* Request a unique minor device number */
+
+  new_dev->minor = timerfd_get_unique_minor();
+
+  /* Get device path */
+
+  sprintf(devpath, CONFIG_TIMER_FD_VFS_PATH "/tfd%u", new_dev->minor);
+
+  /* Register the driver */
+
+  ret = register_driver(devpath, &g_timerfd_fops, 0444, new_dev);
+  if (ret < 0)
+    {
+      ferr("Failed to register new device %s: %d\n", devpath, ret);
+      ret = -ret;

Review comment:
       > What do we gain with this?
   
   Could save ret = -ret in several places
   




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772168288



##########
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;

Review comment:
       Done




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771789209



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,840 @@
+/****************************************************************************
+ * 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 "/dev"
+#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 TIMERFDWORK 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 */

Review comment:
       Yes, work_queue use wdog internally, just like what you done here.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772164452



##########
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)

Review comment:
       Seems that there is a big bug in all this `slist` logic here and in `eventfd`. I will try to fix it




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772188328



##########
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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter */
+
+  dev->counter++;
+
+  work_queue(TIMER_FD_WORK, &dev->work, timerfd_timeout_work, dev, 0);
+
+  /* If this is a repetitive timer, then restart the watchdog */
+
+  if (dev->delay)
+    {
+      wd_start(&dev->wdog, dev->delay, timerfd_timeout, idev);
+    }
+
+  leave_critical_section(intflags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int timerfd_create(int clockid, int flags)
+{
+  FAR struct timerfd_priv_s *new_dev;
+  int new_fd;
+  int ret;
+
+  /* Sanity checks. */
+
+  if (clockid != CLOCK_REALTIME
+#ifdef CONFIG_CLOCK_MONOTONIC
+      && clockid != CLOCK_MONOTONIC
+      && clockid != CLOCK_BOOTTIME
+#endif /* CONFIG_CLOCK_MONOTONIC */
+      )
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* devpath: TIMER_FD_VFS_PATH + /tfd (4) + %u (10) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 10 + 1];
+
+  /* Allocate instance data for this driver */
+
+  new_dev = timerfd_allocdev();
+  if (new_dev == NULL)
+    {
+      /* Failed to allocate new device */
+
+      ret = ENOMEM;
+      goto errout;
+    }
+
+  /* Initialize the timer instance */
+
+  new_dev->clock = clockid;
+  new_dev->crefs = 1;
+  new_dev->delay = 0;
+  new_dev->counter = 0;
+
+  /* Request a unique minor device number */
+
+  new_dev->minor = timerfd_get_unique_minor();
+
+  /* Get device path */
+
+  sprintf(devpath, CONFIG_TIMER_FD_VFS_PATH "/tfd%u", new_dev->minor);
+
+  /* Register the driver */
+
+  ret = register_driver(devpath, &g_timerfd_fops, 0444, new_dev);
+  if (ret < 0)
+    {
+      ferr("Failed to register new device %s: %d\n", devpath, ret);
+      ret = -ret;
+      goto errout_release_minor;
+    }
+
+  /* Device is ready for use */
+
+  nxsem_post(&new_dev->exclsem);
+
+  /* Try open new device */
+
+  new_fd = nx_open(devpath, O_RDONLY |
+                   (flags & (TFD_NONBLOCK | TFD_CLOEXEC)));
+
+  if (new_fd < 0)
+    {
+      ret = -new_fd;

Review comment:
       Done

##########
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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter */
+
+  dev->counter++;
+
+  work_queue(TIMER_FD_WORK, &dev->work, timerfd_timeout_work, dev, 0);
+
+  /* If this is a repetitive timer, then restart the watchdog */
+
+  if (dev->delay)
+    {
+      wd_start(&dev->wdog, dev->delay, timerfd_timeout, idev);
+    }
+
+  leave_critical_section(intflags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int timerfd_create(int clockid, int flags)
+{
+  FAR struct timerfd_priv_s *new_dev;
+  int new_fd;
+  int ret;
+
+  /* Sanity checks. */
+
+  if (clockid != CLOCK_REALTIME
+#ifdef CONFIG_CLOCK_MONOTONIC
+      && clockid != CLOCK_MONOTONIC
+      && clockid != CLOCK_BOOTTIME
+#endif /* CONFIG_CLOCK_MONOTONIC */
+      )
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* devpath: TIMER_FD_VFS_PATH + /tfd (4) + %u (10) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 10 + 1];
+
+  /* Allocate instance data for this driver */
+
+  new_dev = timerfd_allocdev();
+  if (new_dev == NULL)
+    {
+      /* Failed to allocate new device */
+
+      ret = ENOMEM;
+      goto errout;
+    }
+
+  /* Initialize the timer instance */
+
+  new_dev->clock = clockid;
+  new_dev->crefs = 1;
+  new_dev->delay = 0;
+  new_dev->counter = 0;
+
+  /* Request a unique minor device number */
+
+  new_dev->minor = timerfd_get_unique_minor();
+
+  /* Get device path */
+
+  sprintf(devpath, CONFIG_TIMER_FD_VFS_PATH "/tfd%u", new_dev->minor);
+
+  /* Register the driver */
+
+  ret = register_driver(devpath, &g_timerfd_fops, 0444, new_dev);
+  if (ret < 0)
+    {
+      ferr("Failed to register new device %s: %d\n", devpath, ret);
+      ret = -ret;

Review comment:
       Done




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771982974



##########
File path: include/sys/syscall_lookup.h
##########
@@ -218,6 +218,11 @@ SYSCALL_LOOKUP(pwrite,                     4)
 #ifdef CONFIG_EVENT_FD
   SYSCALL_LOOKUP(eventfd,                  2)
 #endif
+#ifdef CONFIG_TIMER_FD
+  SYSCALL_LOOKUP(timerfd_create,           2)
+  SYSCALL_LOOKUP(timerfd_settime,          4)
+  SYSCALL_LOOKUP(timerfd_gettime,          2)

Review comment:
       > I see no problems with `timerfd_gettime` in such approach, but not sure about `timerfd_settime` that needs 3 arguments to be passed. In general `int flags, const struct itimerspec *new_value, struct itimerspec *old_value` should be packed into intermediate structure and pointer to that structure has to be passed to `ioctl` call. I also do not have any issues with that, but the question comes where the declaration of that intermediate type should be located? Should I call something like
   > 
   > ```
   > typedef struct
   > {
   >   int flags;
   >   const struct itimerspec *new_value;
   >   struct itimerspec *old_value;
   > } timerfd_settime_t;
   > ```
   > 
   > into `timerfd.h`? @xiaoxiang781216 what is your opinion?
   
   Yes, it's reasonable. BTW, it's better to use struct directly and change pointer to value, like this:
   ```
    struct timerfd_settime_s
    {
      int flags;
      struct itimerspec new_value;
      struct itimerspec old_value;
    };
   ```
   
   > Also `eventfd_read` and `eventfd_write` are `glibc` functions by definition. While `timerfd` APIs are a syscalls in Linux
   
   Once we move the action into `timerfd_ioctl`, `timerfd_settime` and `timerfd_gettime` can be changed to 'libc' function like `eventfd_read` and `eventfd_write`. Only, timerfd_create need be syscall.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771824136



##########
File path: include/sys/syscall_lookup.h
##########
@@ -218,6 +218,11 @@ SYSCALL_LOOKUP(pwrite,                     4)
 #ifdef CONFIG_EVENT_FD
   SYSCALL_LOOKUP(eventfd,                  2)
 #endif
+#ifdef CONFIG_TIMER_FD
+  SYSCALL_LOOKUP(timerfd_create,           2)
+  SYSCALL_LOOKUP(timerfd_settime,          4)
+  SYSCALL_LOOKUP(timerfd_gettime,          2)

Review comment:
       The idea is simple:
   
   1. Add ioctl(e.g. TIMERFD_SETTIME, TIMER_GETTIME)
   2. Move the code from timerfd_settime/timerfd_getime) to timerfd_ioctl
   3. Implement timerfd_settime and timerfd_gettime in libc as the simple ioctl wrapper.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772138991



##########
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:
       This is copied from `eventfd_do_write`. Should we remove same line there as well?




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771996234



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,845 @@
+/****************************************************************************
+ * 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
+
+#ifdef __INT64_DEFINED
+#define TIMER_FD_COUNTER_MAX UINT64_MAX
+#else
+#define TIMER_FD_COUNTER_MAX UINT32_MAX
+#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);
+static int timerfd_ioctl(FAR struct file *filep, int cmd,
+                         unsigned long arg);
+#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 */
+  timerfd_ioctl  /* 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);
+}
+
+static int timerfd_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+#if 0
+  FAR struct inode *inode = filep->f_inode;
+  FAR struct timerfd_priv_s *priv = inode->i_private;
+#endif
+  return -ENOSYS;
+}
+
+#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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter.  Do not allow rollover */
+
+  if (dev->counter < TIMER_FD_COUNTER_MAX)
+    {
+      dev->counter++;

Review comment:
       I do not want to add dependency to `atomic` for `timerfd` and `up_fetchadd32` is platform specific and dealing with 64 bit incrementing may be a bit tricky.
   Do you see a big problem in using of a critical section?
   
   What I see even more, is changing `enter_critical_section`/`leave_critical_section` to `spin_lock_irqsave`/`spin_unlock_irqrestore` to ensure option when `wdog` is running at Core A while non-blocking read happens on Core B. @xiaoxiang781216 what do you think?




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771995596



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,840 @@
+/****************************************************************************
+ * 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 "/dev"
+#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 TIMERFDWORK 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 */

Review comment:
       `wdog` + `work_queue` is widely used in NuttX code when deferred processing that involve synchronization primitives is needed. I explored `timer_settime` case before implementing `timerfd` exactly in this way as it is now. I really do not think that calling `nxsem_wait_uninterruptible(&dev->exclsem);` from `wdog` expiration callback is a good idea and obtaining `dev->exclsem` is mandatory to ensure integrity of `struct timerfd_priv_s` members (except `counter` of course that is guarded by the critical section). So I see `wdog` + `work_queue` as an only possible solution to implement `timerfd` in a reliable way from both timing and date integrity perspective.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771789130



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,840 @@
+/****************************************************************************
+ * 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 "/dev"

Review comment:
       I will fix eventfd.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771937530



##########
File path: include/sys/syscall_lookup.h
##########
@@ -218,6 +218,11 @@ SYSCALL_LOOKUP(pwrite,                     4)
 #ifdef CONFIG_EVENT_FD
   SYSCALL_LOOKUP(eventfd,                  2)
 #endif
+#ifdef CONFIG_TIMER_FD
+  SYSCALL_LOOKUP(timerfd_create,           2)
+  SYSCALL_LOOKUP(timerfd_settime,          4)
+  SYSCALL_LOOKUP(timerfd_gettime,          2)

Review comment:
       I see no problems with `timerfd_gettime` in such approach, but not sure about `timerfd_settime` that needs 3 arguments to be passed. In general `int flags, const struct itimerspec *new_value, struct itimerspec *old_value` should be packed into intermediate structure and pointer to that structure has to be passed to `ioctl` call. I also do not have any issues with that, but the question comes where the declaration of that intermediate type should be located? Should I call something like
   ```
   typedef struct
   {
     int flags;
     const struct itimerspec *new_value;
     struct itimerspec *old_value;
   } timerfd_settime_t
   ```
   into `timerfd.h`? @xiaoxiang781216 what is your opinion?




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772173576



##########
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:
       Reviewed the logic. Since all the waiters are notified we can clear `dev->rdsems = NULL;` here and omit `slist` removal logic from the `timerfd_read`. This simplifies code a bit.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772156255



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,845 @@
+/****************************************************************************
+ * 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
+
+#ifdef __INT64_DEFINED
+#define TIMER_FD_COUNTER_MAX UINT64_MAX
+#else
+#define TIMER_FD_COUNTER_MAX UINT32_MAX
+#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);
+static int timerfd_ioctl(FAR struct file *filep, int cmd,
+                         unsigned long arg);
+#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 */
+  timerfd_ioctl  /* 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);
+}
+
+static int timerfd_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+#if 0
+  FAR struct inode *inode = filep->f_inode;
+  FAR struct timerfd_priv_s *priv = inode->i_private;
+#endif
+  return -ENOSYS;
+}
+
+#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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter.  Do not allow rollover */
+
+  if (dev->counter < TIMER_FD_COUNTER_MAX)
+    {
+      dev->counter++;

Review comment:
       Done




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771984873



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,840 @@
+/****************************************************************************
+ * 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 "/dev"
+#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 TIMERFDWORK 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 */

Review comment:
       Yes, it's more accurate to increase the count in the timer interrupt, but most user who call timerfd normally do the select/poll/epoll loop, so the difference is minor. BTW, if you want to achieve more accuracy, you can do all in interrupt, just like what timer done:
   https://github.com/apache/incubator-nuttx/blob/master/sched/timer/timer_settime.c#L124-L146
   But, it require you do all sync with the critical section.

##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,845 @@
+/****************************************************************************
+ * 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
+
+#ifdef __INT64_DEFINED
+#define TIMER_FD_COUNTER_MAX UINT64_MAX
+#else
+#define TIMER_FD_COUNTER_MAX UINT32_MAX
+#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);
+static int timerfd_ioctl(FAR struct file *filep, int cmd,
+                         unsigned long arg);
+#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 */
+  timerfd_ioctl  /* 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);
+}
+
+static int timerfd_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+#if 0
+  FAR struct inode *inode = filep->f_inode;
+  FAR struct timerfd_priv_s *priv = inode->i_private;
+#endif
+  return -ENOSYS;
+}
+
+#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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter.  Do not allow rollover */
+
+  if (dev->counter < TIMER_FD_COUNTER_MAX)
+    {
+      dev->counter++;

Review comment:
       atomic_fetch_add or up_fetchadd32 can avoid the critical secton.

##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,845 @@
+/****************************************************************************
+ * 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
+
+#ifdef __INT64_DEFINED
+#define TIMER_FD_COUNTER_MAX UINT64_MAX
+#else
+#define TIMER_FD_COUNTER_MAX UINT32_MAX
+#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);
+static int timerfd_ioctl(FAR struct file *filep, int cmd,
+                         unsigned long arg);
+#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 */
+  timerfd_ioctl  /* 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);
+}
+
+static int timerfd_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+#if 0
+  FAR struct inode *inode = filep->f_inode;
+  FAR struct timerfd_priv_s *priv = inode->i_private;
+#endif
+  return -ENOSYS;
+}
+
+#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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter.  Do not allow rollover */
+
+  if (dev->counter < TIMER_FD_COUNTER_MAX)

Review comment:
       I think we can ignore it safely, it's very hard to overflow 32/64bit count.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771642862



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,840 @@
+/****************************************************************************
+ * 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 "/dev"
+#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 TIMERFDWORK 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 */

Review comment:
       I will experiment with that. I just wanted to use high priority callback to increment timer counter and scheduled work as a holder for notifications.
   @xiaoxiang781216 does `work_queue` provide the same timing precision as `wdog`?




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771982974



##########
File path: include/sys/syscall_lookup.h
##########
@@ -218,6 +218,11 @@ SYSCALL_LOOKUP(pwrite,                     4)
 #ifdef CONFIG_EVENT_FD
   SYSCALL_LOOKUP(eventfd,                  2)
 #endif
+#ifdef CONFIG_TIMER_FD
+  SYSCALL_LOOKUP(timerfd_create,           2)
+  SYSCALL_LOOKUP(timerfd_settime,          4)
+  SYSCALL_LOOKUP(timerfd_gettime,          2)

Review comment:
       > I see no problems with `timerfd_gettime` in such approach, but not sure about `timerfd_settime` that needs 3 arguments to be passed. In general `int flags, const struct itimerspec *new_value, struct itimerspec *old_value` should be packed into intermediate structure and pointer to that structure has to be passed to `ioctl` call. I also do not have any issues with that, but the question comes where the declaration of that intermediate type should be located? Should I call something like
   > 
   > ```
   > typedef struct
   > {
   >   int flags;
   >   const struct itimerspec *new_value;
   >   struct itimerspec *old_value;
   > } timerfd_settime_t;
   > ```
   > 
   > into `timerfd.h`? @xiaoxiang781216 what is your opinion?
   
   Yes, it's reasonable. BTW, it's better to use struct directly and change pointer to value, like this:
   ```
    struct timerfd_settime_s
    {
      int flags;
      struct itimerspec new_value;
      struct itimerspec old_value;
    };
   ```
   
   > Also `eventfd_read` and `eventfd_write` are `glibc` functions by definition. While `timerfd` APIs are a syscalls in Linux
   
   Once we move the action into `timerfd_ioctl`, `timerfd_settime` and `timerfd_gettime` can be changed to `libc` function just like `eventfd_read` and `eventfd_write`. Only, `timerfd_create` still keep as the syscall.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772076761



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,845 @@
+/****************************************************************************
+ * 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
+
+#ifdef __INT64_DEFINED
+#define TIMER_FD_COUNTER_MAX UINT64_MAX
+#else
+#define TIMER_FD_COUNTER_MAX UINT32_MAX
+#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);
+static int timerfd_ioctl(FAR struct file *filep, int cmd,
+                         unsigned long arg);
+#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 */
+  timerfd_ioctl  /* 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);
+}
+
+static int timerfd_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+#if 0
+  FAR struct inode *inode = filep->f_inode;
+  FAR struct timerfd_priv_s *priv = inode->i_private;
+#endif
+  return -ENOSYS;
+}
+
+#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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter.  Do not allow rollover */
+
+  if (dev->counter < TIMER_FD_COUNTER_MAX)
+    {
+      dev->counter++;

Review comment:
       > I do not want to add dependency to `atomic` for `timerfd` and `up_fetchadd32` is platform specific and dealing with 64 bit incrementing may be a bit tricky. Do you see a big problem in using of a critical section?
   > 
   
   No, just a suggestion for your consideration.
   
   > What I see even more, is changing `enter_critical_section`/`leave_critical_section` to `spin_lock_irqsave`/`spin_unlock_irqrestore` to ensure option when `wdog` is running at Core A while non-blocking read happens on Core B. @xiaoxiang781216 what do you think?
   > 
   
   Yes, if there is no potential wait in side the critical section, spinlock is better than critical section. Here is a issue related to this: https://github.com/apache/incubator-nuttx/issues/1144 and https://github.com/apache/incubator-nuttx/issues/2213.
   
   > BTW atomic_fetch_add also use `spin_lock_irqsave`/`spin_unlock_irqrestore`
   
   It's depends on the architecture(e.g. arm may use LDREX and STREX).




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772072682



##########
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);

Review comment:
       don't need work is always oneshot

##########
File path: include/sys/syscall_lookup.h
##########
@@ -218,6 +218,11 @@ SYSCALL_LOOKUP(pwrite,                     4)
 #ifdef CONFIG_EVENT_FD
   SYSCALL_LOOKUP(eventfd,                  2)
 #endif
+#ifdef CONFIG_TIMER_FD
+  SYSCALL_LOOKUP(timerfd_create,           2)
+  SYSCALL_LOOKUP(timerfd_settime,          4)
+  SYSCALL_LOOKUP(timerfd_gettime,          2)

Review comment:
       Sure, let's do in the next step.
   
   > BTW going with
   > 
   > ```
   > struct timerfd_settime_s {
   >  int flags;
   >  struct itimerspec new_value;
   >  struct itimerspec old_value;
   > };
   > ```
   > 
   > is not the best as `old_value` is optional parameter that can be `NULL`.
   
   timerfd_ioctl could return old_value always, but timerfd_settime can skip the assignment in case of old_value is NULL.

##########
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;

Review comment:
       errout to out?

##########
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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter */
+
+  dev->counter++;
+
+  work_queue(TIMER_FD_WORK, &dev->work, timerfd_timeout_work, dev, 0);
+
+  /* If this is a repetitive timer, then restart the watchdog */
+
+  if (dev->delay)
+    {
+      wd_start(&dev->wdog, dev->delay, timerfd_timeout, idev);
+    }
+
+  leave_critical_section(intflags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int timerfd_create(int clockid, int flags)
+{
+  FAR struct timerfd_priv_s *new_dev;
+  int new_fd;
+  int ret;
+
+  /* Sanity checks. */
+
+  if (clockid != CLOCK_REALTIME
+#ifdef CONFIG_CLOCK_MONOTONIC
+      && clockid != CLOCK_MONOTONIC
+      && clockid != CLOCK_BOOTTIME
+#endif /* CONFIG_CLOCK_MONOTONIC */
+      )
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* devpath: TIMER_FD_VFS_PATH + /tfd (4) + %u (10) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 10 + 1];
+
+  /* Allocate instance data for this driver */
+
+  new_dev = timerfd_allocdev();
+  if (new_dev == NULL)
+    {
+      /* Failed to allocate new device */
+
+      ret = ENOMEM;
+      goto errout;
+    }
+
+  /* Initialize the timer instance */
+
+  new_dev->clock = clockid;
+  new_dev->crefs = 1;
+  new_dev->delay = 0;
+  new_dev->counter = 0;
+
+  /* Request a unique minor device number */
+
+  new_dev->minor = timerfd_get_unique_minor();
+
+  /* Get device path */
+
+  sprintf(devpath, CONFIG_TIMER_FD_VFS_PATH "/tfd%u", new_dev->minor);
+
+  /* Register the driver */
+
+  ret = register_driver(devpath, &g_timerfd_fops, 0444, new_dev);
+  if (ret < 0)
+    {
+      ferr("Failed to register new device %s: %d\n", devpath, ret);
+      ret = -ret;
+      goto errout_release_minor;
+    }
+
+  /* Device is ready for use */
+
+  nxsem_post(&new_dev->exclsem);
+
+  /* Try open new device */
+
+  new_fd = nx_open(devpath, O_RDONLY |
+                   (flags & (TFD_NONBLOCK | TFD_CLOEXEC)));
+
+  if (new_fd < 0)
+    {
+      ret = -new_fd;
+      goto errout_unregister_driver;
+    }
+
+  return new_fd;
+
+errout_unregister_driver:
+  unregister_driver(devpath);
+errout_release_minor:
+  timerfd_release_minor(new_dev->minor);
+  timerfd_destroy(new_dev);
+errout:
+  set_errno(ret);
+  return ERROR;
+}
+
+int timerfd_settime(int fd, int flags,
+                    FAR const struct itimerspec *new_value,
+                    FAR struct itimerspec *old_value)
+{
+  FAR struct file *filep;
+  FAR struct timerfd_priv_s *dev;
+  irqstate_t intflags;
+  sclock_t delay;
+  int ret;
+
+  /* Some sanity checks */
+
+  if (!new_value)
+    {
+      ret = EFAULT;
+      goto errout;
+    }
+
+  if (flags && (flags & TFD_TIMER_ABSTIME) == 0)
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* Get file pointer by file descriptor */
+
+  ret = fs_getfilep(fd, &filep);
+  if (ret < 0)
+    {
+      ret = -ret;
+      goto errout;
+    }
+
+  /* Check fd come from us */
+
+  if (!filep->f_inode || filep->f_inode->u.i_ops != &g_timerfd_fops)
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  dev = (FAR struct timerfd_priv_s *)filep->f_inode->i_private;
+
+  if (old_value)
+    {
+      /* Get the number of ticks before the underlying watchdog expires */
+
+      delay = wd_gettime(&dev->wdog);
+
+      /* Convert that to a struct timespec and return it */
+
+      clock_ticks2time(delay, &old_value->it_value);
+      clock_ticks2time(dev->delay, &old_value->it_interval);
+    }
+
+  /* Disable interrupts here to ensure that expiration counter is accessed
+   * atomicaly and timeout work is canceled with the same sequence
+   */
+
+  intflags = enter_critical_section();
+
+  /* Disarm the timer (in case the timer was already armed when
+   * timerfd_settime() is called).
+   */
+
+  wd_cancel(&dev->wdog);
+
+  /* Cancel notification work */
+
+  work_cancel(TIMER_FD_WORK, &dev->work);
+
+  /* Clear expiration counter */
+
+  dev->counter = 0;
+
+  /* If the it_value member of value is zero, the timer will not be
+   * re-armed
+   */
+
+  if (new_value->it_value.tv_sec <= 0 && new_value->it_value.tv_nsec <= 0)
+    {
+      leave_critical_section(intflags);
+      return OK;
+    }
+
+  /* Setup up any repetitive timer */
+
+  if (new_value->it_interval.tv_sec > 0 ||
+      new_value->it_interval.tv_nsec > 0)
+    {
+      clock_time2ticks(&new_value->it_interval, &delay);
+
+      /* REVISIT: Should delay be sclock_t? */
+
+      dev->delay = (int)delay;
+    }
+  else
+    {
+      dev->delay = 0;
+    }
+
+  /* We need to disable timer interrupts through the following section so
+   * that the system timer is stable.
+   */
+
+  /* Check if abstime is selected */
+
+  if ((flags & TFD_TIMER_ABSTIME) != 0)
+    {
+      /* Calculate a delay corresponding to the absolute time in 'value' */
+
+      clock_abstime2ticks(dev->clock, &new_value->it_value, &delay);
+    }
+  else
+    {
+      /* Calculate a delay assuming that 'value' holds the relative time
+       * to wait.  We have internal knowledge that clock_time2ticks always
+       * returns success.
+       */
+
+      clock_time2ticks(&new_value->it_value, &delay);
+    }
+
+  /* If the time is in the past or now, then set up the next interval
+   * instead (assuming a repetitive timer).
+   */
+
+  if (delay <= 0)
+    {
+      delay = dev->delay;
+    }
+
+  /* Then start the watchdog */
+
+  if (delay > 0)
+    {
+      ret = wd_start(&dev->wdog, delay, timerfd_timeout, (wdparm_t)dev);
+      if (ret < 0)
+        {
+          ret = -ret;

Review comment:
       remove

##########
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:
       remove, let's timerfd_read restore the list

##########
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)

Review comment:
       need remove sem from slist in case of success too.

##########
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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter */
+
+  dev->counter++;
+
+  work_queue(TIMER_FD_WORK, &dev->work, timerfd_timeout_work, dev, 0);
+
+  /* If this is a repetitive timer, then restart the watchdog */
+
+  if (dev->delay)
+    {
+      wd_start(&dev->wdog, dev->delay, timerfd_timeout, idev);
+    }
+
+  leave_critical_section(intflags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int timerfd_create(int clockid, int flags)
+{
+  FAR struct timerfd_priv_s *new_dev;
+  int new_fd;
+  int ret;
+
+  /* Sanity checks. */
+
+  if (clockid != CLOCK_REALTIME
+#ifdef CONFIG_CLOCK_MONOTONIC
+      && clockid != CLOCK_MONOTONIC
+      && clockid != CLOCK_BOOTTIME
+#endif /* CONFIG_CLOCK_MONOTONIC */
+      )
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* devpath: TIMER_FD_VFS_PATH + /tfd (4) + %u (10) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 10 + 1];
+
+  /* Allocate instance data for this driver */
+
+  new_dev = timerfd_allocdev();
+  if (new_dev == NULL)
+    {
+      /* Failed to allocate new device */
+
+      ret = ENOMEM;
+      goto errout;
+    }
+
+  /* Initialize the timer instance */
+
+  new_dev->clock = clockid;
+  new_dev->crefs = 1;
+  new_dev->delay = 0;
+  new_dev->counter = 0;
+
+  /* Request a unique minor device number */
+
+  new_dev->minor = timerfd_get_unique_minor();
+
+  /* Get device path */
+
+  sprintf(devpath, CONFIG_TIMER_FD_VFS_PATH "/tfd%u", new_dev->minor);
+
+  /* Register the driver */
+
+  ret = register_driver(devpath, &g_timerfd_fops, 0444, new_dev);
+  if (ret < 0)
+    {
+      ferr("Failed to register new device %s: %d\n", devpath, ret);
+      ret = -ret;
+      goto errout_release_minor;
+    }
+
+  /* Device is ready for use */
+
+  nxsem_post(&new_dev->exclsem);
+
+  /* Try open new device */
+
+  new_fd = nx_open(devpath, O_RDONLY |
+                   (flags & (TFD_NONBLOCK | TFD_CLOEXEC)));
+
+  if (new_fd < 0)
+    {
+      ret = -new_fd;

Review comment:
       remove -

##########
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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter */
+
+  dev->counter++;
+
+  work_queue(TIMER_FD_WORK, &dev->work, timerfd_timeout_work, dev, 0);
+
+  /* If this is a repetitive timer, then restart the watchdog */
+
+  if (dev->delay)
+    {
+      wd_start(&dev->wdog, dev->delay, timerfd_timeout, idev);
+    }
+
+  leave_critical_section(intflags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int timerfd_create(int clockid, int flags)
+{
+  FAR struct timerfd_priv_s *new_dev;
+  int new_fd;
+  int ret;
+
+  /* Sanity checks. */
+
+  if (clockid != CLOCK_REALTIME
+#ifdef CONFIG_CLOCK_MONOTONIC
+      && clockid != CLOCK_MONOTONIC
+      && clockid != CLOCK_BOOTTIME
+#endif /* CONFIG_CLOCK_MONOTONIC */
+      )
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* devpath: TIMER_FD_VFS_PATH + /tfd (4) + %u (10) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 10 + 1];
+
+  /* Allocate instance data for this driver */
+
+  new_dev = timerfd_allocdev();
+  if (new_dev == NULL)
+    {
+      /* Failed to allocate new device */
+
+      ret = ENOMEM;
+      goto errout;
+    }
+
+  /* Initialize the timer instance */
+
+  new_dev->clock = clockid;
+  new_dev->crefs = 1;
+  new_dev->delay = 0;
+  new_dev->counter = 0;
+
+  /* Request a unique minor device number */
+
+  new_dev->minor = timerfd_get_unique_minor();
+
+  /* Get device path */
+
+  sprintf(devpath, CONFIG_TIMER_FD_VFS_PATH "/tfd%u", new_dev->minor);
+
+  /* Register the driver */
+
+  ret = register_driver(devpath, &g_timerfd_fops, 0444, new_dev);
+  if (ret < 0)
+    {
+      ferr("Failed to register new device %s: %d\n", devpath, ret);
+      ret = -ret;
+      goto errout_release_minor;
+    }
+
+  /* Device is ready for use */
+
+  nxsem_post(&new_dev->exclsem);
+
+  /* Try open new device */
+
+  new_fd = nx_open(devpath, O_RDONLY |
+                   (flags & (TFD_NONBLOCK | TFD_CLOEXEC)));
+
+  if (new_fd < 0)
+    {
+      ret = -new_fd;
+      goto errout_unregister_driver;
+    }
+
+  return new_fd;
+
+errout_unregister_driver:
+  unregister_driver(devpath);
+errout_release_minor:
+  timerfd_release_minor(new_dev->minor);
+  timerfd_destroy(new_dev);
+errout:
+  set_errno(ret);
+  return ERROR;
+}
+
+int timerfd_settime(int fd, int flags,
+                    FAR const struct itimerspec *new_value,
+                    FAR struct itimerspec *old_value)
+{
+  FAR struct file *filep;
+  FAR struct timerfd_priv_s *dev;
+  irqstate_t intflags;
+  sclock_t delay;
+  int ret;
+
+  /* Some sanity checks */
+
+  if (!new_value)
+    {
+      ret = EFAULT;
+      goto errout;
+    }
+
+  if (flags && (flags & TFD_TIMER_ABSTIME) == 0)
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* Get file pointer by file descriptor */
+
+  ret = fs_getfilep(fd, &filep);
+  if (ret < 0)
+    {
+      ret = -ret;
+      goto errout;
+    }
+
+  /* Check fd come from us */
+
+  if (!filep->f_inode || filep->f_inode->u.i_ops != &g_timerfd_fops)
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  dev = (FAR struct timerfd_priv_s *)filep->f_inode->i_private;
+
+  if (old_value)
+    {
+      /* Get the number of ticks before the underlying watchdog expires */
+
+      delay = wd_gettime(&dev->wdog);
+
+      /* Convert that to a struct timespec and return it */
+
+      clock_ticks2time(delay, &old_value->it_value);
+      clock_ticks2time(dev->delay, &old_value->it_interval);
+    }
+
+  /* Disable interrupts here to ensure that expiration counter is accessed
+   * atomicaly and timeout work is canceled with the same sequence
+   */
+
+  intflags = enter_critical_section();
+
+  /* Disarm the timer (in case the timer was already armed when
+   * timerfd_settime() is called).
+   */
+
+  wd_cancel(&dev->wdog);
+
+  /* Cancel notification work */
+
+  work_cancel(TIMER_FD_WORK, &dev->work);
+
+  /* Clear expiration counter */
+
+  dev->counter = 0;
+
+  /* If the it_value member of value is zero, the timer will not be
+   * re-armed
+   */
+
+  if (new_value->it_value.tv_sec <= 0 && new_value->it_value.tv_nsec <= 0)
+    {
+      leave_critical_section(intflags);
+      return OK;
+    }
+
+  /* Setup up any repetitive timer */
+
+  if (new_value->it_interval.tv_sec > 0 ||
+      new_value->it_interval.tv_nsec > 0)
+    {
+      clock_time2ticks(&new_value->it_interval, &delay);
+
+      /* REVISIT: Should delay be sclock_t? */
+
+      dev->delay = (int)delay;
+    }
+  else
+    {
+      dev->delay = 0;
+    }
+
+  /* We need to disable timer interrupts through the following section so
+   * that the system timer is stable.
+   */
+
+  /* Check if abstime is selected */
+
+  if ((flags & TFD_TIMER_ABSTIME) != 0)
+    {
+      /* Calculate a delay corresponding to the absolute time in 'value' */
+
+      clock_abstime2ticks(dev->clock, &new_value->it_value, &delay);
+    }
+  else
+    {
+      /* Calculate a delay assuming that 'value' holds the relative time
+       * to wait.  We have internal knowledge that clock_time2ticks always
+       * returns success.
+       */
+
+      clock_time2ticks(&new_value->it_value, &delay);
+    }
+
+  /* If the time is in the past or now, then set up the next interval
+   * instead (assuming a repetitive timer).
+   */
+
+  if (delay <= 0)
+    {
+      delay = dev->delay;
+    }
+
+  /* Then start the watchdog */
+
+  if (delay > 0)
+    {
+      ret = wd_start(&dev->wdog, delay, timerfd_timeout, (wdparm_t)dev);
+      if (ret < 0)
+        {
+          ret = -ret;
+          leave_critical_section(intflags);
+          goto errout;
+        }
+    }
+
+  leave_critical_section(intflags);
+  return OK;
+
+errout:
+  set_errno(ret);
+  return ERROR;
+}
+
+int timerfd_gettime(int fd, FAR struct itimerspec *curr_value)
+{
+  FAR struct file *filep;
+  FAR struct timerfd_priv_s *dev;
+  sclock_t ticks;
+  int ret;
+
+  /* Some sanity checks */
+
+  if (!curr_value)
+    {
+      ret = EFAULT;
+      goto errout;
+    }
+
+  /* Get file pointer by file descriptor */
+
+  ret = fs_getfilep(fd, &filep);
+  if (ret < 0)
+    {
+      ret = -ret;

Review comment:
       same comment

##########
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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter */
+
+  dev->counter++;
+
+  work_queue(TIMER_FD_WORK, &dev->work, timerfd_timeout_work, dev, 0);
+
+  /* If this is a repetitive timer, then restart the watchdog */
+
+  if (dev->delay)
+    {
+      wd_start(&dev->wdog, dev->delay, timerfd_timeout, idev);
+    }
+
+  leave_critical_section(intflags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int timerfd_create(int clockid, int flags)
+{
+  FAR struct timerfd_priv_s *new_dev;
+  int new_fd;
+  int ret;
+
+  /* Sanity checks. */
+
+  if (clockid != CLOCK_REALTIME
+#ifdef CONFIG_CLOCK_MONOTONIC
+      && clockid != CLOCK_MONOTONIC
+      && clockid != CLOCK_BOOTTIME
+#endif /* CONFIG_CLOCK_MONOTONIC */
+      )
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* devpath: TIMER_FD_VFS_PATH + /tfd (4) + %u (10) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 10 + 1];
+
+  /* Allocate instance data for this driver */
+
+  new_dev = timerfd_allocdev();
+  if (new_dev == NULL)
+    {
+      /* Failed to allocate new device */
+
+      ret = ENOMEM;
+      goto errout;
+    }
+
+  /* Initialize the timer instance */
+
+  new_dev->clock = clockid;
+  new_dev->crefs = 1;
+  new_dev->delay = 0;
+  new_dev->counter = 0;
+
+  /* Request a unique minor device number */
+
+  new_dev->minor = timerfd_get_unique_minor();
+
+  /* Get device path */
+
+  sprintf(devpath, CONFIG_TIMER_FD_VFS_PATH "/tfd%u", new_dev->minor);
+
+  /* Register the driver */
+
+  ret = register_driver(devpath, &g_timerfd_fops, 0444, new_dev);
+  if (ret < 0)
+    {
+      ferr("Failed to register new device %s: %d\n", devpath, ret);
+      ret = -ret;
+      goto errout_release_minor;
+    }
+
+  /* Device is ready for use */
+
+  nxsem_post(&new_dev->exclsem);
+
+  /* Try open new device */
+
+  new_fd = nx_open(devpath, O_RDONLY |
+                   (flags & (TFD_NONBLOCK | TFD_CLOEXEC)));
+
+  if (new_fd < 0)
+    {
+      ret = -new_fd;
+      goto errout_unregister_driver;
+    }
+
+  return new_fd;
+
+errout_unregister_driver:
+  unregister_driver(devpath);
+errout_release_minor:
+  timerfd_release_minor(new_dev->minor);
+  timerfd_destroy(new_dev);
+errout:
+  set_errno(ret);
+  return ERROR;
+}
+
+int timerfd_settime(int fd, int flags,
+                    FAR const struct itimerspec *new_value,
+                    FAR struct itimerspec *old_value)
+{
+  FAR struct file *filep;
+  FAR struct timerfd_priv_s *dev;
+  irqstate_t intflags;
+  sclock_t delay;
+  int ret;
+
+  /* Some sanity checks */
+
+  if (!new_value)
+    {
+      ret = EFAULT;
+      goto errout;
+    }
+
+  if (flags && (flags & TFD_TIMER_ABSTIME) == 0)
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* Get file pointer by file descriptor */
+
+  ret = fs_getfilep(fd, &filep);
+  if (ret < 0)
+    {
+      ret = -ret;

Review comment:
       same as timerfd_create

##########
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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter */
+
+  dev->counter++;
+
+  work_queue(TIMER_FD_WORK, &dev->work, timerfd_timeout_work, dev, 0);
+
+  /* If this is a repetitive timer, then restart the watchdog */
+
+  if (dev->delay)
+    {
+      wd_start(&dev->wdog, dev->delay, timerfd_timeout, idev);
+    }
+
+  leave_critical_section(intflags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int timerfd_create(int clockid, int flags)
+{
+  FAR struct timerfd_priv_s *new_dev;
+  int new_fd;
+  int ret;
+
+  /* Sanity checks. */
+
+  if (clockid != CLOCK_REALTIME
+#ifdef CONFIG_CLOCK_MONOTONIC
+      && clockid != CLOCK_MONOTONIC
+      && clockid != CLOCK_BOOTTIME
+#endif /* CONFIG_CLOCK_MONOTONIC */
+      )
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* devpath: TIMER_FD_VFS_PATH + /tfd (4) + %u (10) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 10 + 1];
+
+  /* Allocate instance data for this driver */
+
+  new_dev = timerfd_allocdev();
+  if (new_dev == NULL)
+    {
+      /* Failed to allocate new device */
+
+      ret = ENOMEM;
+      goto errout;
+    }
+
+  /* Initialize the timer instance */
+
+  new_dev->clock = clockid;
+  new_dev->crefs = 1;
+  new_dev->delay = 0;
+  new_dev->counter = 0;
+
+  /* Request a unique minor device number */
+
+  new_dev->minor = timerfd_get_unique_minor();
+
+  /* Get device path */
+
+  sprintf(devpath, CONFIG_TIMER_FD_VFS_PATH "/tfd%u", new_dev->minor);
+
+  /* Register the driver */
+
+  ret = register_driver(devpath, &g_timerfd_fops, 0444, new_dev);
+  if (ret < 0)
+    {
+      ferr("Failed to register new device %s: %d\n", devpath, ret);
+      ret = -ret;

Review comment:
       remove, let's assign ret to -EINVAL and -ENOMEM and set_errno(-ret) instead




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772076761



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,845 @@
+/****************************************************************************
+ * 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
+
+#ifdef __INT64_DEFINED
+#define TIMER_FD_COUNTER_MAX UINT64_MAX
+#else
+#define TIMER_FD_COUNTER_MAX UINT32_MAX
+#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);
+static int timerfd_ioctl(FAR struct file *filep, int cmd,
+                         unsigned long arg);
+#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 */
+  timerfd_ioctl  /* 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);
+}
+
+static int timerfd_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+#if 0
+  FAR struct inode *inode = filep->f_inode;
+  FAR struct timerfd_priv_s *priv = inode->i_private;
+#endif
+  return -ENOSYS;
+}
+
+#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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter.  Do not allow rollover */
+
+  if (dev->counter < TIMER_FD_COUNTER_MAX)
+    {
+      dev->counter++;

Review comment:
       > I do not want to add dependency to `atomic` for `timerfd` and `up_fetchadd32` is platform specific and dealing with 64 bit incrementing may be a bit tricky. Do you see a big problem in using of a critical section?
   > 
   
   No, just a suggestion for your consideration.
   
   > What I see even more, is changing `enter_critical_section`/`leave_critical_section` to `spin_lock_irqsave`/`spin_unlock_irqrestore` to ensure option when `wdog` is running at Core A while non-blocking read happens on Core B. @xiaoxiang781216 what do you think?
   > 
   
   Yes, if there is no potential wait in side the critical section, spinlock is better than critical section. Here is a issue related to this: https://github.com/apache/incubator-nuttx/issues/1144
   
   > BTW atomic_fetch_add also use `spin_lock_irqsave`/`spin_unlock_irqrestore`
   
   It's depends on the architecture(e.g. arm may use LDREX and STREX).




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771588692



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,840 @@
+/****************************************************************************
+ * 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 "/dev"

Review comment:
       /var/timer?

##########
File path: include/sys/timerfd.h
##########
@@ -0,0 +1,80 @@
+/****************************************************************************
+ * include/sys/timerfd.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_SYS_TIMERFD_H
+#define __INCLUDE_SYS_TIMERFD_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <fcntl.h>
+#include <time.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define TFD_NONBLOCK O_NONBLOCK
+#define TFD_CLOEXEC  O_CLOEXEC
+
+#define TFD_TIMER_ABSTIME TIMER_ABSTIME
+
+/* Get device minor number */
+
+#define TFD_FIOC_MINOR FIOC_MINOR
+
+/****************************************************************************
+ * Public Type Declarations
+ ****************************************************************************/
+
+/* Type for timer counter */
+
+typedef uint32_t timerfd_t;

Review comment:
       timerfd_t isn't used?

##########
File path: include/sys/timerfd.h
##########
@@ -0,0 +1,80 @@
+/****************************************************************************
+ * include/sys/timerfd.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_SYS_TIMERFD_H
+#define __INCLUDE_SYS_TIMERFD_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <fcntl.h>
+#include <time.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define TFD_NONBLOCK O_NONBLOCK
+#define TFD_CLOEXEC  O_CLOEXEC
+
+#define TFD_TIMER_ABSTIME TIMER_ABSTIME
+
+/* Get device minor number */
+
+#define TFD_FIOC_MINOR FIOC_MINOR
+
+/****************************************************************************
+ * Public Type Declarations
+ ****************************************************************************/
+
+/* Type for timer counter */
+
+typedef uint32_t timerfd_t;
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+int timerfd_create(int clockid, int flags);
+
+int timerfd_settime(int fd, int flags,
+                    FAR const struct itimerspec *new_value,
+                    FAR struct itimerspec *old_value);
+
+int timerfd_gettime(int fd, FAR struct itimerspec *curr_value);
+
+int timerfd_get_minor(int fd);

Review comment:
       Not defined on Linux

##########
File path: include/sys/syscall_lookup.h
##########
@@ -218,6 +218,11 @@ SYSCALL_LOOKUP(pwrite,                     4)
 #ifdef CONFIG_EVENT_FD
   SYSCALL_LOOKUP(eventfd,                  2)
 #endif
+#ifdef CONFIG_TIMER_FD
+  SYSCALL_LOOKUP(timerfd_create,           2)
+  SYSCALL_LOOKUP(timerfd_settime,          4)
+  SYSCALL_LOOKUP(timerfd_gettime,          2)

Review comment:
       it's better to implement timerfd_settime/timerfd_gettime through ioctl to reduce the syscall number.

##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,840 @@
+/****************************************************************************
+ * 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 "/dev"
+#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 TIMERFDWORK 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 */

Review comment:
       why not remove wdog and call work_queue with delay instead?

##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,840 @@
+/****************************************************************************
+ * 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 "/dev"
+#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 TIMERFDWORK 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 */
+  size_t        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);
+static int timerfd_ioctl(FAR struct file *filep, int cmd,
+                         unsigned long arg);
+#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 size_t timerfd_get_unique_minor(void);
+static void timerfd_release_minor(size_t 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 */
+  timerfd_ioctl  /* 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(TIMERFDWORK, &dev->work);
+  nxsem_destroy(&dev->exclsem);
+  kmm_free(dev);
+}
+
+#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 size_t timerfd_get_unique_minor(void)
+{
+  static size_t minor;
+
+  return minor++;
+}
+
+static void timerfd_release_minor(size_t 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) + %d (3) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 3 + 1];

Review comment:
       3->10 like open




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771799847



##########
File path: include/sys/timerfd.h
##########
@@ -0,0 +1,80 @@
+/****************************************************************************
+ * include/sys/timerfd.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_SYS_TIMERFD_H
+#define __INCLUDE_SYS_TIMERFD_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <fcntl.h>
+#include <time.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define TFD_NONBLOCK O_NONBLOCK
+#define TFD_CLOEXEC  O_CLOEXEC
+
+#define TFD_TIMER_ABSTIME TIMER_ABSTIME
+
+/* Get device minor number */
+
+#define TFD_FIOC_MINOR FIOC_MINOR
+
+/****************************************************************************
+ * Public Type Declarations
+ ****************************************************************************/
+
+/* Type for timer counter */
+
+typedef uint32_t timerfd_t;

Review comment:
       Yeah, but unfortunately there is no such type for `timefd_t`.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771799847



##########
File path: include/sys/timerfd.h
##########
@@ -0,0 +1,80 @@
+/****************************************************************************
+ * include/sys/timerfd.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_SYS_TIMERFD_H
+#define __INCLUDE_SYS_TIMERFD_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <fcntl.h>
+#include <time.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define TFD_NONBLOCK O_NONBLOCK
+#define TFD_CLOEXEC  O_CLOEXEC
+
+#define TFD_TIMER_ABSTIME TIMER_ABSTIME
+
+/* Get device minor number */
+
+#define TFD_FIOC_MINOR FIOC_MINOR
+
+/****************************************************************************
+ * Public Type Declarations
+ ****************************************************************************/
+
+/* Type for timer counter */
+
+typedef uint32_t timerfd_t;

Review comment:
       Yeah, but unfortunately there is no such type for `timerfd_t`.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771930309



##########
File path: include/sys/timerfd.h
##########
@@ -0,0 +1,80 @@
+/****************************************************************************
+ * include/sys/timerfd.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_SYS_TIMERFD_H
+#define __INCLUDE_SYS_TIMERFD_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <fcntl.h>
+#include <time.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define TFD_NONBLOCK O_NONBLOCK
+#define TFD_CLOEXEC  O_CLOEXEC
+
+#define TFD_TIMER_ABSTIME TIMER_ABSTIME
+
+/* Get device minor number */
+
+#define TFD_FIOC_MINOR FIOC_MINOR
+
+/****************************************************************************
+ * Public Type Declarations
+ ****************************************************************************/
+
+/* Type for timer counter */
+
+typedef uint32_t timerfd_t;

Review comment:
       @xiaoxiang781216 do you recommend to remove `timerfd_t` from the header? If yes, then interface in header will be not too clear IMO.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772074608



##########
File path: include/sys/syscall_lookup.h
##########
@@ -218,6 +218,11 @@ SYSCALL_LOOKUP(pwrite,                     4)
 #ifdef CONFIG_EVENT_FD
   SYSCALL_LOOKUP(eventfd,                  2)
 #endif
+#ifdef CONFIG_TIMER_FD
+  SYSCALL_LOOKUP(timerfd_create,           2)
+  SYSCALL_LOOKUP(timerfd_settime,          4)
+  SYSCALL_LOOKUP(timerfd_gettime,          2)

Review comment:
       Sure, let's do in the next step.
   
   > BTW going with
   > 
   > ```
   > struct timerfd_settime_s {
   >  int flags;
   >  struct itimerspec new_value;
   >  struct itimerspec old_value;
   > };
   > ```
   > 
   > is not the best as `old_value` is optional parameter that can be `NULL`.
   
   timerfd_ioctl could return old_value always, but timerfd_settime can skip the assignment in case of old_value is NULL. This is common trick to move the dirty work to the userspace library.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772147959



##########
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)

Review comment:
       copied from the `eventfd`. Probably need to revisit the change here and there




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771937530



##########
File path: include/sys/syscall_lookup.h
##########
@@ -218,6 +218,11 @@ SYSCALL_LOOKUP(pwrite,                     4)
 #ifdef CONFIG_EVENT_FD
   SYSCALL_LOOKUP(eventfd,                  2)
 #endif
+#ifdef CONFIG_TIMER_FD
+  SYSCALL_LOOKUP(timerfd_create,           2)
+  SYSCALL_LOOKUP(timerfd_settime,          4)
+  SYSCALL_LOOKUP(timerfd_gettime,          2)

Review comment:
       I see no problems with `timerfd_gettime` in such approach, but not sure about `timerfd_settime` that needs 3 arguments to be passed. In general `int flags, const struct itimerspec *new_value, struct itimerspec *old_value` should be packed into intermediate structure and pointer to that structure has to be passed to `ioctl` call. I also do not have any issues with that, but the question comes where the declaration of that intermediate type should be located? Should I call something like
   ```
   typedef struct
   {
     int flags;
     const struct itimerspec *new_value;
     struct itimerspec *old_value;
   } timerfd_settime_t
   ```
   into `timerfd.h`?




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771943435



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,845 @@
+/****************************************************************************
+ * 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
+
+#ifdef __INT64_DEFINED
+#define TIMER_FD_COUNTER_MAX UINT64_MAX
+#else
+#define TIMER_FD_COUNTER_MAX UINT32_MAX
+#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);
+static int timerfd_ioctl(FAR struct file *filep, int cmd,
+                         unsigned long arg);
+#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 */
+  timerfd_ioctl  /* 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);
+}
+
+static int timerfd_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+#if 0
+  FAR struct inode *inode = filep->f_inode;
+  FAR struct timerfd_priv_s *priv = inode->i_private;
+#endif
+  return -ENOSYS;
+}
+
+#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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter.  Do not allow rollover */
+
+  if (dev->counter < TIMER_FD_COUNTER_MAX)

Review comment:
       @xiaoxiang781216 what do you think should we allow rollover?




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771996880



##########
File path: include/sys/syscall_lookup.h
##########
@@ -218,6 +218,11 @@ SYSCALL_LOOKUP(pwrite,                     4)
 #ifdef CONFIG_EVENT_FD
   SYSCALL_LOOKUP(eventfd,                  2)
 #endif
+#ifdef CONFIG_TIMER_FD
+  SYSCALL_LOOKUP(timerfd_create,           2)
+  SYSCALL_LOOKUP(timerfd_settime,          4)
+  SYSCALL_LOOKUP(timerfd_gettime,          2)

Review comment:
       Can this be done in 2 steps? I really do not mind to go this way, but since such change goes in another path compared to Linux and require additional retesting and significant rework (at least IMO) I would like to open an enhancement issue and involve some more people into discussion instead of doing changes in this  PR. @xiaoxiang781216 does this sounds reasonable?




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772164452



##########
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)

Review comment:
       Seems that there is a big bug in all this `slist` logic here and in `eventfd`. I will try to fix it




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771640983



##########
File path: include/sys/timerfd.h
##########
@@ -0,0 +1,80 @@
+/****************************************************************************
+ * include/sys/timerfd.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_SYS_TIMERFD_H
+#define __INCLUDE_SYS_TIMERFD_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <fcntl.h>
+#include <time.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define TFD_NONBLOCK O_NONBLOCK
+#define TFD_CLOEXEC  O_CLOEXEC
+
+#define TFD_TIMER_ABSTIME TIMER_ABSTIME
+
+/* Get device minor number */
+
+#define TFD_FIOC_MINOR FIOC_MINOR
+
+/****************************************************************************
+ * Public Type Declarations
+ ****************************************************************************/
+
+/* Type for timer counter */
+
+typedef uint32_t timerfd_t;

Review comment:
       It is used internally. Probably will move it to C-file.
   I noticed that `eventfd` use `typedef uint32_t eventfd_t;` while Linux `typedef uint64_t eventfd_t;`, so I implemented `timerfd` also to use 32-bit.
   @xiaoxiang781216 what do you think, should we move to `uint64_t` for both `eventfd` and `timerfd`?




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772174306



##########
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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter */
+
+  dev->counter++;
+
+  work_queue(TIMER_FD_WORK, &dev->work, timerfd_timeout_work, dev, 0);
+
+  /* If this is a repetitive timer, then restart the watchdog */
+
+  if (dev->delay)
+    {
+      wd_start(&dev->wdog, dev->delay, timerfd_timeout, idev);
+    }
+
+  leave_critical_section(intflags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int timerfd_create(int clockid, int flags)
+{
+  FAR struct timerfd_priv_s *new_dev;
+  int new_fd;
+  int ret;
+
+  /* Sanity checks. */
+
+  if (clockid != CLOCK_REALTIME
+#ifdef CONFIG_CLOCK_MONOTONIC
+      && clockid != CLOCK_MONOTONIC
+      && clockid != CLOCK_BOOTTIME
+#endif /* CONFIG_CLOCK_MONOTONIC */
+      )
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* devpath: TIMER_FD_VFS_PATH + /tfd (4) + %u (10) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 10 + 1];
+
+  /* Allocate instance data for this driver */
+
+  new_dev = timerfd_allocdev();
+  if (new_dev == NULL)
+    {
+      /* Failed to allocate new device */
+
+      ret = ENOMEM;
+      goto errout;
+    }
+
+  /* Initialize the timer instance */
+
+  new_dev->clock = clockid;
+  new_dev->crefs = 1;
+  new_dev->delay = 0;
+  new_dev->counter = 0;
+
+  /* Request a unique minor device number */
+
+  new_dev->minor = timerfd_get_unique_minor();
+
+  /* Get device path */
+
+  sprintf(devpath, CONFIG_TIMER_FD_VFS_PATH "/tfd%u", new_dev->minor);
+
+  /* Register the driver */
+
+  ret = register_driver(devpath, &g_timerfd_fops, 0444, new_dev);
+  if (ret < 0)
+    {
+      ferr("Failed to register new device %s: %d\n", devpath, ret);
+      ret = -ret;

Review comment:
       Ok. I will redo the logic




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771824136



##########
File path: include/sys/syscall_lookup.h
##########
@@ -218,6 +218,11 @@ SYSCALL_LOOKUP(pwrite,                     4)
 #ifdef CONFIG_EVENT_FD
   SYSCALL_LOOKUP(eventfd,                  2)
 #endif
+#ifdef CONFIG_TIMER_FD
+  SYSCALL_LOOKUP(timerfd_create,           2)
+  SYSCALL_LOOKUP(timerfd_settime,          4)
+  SYSCALL_LOOKUP(timerfd_gettime,          2)

Review comment:
       The idea is simple:
   
   1. Add ioctl(e.g. TIMERFD_SETTIME, TIMER_GETTIME)
   2. Move the code from timerfd_settime/timerfd_getime) to timerfd_ioctl
   3. Implement timerfd_settime and timerfd_gettime in libc as the simple ioctl wrapper.
   
   Many driver use this technique, you can reference terminal API here:
   https://github.com/apache/incubator-nuttx/tree/master/libs/libc/termios
   https://github.com/apache/incubator-nuttx/blob/master/drivers/serial/serial.c#L1335-L1445




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772144813



##########
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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter */
+
+  dev->counter++;
+
+  work_queue(TIMER_FD_WORK, &dev->work, timerfd_timeout_work, dev, 0);
+
+  /* If this is a repetitive timer, then restart the watchdog */
+
+  if (dev->delay)
+    {
+      wd_start(&dev->wdog, dev->delay, timerfd_timeout, idev);
+    }
+
+  leave_critical_section(intflags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int timerfd_create(int clockid, int flags)
+{
+  FAR struct timerfd_priv_s *new_dev;
+  int new_fd;
+  int ret;
+
+  /* Sanity checks. */
+
+  if (clockid != CLOCK_REALTIME
+#ifdef CONFIG_CLOCK_MONOTONIC
+      && clockid != CLOCK_MONOTONIC
+      && clockid != CLOCK_BOOTTIME
+#endif /* CONFIG_CLOCK_MONOTONIC */
+      )
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* devpath: TIMER_FD_VFS_PATH + /tfd (4) + %u (10) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 10 + 1];
+
+  /* Allocate instance data for this driver */
+
+  new_dev = timerfd_allocdev();
+  if (new_dev == NULL)
+    {
+      /* Failed to allocate new device */
+
+      ret = ENOMEM;
+      goto errout;
+    }
+
+  /* Initialize the timer instance */
+
+  new_dev->clock = clockid;
+  new_dev->crefs = 1;
+  new_dev->delay = 0;
+  new_dev->counter = 0;
+
+  /* Request a unique minor device number */
+
+  new_dev->minor = timerfd_get_unique_minor();
+
+  /* Get device path */
+
+  sprintf(devpath, CONFIG_TIMER_FD_VFS_PATH "/tfd%u", new_dev->minor);
+
+  /* Register the driver */
+
+  ret = register_driver(devpath, &g_timerfd_fops, 0444, new_dev);
+  if (ret < 0)
+    {
+      ferr("Failed to register new device %s: %d\n", devpath, ret);
+      ret = -ret;

Review comment:
       What do we gain with this?




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771982129



##########
File path: include/sys/timerfd.h
##########
@@ -0,0 +1,80 @@
+/****************************************************************************
+ * include/sys/timerfd.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_SYS_TIMERFD_H
+#define __INCLUDE_SYS_TIMERFD_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <fcntl.h>
+#include <time.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define TFD_NONBLOCK O_NONBLOCK
+#define TFD_CLOEXEC  O_CLOEXEC
+
+#define TFD_TIMER_ABSTIME TIMER_ABSTIME
+
+/* Get device minor number */
+
+#define TFD_FIOC_MINOR FIOC_MINOR
+
+/****************************************************************************
+ * Public Type Declarations
+ ****************************************************************************/
+
+/* Type for timer counter */
+
+typedef uint32_t timerfd_t;

Review comment:
       No, I think it's better to keep in the public header file, so people get notice that the size may change from 64bits to 32bits.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771939965



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,840 @@
+/****************************************************************************
+ * 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 "/dev"
+#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 TIMERFDWORK 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 */

Review comment:
       I think that current approach if fine as I would like to keep timer expiration counter incremented precisely in `wdog` handler while notification is done in the background `work_queue` thread that calls `nxsem_wait_uninterruptible(&dev->exclsem);`. In general this potentially can be more precise if non-bocking `read` is performed just after watchdog expires, but before `word_queue` handler is involved (I mean in the case if we switch to delayed `work_queue` and call increment there). 




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771826966



##########
File path: include/sys/timerfd.h
##########
@@ -0,0 +1,80 @@
+/****************************************************************************
+ * include/sys/timerfd.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_SYS_TIMERFD_H
+#define __INCLUDE_SYS_TIMERFD_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <fcntl.h>
+#include <time.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define TFD_NONBLOCK O_NONBLOCK
+#define TFD_CLOEXEC  O_CLOEXEC
+
+#define TFD_TIMER_ABSTIME TIMER_ABSTIME
+
+/* Get device minor number */
+
+#define TFD_FIOC_MINOR FIOC_MINOR
+
+/****************************************************************************
+ * Public Type Declarations
+ ****************************************************************************/
+
+/* Type for timer counter */
+
+typedef uint32_t timerfd_t;

Review comment:
       Fix for eventfd is here: https://github.com/apache/incubator-nuttx/pull/5028/commits/a2e527dc13ded602484ecf9e7cb5089d4e8ac1bf




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772146124



##########
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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter */
+
+  dev->counter++;
+
+  work_queue(TIMER_FD_WORK, &dev->work, timerfd_timeout_work, dev, 0);
+
+  /* If this is a repetitive timer, then restart the watchdog */
+
+  if (dev->delay)
+    {
+      wd_start(&dev->wdog, dev->delay, timerfd_timeout, idev);
+    }
+
+  leave_critical_section(intflags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int timerfd_create(int clockid, int flags)
+{
+  FAR struct timerfd_priv_s *new_dev;
+  int new_fd;
+  int ret;
+
+  /* Sanity checks. */
+
+  if (clockid != CLOCK_REALTIME
+#ifdef CONFIG_CLOCK_MONOTONIC
+      && clockid != CLOCK_MONOTONIC
+      && clockid != CLOCK_BOOTTIME
+#endif /* CONFIG_CLOCK_MONOTONIC */
+      )
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* devpath: TIMER_FD_VFS_PATH + /tfd (4) + %u (10) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 10 + 1];
+
+  /* Allocate instance data for this driver */
+
+  new_dev = timerfd_allocdev();
+  if (new_dev == NULL)
+    {
+      /* Failed to allocate new device */
+
+      ret = ENOMEM;
+      goto errout;
+    }
+
+  /* Initialize the timer instance */
+
+  new_dev->clock = clockid;
+  new_dev->crefs = 1;
+  new_dev->delay = 0;
+  new_dev->counter = 0;
+
+  /* Request a unique minor device number */
+
+  new_dev->minor = timerfd_get_unique_minor();
+
+  /* Get device path */
+
+  sprintf(devpath, CONFIG_TIMER_FD_VFS_PATH "/tfd%u", new_dev->minor);
+
+  /* Register the driver */
+
+  ret = register_driver(devpath, &g_timerfd_fops, 0444, new_dev);
+  if (ret < 0)
+    {
+      ferr("Failed to register new device %s: %d\n", devpath, ret);
+      ret = -ret;

Review comment:
       - `int closedir(FAR DIR *dirp)`
   - `FAR DIR *opendir(FAR const char *path)`
   - `FAR struct dirent *readdir(DIR *dirp)`
   - `FAR char *getenv(FAR const char *name)`
   - and others
   Use the same approach as here




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771982974



##########
File path: include/sys/syscall_lookup.h
##########
@@ -218,6 +218,11 @@ SYSCALL_LOOKUP(pwrite,                     4)
 #ifdef CONFIG_EVENT_FD
   SYSCALL_LOOKUP(eventfd,                  2)
 #endif
+#ifdef CONFIG_TIMER_FD
+  SYSCALL_LOOKUP(timerfd_create,           2)
+  SYSCALL_LOOKUP(timerfd_settime,          4)
+  SYSCALL_LOOKUP(timerfd_gettime,          2)

Review comment:
       > I see no problems with `timerfd_gettime` in such approach, but not sure about `timerfd_settime` that needs 3 arguments to be passed. In general `int flags, const struct itimerspec *new_value, struct itimerspec *old_value` should be packed into intermediate structure and pointer to that structure has to be passed to `ioctl` call. I also do not have any issues with that, but the question comes where the declaration of that intermediate type should be located? Should I call something like
   > 
   > ```
   > typedef struct
   > {
   >   int flags;
   >   const struct itimerspec *new_value;
   >   struct itimerspec *old_value;
   > } timerfd_settime_t;
   > ```
   > 
   > into `timerfd.h`? @xiaoxiang781216 what is your opinion?
   
   Yes, it's reasonable. BTW, it's better to use struct directly and change pointer to value, like this:
   ```
    struct timerfd_settime_s
    {
      int flags;
      struct itimerspec new_value;
      struct itimerspec old_value;
    };
   ```
   
   > Also `eventfd_read` and `eventfd_write` are `glibc` functions by definition. While `timerfd` APIs are a syscalls in Linux
   
   Once we move the action into `timerfd_ioctl`, `timerfd_settime` and `timerfd_gettime` can be changed to `libc` function just like `eventfd_read` and `eventfd_write`. Only, timerfd_create need be syscall.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772076761



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,845 @@
+/****************************************************************************
+ * 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
+
+#ifdef __INT64_DEFINED
+#define TIMER_FD_COUNTER_MAX UINT64_MAX
+#else
+#define TIMER_FD_COUNTER_MAX UINT32_MAX
+#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);
+static int timerfd_ioctl(FAR struct file *filep, int cmd,
+                         unsigned long arg);
+#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 */
+  timerfd_ioctl  /* 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);
+}
+
+static int timerfd_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+#if 0
+  FAR struct inode *inode = filep->f_inode;
+  FAR struct timerfd_priv_s *priv = inode->i_private;
+#endif
+  return -ENOSYS;
+}
+
+#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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter.  Do not allow rollover */
+
+  if (dev->counter < TIMER_FD_COUNTER_MAX)
+    {
+      dev->counter++;

Review comment:
       > I do not want to add dependency to `atomic` for `timerfd` and `up_fetchadd32` is platform specific and dealing with 64 bit incrementing may be a bit tricky. Do you see a big problem in using of a critical section?
   > 
   
   No, just a suggestion for your consideration.
   
   > What I see even more, is changing `enter_critical_section`/`leave_critical_section` to `spin_lock_irqsave`/`spin_unlock_irqrestore` to ensure option when `wdog` is running at Core A while non-blocking read happens on Core B. @xiaoxiang781216 what do you think?
   > 
   
   Yes, if there is no potential wait in side the critical section, spinlock is better than critical section
   
   > BTW atomic_fetch_add also use `spin_lock_irqsave`/`spin_unlock_irqrestore`
   
   It's depends on the architecture(e.g. arm may use LDREX and STREX).




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772188447



##########
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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter */
+
+  dev->counter++;
+
+  work_queue(TIMER_FD_WORK, &dev->work, timerfd_timeout_work, dev, 0);
+
+  /* If this is a repetitive timer, then restart the watchdog */
+
+  if (dev->delay)
+    {
+      wd_start(&dev->wdog, dev->delay, timerfd_timeout, idev);
+    }
+
+  leave_critical_section(intflags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int timerfd_create(int clockid, int flags)
+{
+  FAR struct timerfd_priv_s *new_dev;
+  int new_fd;
+  int ret;
+
+  /* Sanity checks. */
+
+  if (clockid != CLOCK_REALTIME
+#ifdef CONFIG_CLOCK_MONOTONIC
+      && clockid != CLOCK_MONOTONIC
+      && clockid != CLOCK_BOOTTIME
+#endif /* CONFIG_CLOCK_MONOTONIC */
+      )
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* devpath: TIMER_FD_VFS_PATH + /tfd (4) + %u (10) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 10 + 1];
+
+  /* Allocate instance data for this driver */
+
+  new_dev = timerfd_allocdev();
+  if (new_dev == NULL)
+    {
+      /* Failed to allocate new device */
+
+      ret = ENOMEM;
+      goto errout;
+    }
+
+  /* Initialize the timer instance */
+
+  new_dev->clock = clockid;
+  new_dev->crefs = 1;
+  new_dev->delay = 0;
+  new_dev->counter = 0;
+
+  /* Request a unique minor device number */
+
+  new_dev->minor = timerfd_get_unique_minor();
+
+  /* Get device path */
+
+  sprintf(devpath, CONFIG_TIMER_FD_VFS_PATH "/tfd%u", new_dev->minor);
+
+  /* Register the driver */
+
+  ret = register_driver(devpath, &g_timerfd_fops, 0444, new_dev);
+  if (ret < 0)
+    {
+      ferr("Failed to register new device %s: %d\n", devpath, ret);
+      ret = -ret;
+      goto errout_release_minor;
+    }
+
+  /* Device is ready for use */
+
+  nxsem_post(&new_dev->exclsem);
+
+  /* Try open new device */
+
+  new_fd = nx_open(devpath, O_RDONLY |
+                   (flags & (TFD_NONBLOCK | TFD_CLOEXEC)));
+
+  if (new_fd < 0)
+    {
+      ret = -new_fd;
+      goto errout_unregister_driver;
+    }
+
+  return new_fd;
+
+errout_unregister_driver:
+  unregister_driver(devpath);
+errout_release_minor:
+  timerfd_release_minor(new_dev->minor);
+  timerfd_destroy(new_dev);
+errout:
+  set_errno(ret);
+  return ERROR;
+}
+
+int timerfd_settime(int fd, int flags,
+                    FAR const struct itimerspec *new_value,
+                    FAR struct itimerspec *old_value)
+{
+  FAR struct file *filep;
+  FAR struct timerfd_priv_s *dev;
+  irqstate_t intflags;
+  sclock_t delay;
+  int ret;
+
+  /* Some sanity checks */
+
+  if (!new_value)
+    {
+      ret = EFAULT;
+      goto errout;
+    }
+
+  if (flags && (flags & TFD_TIMER_ABSTIME) == 0)
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* Get file pointer by file descriptor */
+
+  ret = fs_getfilep(fd, &filep);
+  if (ret < 0)
+    {
+      ret = -ret;
+      goto errout;
+    }
+
+  /* Check fd come from us */
+
+  if (!filep->f_inode || filep->f_inode->u.i_ops != &g_timerfd_fops)
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  dev = (FAR struct timerfd_priv_s *)filep->f_inode->i_private;
+
+  if (old_value)
+    {
+      /* Get the number of ticks before the underlying watchdog expires */
+
+      delay = wd_gettime(&dev->wdog);
+
+      /* Convert that to a struct timespec and return it */
+
+      clock_ticks2time(delay, &old_value->it_value);
+      clock_ticks2time(dev->delay, &old_value->it_interval);
+    }
+
+  /* Disable interrupts here to ensure that expiration counter is accessed
+   * atomicaly and timeout work is canceled with the same sequence
+   */
+
+  intflags = enter_critical_section();
+
+  /* Disarm the timer (in case the timer was already armed when
+   * timerfd_settime() is called).
+   */
+
+  wd_cancel(&dev->wdog);
+
+  /* Cancel notification work */
+
+  work_cancel(TIMER_FD_WORK, &dev->work);
+
+  /* Clear expiration counter */
+
+  dev->counter = 0;
+
+  /* If the it_value member of value is zero, the timer will not be
+   * re-armed
+   */
+
+  if (new_value->it_value.tv_sec <= 0 && new_value->it_value.tv_nsec <= 0)
+    {
+      leave_critical_section(intflags);
+      return OK;
+    }
+
+  /* Setup up any repetitive timer */
+
+  if (new_value->it_interval.tv_sec > 0 ||
+      new_value->it_interval.tv_nsec > 0)
+    {
+      clock_time2ticks(&new_value->it_interval, &delay);
+
+      /* REVISIT: Should delay be sclock_t? */
+
+      dev->delay = (int)delay;
+    }
+  else
+    {
+      dev->delay = 0;
+    }
+
+  /* We need to disable timer interrupts through the following section so
+   * that the system timer is stable.
+   */
+
+  /* Check if abstime is selected */
+
+  if ((flags & TFD_TIMER_ABSTIME) != 0)
+    {
+      /* Calculate a delay corresponding to the absolute time in 'value' */
+
+      clock_abstime2ticks(dev->clock, &new_value->it_value, &delay);
+    }
+  else
+    {
+      /* Calculate a delay assuming that 'value' holds the relative time
+       * to wait.  We have internal knowledge that clock_time2ticks always
+       * returns success.
+       */
+
+      clock_time2ticks(&new_value->it_value, &delay);
+    }
+
+  /* If the time is in the past or now, then set up the next interval
+   * instead (assuming a repetitive timer).
+   */
+
+  if (delay <= 0)
+    {
+      delay = dev->delay;
+    }
+
+  /* Then start the watchdog */
+
+  if (delay > 0)
+    {
+      ret = wd_start(&dev->wdog, delay, timerfd_timeout, (wdparm_t)dev);
+      if (ret < 0)
+        {
+          ret = -ret;

Review comment:
       Done

##########
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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter */
+
+  dev->counter++;
+
+  work_queue(TIMER_FD_WORK, &dev->work, timerfd_timeout_work, dev, 0);
+
+  /* If this is a repetitive timer, then restart the watchdog */
+
+  if (dev->delay)
+    {
+      wd_start(&dev->wdog, dev->delay, timerfd_timeout, idev);
+    }
+
+  leave_critical_section(intflags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int timerfd_create(int clockid, int flags)
+{
+  FAR struct timerfd_priv_s *new_dev;
+  int new_fd;
+  int ret;
+
+  /* Sanity checks. */
+
+  if (clockid != CLOCK_REALTIME
+#ifdef CONFIG_CLOCK_MONOTONIC
+      && clockid != CLOCK_MONOTONIC
+      && clockid != CLOCK_BOOTTIME
+#endif /* CONFIG_CLOCK_MONOTONIC */
+      )
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* devpath: TIMER_FD_VFS_PATH + /tfd (4) + %u (10) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 10 + 1];
+
+  /* Allocate instance data for this driver */
+
+  new_dev = timerfd_allocdev();
+  if (new_dev == NULL)
+    {
+      /* Failed to allocate new device */
+
+      ret = ENOMEM;
+      goto errout;
+    }
+
+  /* Initialize the timer instance */
+
+  new_dev->clock = clockid;
+  new_dev->crefs = 1;
+  new_dev->delay = 0;
+  new_dev->counter = 0;
+
+  /* Request a unique minor device number */
+
+  new_dev->minor = timerfd_get_unique_minor();
+
+  /* Get device path */
+
+  sprintf(devpath, CONFIG_TIMER_FD_VFS_PATH "/tfd%u", new_dev->minor);
+
+  /* Register the driver */
+
+  ret = register_driver(devpath, &g_timerfd_fops, 0444, new_dev);
+  if (ret < 0)
+    {
+      ferr("Failed to register new device %s: %d\n", devpath, ret);
+      ret = -ret;
+      goto errout_release_minor;
+    }
+
+  /* Device is ready for use */
+
+  nxsem_post(&new_dev->exclsem);
+
+  /* Try open new device */
+
+  new_fd = nx_open(devpath, O_RDONLY |
+                   (flags & (TFD_NONBLOCK | TFD_CLOEXEC)));
+
+  if (new_fd < 0)
+    {
+      ret = -new_fd;
+      goto errout_unregister_driver;
+    }
+
+  return new_fd;
+
+errout_unregister_driver:
+  unregister_driver(devpath);
+errout_release_minor:
+  timerfd_release_minor(new_dev->minor);
+  timerfd_destroy(new_dev);
+errout:
+  set_errno(ret);
+  return ERROR;
+}
+
+int timerfd_settime(int fd, int flags,
+                    FAR const struct itimerspec *new_value,
+                    FAR struct itimerspec *old_value)
+{
+  FAR struct file *filep;
+  FAR struct timerfd_priv_s *dev;
+  irqstate_t intflags;
+  sclock_t delay;
+  int ret;
+
+  /* Some sanity checks */
+
+  if (!new_value)
+    {
+      ret = EFAULT;
+      goto errout;
+    }
+
+  if (flags && (flags & TFD_TIMER_ABSTIME) == 0)
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* Get file pointer by file descriptor */
+
+  ret = fs_getfilep(fd, &filep);
+  if (ret < 0)
+    {
+      ret = -ret;
+      goto errout;
+    }
+
+  /* Check fd come from us */
+
+  if (!filep->f_inode || filep->f_inode->u.i_ops != &g_timerfd_fops)
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  dev = (FAR struct timerfd_priv_s *)filep->f_inode->i_private;
+
+  if (old_value)
+    {
+      /* Get the number of ticks before the underlying watchdog expires */
+
+      delay = wd_gettime(&dev->wdog);
+
+      /* Convert that to a struct timespec and return it */
+
+      clock_ticks2time(delay, &old_value->it_value);
+      clock_ticks2time(dev->delay, &old_value->it_interval);
+    }
+
+  /* Disable interrupts here to ensure that expiration counter is accessed
+   * atomicaly and timeout work is canceled with the same sequence
+   */
+
+  intflags = enter_critical_section();
+
+  /* Disarm the timer (in case the timer was already armed when
+   * timerfd_settime() is called).
+   */
+
+  wd_cancel(&dev->wdog);
+
+  /* Cancel notification work */
+
+  work_cancel(TIMER_FD_WORK, &dev->work);
+
+  /* Clear expiration counter */
+
+  dev->counter = 0;
+
+  /* If the it_value member of value is zero, the timer will not be
+   * re-armed
+   */
+
+  if (new_value->it_value.tv_sec <= 0 && new_value->it_value.tv_nsec <= 0)
+    {
+      leave_critical_section(intflags);
+      return OK;
+    }
+
+  /* Setup up any repetitive timer */
+
+  if (new_value->it_interval.tv_sec > 0 ||
+      new_value->it_interval.tv_nsec > 0)
+    {
+      clock_time2ticks(&new_value->it_interval, &delay);
+
+      /* REVISIT: Should delay be sclock_t? */
+
+      dev->delay = (int)delay;
+    }
+  else
+    {
+      dev->delay = 0;
+    }
+
+  /* We need to disable timer interrupts through the following section so
+   * that the system timer is stable.
+   */
+
+  /* Check if abstime is selected */
+
+  if ((flags & TFD_TIMER_ABSTIME) != 0)
+    {
+      /* Calculate a delay corresponding to the absolute time in 'value' */
+
+      clock_abstime2ticks(dev->clock, &new_value->it_value, &delay);
+    }
+  else
+    {
+      /* Calculate a delay assuming that 'value' holds the relative time
+       * to wait.  We have internal knowledge that clock_time2ticks always
+       * returns success.
+       */
+
+      clock_time2ticks(&new_value->it_value, &delay);
+    }
+
+  /* If the time is in the past or now, then set up the next interval
+   * instead (assuming a repetitive timer).
+   */
+
+  if (delay <= 0)
+    {
+      delay = dev->delay;
+    }
+
+  /* Then start the watchdog */
+
+  if (delay > 0)
+    {
+      ret = wd_start(&dev->wdog, delay, timerfd_timeout, (wdparm_t)dev);
+      if (ret < 0)
+        {
+          ret = -ret;
+          leave_critical_section(intflags);
+          goto errout;
+        }
+    }
+
+  leave_critical_section(intflags);
+  return OK;
+
+errout:
+  set_errno(ret);
+  return ERROR;
+}
+
+int timerfd_gettime(int fd, FAR struct itimerspec *curr_value)
+{
+  FAR struct file *filep;
+  FAR struct timerfd_priv_s *dev;
+  sclock_t ticks;
+  int ret;
+
+  /* Some sanity checks */
+
+  if (!curr_value)
+    {
+      ret = EFAULT;
+      goto errout;
+    }
+
+  /* Get file pointer by file descriptor */
+
+  ret = fs_getfilep(fd, &filep);
+  if (ret < 0)
+    {
+      ret = -ret;

Review comment:
       Done

##########
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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter */
+
+  dev->counter++;
+
+  work_queue(TIMER_FD_WORK, &dev->work, timerfd_timeout_work, dev, 0);
+
+  /* If this is a repetitive timer, then restart the watchdog */
+
+  if (dev->delay)
+    {
+      wd_start(&dev->wdog, dev->delay, timerfd_timeout, idev);
+    }
+
+  leave_critical_section(intflags);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int timerfd_create(int clockid, int flags)
+{
+  FAR struct timerfd_priv_s *new_dev;
+  int new_fd;
+  int ret;
+
+  /* Sanity checks. */
+
+  if (clockid != CLOCK_REALTIME
+#ifdef CONFIG_CLOCK_MONOTONIC
+      && clockid != CLOCK_MONOTONIC
+      && clockid != CLOCK_BOOTTIME
+#endif /* CONFIG_CLOCK_MONOTONIC */
+      )
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* devpath: TIMER_FD_VFS_PATH + /tfd (4) + %u (10) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 10 + 1];
+
+  /* Allocate instance data for this driver */
+
+  new_dev = timerfd_allocdev();
+  if (new_dev == NULL)
+    {
+      /* Failed to allocate new device */
+
+      ret = ENOMEM;
+      goto errout;
+    }
+
+  /* Initialize the timer instance */
+
+  new_dev->clock = clockid;
+  new_dev->crefs = 1;
+  new_dev->delay = 0;
+  new_dev->counter = 0;
+
+  /* Request a unique minor device number */
+
+  new_dev->minor = timerfd_get_unique_minor();
+
+  /* Get device path */
+
+  sprintf(devpath, CONFIG_TIMER_FD_VFS_PATH "/tfd%u", new_dev->minor);
+
+  /* Register the driver */
+
+  ret = register_driver(devpath, &g_timerfd_fops, 0444, new_dev);
+  if (ret < 0)
+    {
+      ferr("Failed to register new device %s: %d\n", devpath, ret);
+      ret = -ret;
+      goto errout_release_minor;
+    }
+
+  /* Device is ready for use */
+
+  nxsem_post(&new_dev->exclsem);
+
+  /* Try open new device */
+
+  new_fd = nx_open(devpath, O_RDONLY |
+                   (flags & (TFD_NONBLOCK | TFD_CLOEXEC)));
+
+  if (new_fd < 0)
+    {
+      ret = -new_fd;
+      goto errout_unregister_driver;
+    }
+
+  return new_fd;
+
+errout_unregister_driver:
+  unregister_driver(devpath);
+errout_release_minor:
+  timerfd_release_minor(new_dev->minor);
+  timerfd_destroy(new_dev);
+errout:
+  set_errno(ret);
+  return ERROR;
+}
+
+int timerfd_settime(int fd, int flags,
+                    FAR const struct itimerspec *new_value,
+                    FAR struct itimerspec *old_value)
+{
+  FAR struct file *filep;
+  FAR struct timerfd_priv_s *dev;
+  irqstate_t intflags;
+  sclock_t delay;
+  int ret;
+
+  /* Some sanity checks */
+
+  if (!new_value)
+    {
+      ret = EFAULT;
+      goto errout;
+    }
+
+  if (flags && (flags & TFD_TIMER_ABSTIME) == 0)
+    {
+      ret = EINVAL;
+      goto errout;
+    }
+
+  /* Get file pointer by file descriptor */
+
+  ret = fs_getfilep(fd, &filep);
+  if (ret < 0)
+    {
+      ret = -ret;

Review comment:
       Done




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771643792



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,840 @@
+/****************************************************************************
+ * 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 "/dev"
+#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 TIMERFDWORK 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 */
+  size_t        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);
+static int timerfd_ioctl(FAR struct file *filep, int cmd,
+                         unsigned long arg);
+#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 size_t timerfd_get_unique_minor(void);
+static void timerfd_release_minor(size_t 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 */
+  timerfd_ioctl  /* 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(TIMERFDWORK, &dev->work);
+  nxsem_destroy(&dev->exclsem);
+  kmm_free(dev);
+}
+
+#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 size_t timerfd_get_unique_minor(void)
+{
+  static size_t minor;
+
+  return minor++;
+}
+
+static void timerfd_release_minor(size_t 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) + %d (3) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 3 + 1];

Review comment:
       Again copy paste from `eventfd`.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771638879



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,840 @@
+/****************************************************************************
+ * 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 "/dev"

Review comment:
       Copy paste from `eventfd`. Will fix it

##########
File path: include/sys/timerfd.h
##########
@@ -0,0 +1,80 @@
+/****************************************************************************
+ * include/sys/timerfd.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_SYS_TIMERFD_H
+#define __INCLUDE_SYS_TIMERFD_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <fcntl.h>
+#include <time.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define TFD_NONBLOCK O_NONBLOCK
+#define TFD_CLOEXEC  O_CLOEXEC
+
+#define TFD_TIMER_ABSTIME TIMER_ABSTIME
+
+/* Get device minor number */
+
+#define TFD_FIOC_MINOR FIOC_MINOR
+
+/****************************************************************************
+ * Public Type Declarations
+ ****************************************************************************/
+
+/* Type for timer counter */
+
+typedef uint32_t timerfd_t;
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+int timerfd_create(int clockid, int flags);
+
+int timerfd_settime(int fd, int flags,
+                    FAR const struct itimerspec *new_value,
+                    FAR struct itimerspec *old_value);
+
+int timerfd_gettime(int fd, FAR struct itimerspec *curr_value);
+
+int timerfd_get_minor(int fd);

Review comment:
       Will remove




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772154058



##########
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);

Review comment:
       Ok




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771996234



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,845 @@
+/****************************************************************************
+ * 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
+
+#ifdef __INT64_DEFINED
+#define TIMER_FD_COUNTER_MAX UINT64_MAX
+#else
+#define TIMER_FD_COUNTER_MAX UINT32_MAX
+#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);
+static int timerfd_ioctl(FAR struct file *filep, int cmd,
+                         unsigned long arg);
+#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 */
+  timerfd_ioctl  /* 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);
+}
+
+static int timerfd_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+#if 0
+  FAR struct inode *inode = filep->f_inode;
+  FAR struct timerfd_priv_s *priv = inode->i_private;
+#endif
+  return -ENOSYS;
+}
+
+#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;
+
+  nxsem_post(&dev->exclsem);
+}
+
+static void timerfd_timeout(wdparm_t idev)
+{
+  FAR struct timerfd_priv_s *dev = (FAR struct timerfd_priv_s *)idev;
+  irqstate_t intflags;
+
+  /* Disable interrupts to ensure that expiration counter is accessed
+   * atomically
+   */
+
+  intflags = enter_critical_section();
+
+  /* Increment timer expiration counter.  Do not allow rollover */
+
+  if (dev->counter < TIMER_FD_COUNTER_MAX)
+    {
+      dev->counter++;

Review comment:
       I do not want to add dependency to `atomic` for `timerfd` and `up_fetchadd32` is platform specific and dealing with 64 bit incrementing may be a bit tricky.
   Do you see a big problem in using of a critical section?
   
   What I see even more, is changing `enter_critical_section`/`leave_critical_section` to `spin_lock_irqsave`/`spin_unlock_irqrestore` to ensure option when `wdog` is running at Core A while non-blocking read happens on Core B. @xiaoxiang781216 what do you think?
   
   BTW atomic_fetch_add also use `spin_lock_irqsave`/`spin_unlock_irqrestore`




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772172732



##########
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)

Review comment:
       Reviewed the logic. The success case is covered with `dev->rdsems = NULL;` from `timerfd_timeout_work`, so all the waiters are notified successfully and no need to perform any dequeue logic here.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 merged pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 merged pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014


   


-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r772137408



##########
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);

Review comment:
       In case of repeatable timers the `wdog` in restarted by
   ```
    if (dev->delay)
       {
         wd_start(&dev->wdog, dev->delay, timerfd_timeout, idev);
       }
   ```
   in `timerfd_timeout` so it is better to cancel the work here




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771788773



##########
File path: include/sys/timerfd.h
##########
@@ -0,0 +1,80 @@
+/****************************************************************************
+ * include/sys/timerfd.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_SYS_TIMERFD_H
+#define __INCLUDE_SYS_TIMERFD_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <fcntl.h>
+#include <time.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define TFD_NONBLOCK O_NONBLOCK
+#define TFD_CLOEXEC  O_CLOEXEC
+
+#define TFD_TIMER_ABSTIME TIMER_ABSTIME
+
+/* Get device minor number */
+
+#define TFD_FIOC_MINOR FIOC_MINOR
+
+/****************************************************************************
+ * Public Type Declarations
+ ****************************************************************************/
+
+/* Type for timer counter */
+
+typedef uint32_t timerfd_t;

Review comment:
       uint64_t doesn't always exist on all target, that's why uint32_t is used here. But, anyway uint32_t is enough in this case. BTW,  Linux use eventfd_t instead uint64_t which indicate that it expect to use the different type.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771641602



##########
File path: include/sys/syscall_lookup.h
##########
@@ -218,6 +218,11 @@ SYSCALL_LOOKUP(pwrite,                     4)
 #ifdef CONFIG_EVENT_FD
   SYSCALL_LOOKUP(eventfd,                  2)
 #endif
+#ifdef CONFIG_TIMER_FD
+  SYSCALL_LOOKUP(timerfd_create,           2)
+  SYSCALL_LOOKUP(timerfd_settime,          4)
+  SYSCALL_LOOKUP(timerfd_gettime,          2)

Review comment:
       Could you please point me to some example? I just followed `eventfd` as an example.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771825706



##########
File path: include/sys/timerfd.h
##########
@@ -0,0 +1,80 @@
+/****************************************************************************
+ * include/sys/timerfd.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_SYS_TIMERFD_H
+#define __INCLUDE_SYS_TIMERFD_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <fcntl.h>
+#include <time.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define TFD_NONBLOCK O_NONBLOCK
+#define TFD_CLOEXEC  O_CLOEXEC
+
+#define TFD_TIMER_ABSTIME TIMER_ABSTIME
+
+/* Get device minor number */
+
+#define TFD_FIOC_MINOR FIOC_MINOR
+
+/****************************************************************************
+ * Public Type Declarations
+ ****************************************************************************/
+
+/* Type for timer counter */
+
+typedef uint32_t timerfd_t;

Review comment:
       After reread eventfd and timerfd man, it look better to use uint64_t if possible. Since the value return from read may different from Linux, timerfd_t typedef is good to notice people there is some difference.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771980410



##########
File path: include/sys/syscall_lookup.h
##########
@@ -218,6 +218,11 @@ SYSCALL_LOOKUP(pwrite,                     4)
 #ifdef CONFIG_EVENT_FD
   SYSCALL_LOOKUP(eventfd,                  2)
 #endif
+#ifdef CONFIG_TIMER_FD
+  SYSCALL_LOOKUP(timerfd_create,           2)
+  SYSCALL_LOOKUP(timerfd_settime,          4)
+  SYSCALL_LOOKUP(timerfd_gettime,          2)

Review comment:
       Also `eventfd_read` and `eventfd_write` are `glibc` functions by definition. While `timerfd` APIs are a syscalls in Linux




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771996880



##########
File path: include/sys/syscall_lookup.h
##########
@@ -218,6 +218,11 @@ SYSCALL_LOOKUP(pwrite,                     4)
 #ifdef CONFIG_EVENT_FD
   SYSCALL_LOOKUP(eventfd,                  2)
 #endif
+#ifdef CONFIG_TIMER_FD
+  SYSCALL_LOOKUP(timerfd_create,           2)
+  SYSCALL_LOOKUP(timerfd_settime,          4)
+  SYSCALL_LOOKUP(timerfd_gettime,          2)

Review comment:
       Can this be done in 2 steps? I really do not mind to go this way, but since such change goes in another path compared to Linux and require additional retesting and significant rework (at least IMO) I would like to open an enhancement issue and involve some more people into discussion instead of doing changes in this  PR. @xiaoxiang781216 does this sounds reasonable?
   
   BTW going with
   ```
   struct timerfd_settime_s {
    int flags;
    struct itimerspec new_value;
    struct itimerspec old_value;
   };
   ```
   is not the best as `old_value` is optional parameter that can be `NULL`.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] xiaoxiang781216 commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771789163



##########
File path: fs/vfs/fs_timerfd.c
##########
@@ -0,0 +1,840 @@
+/****************************************************************************
+ * 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 "/dev"
+#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 TIMERFDWORK 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 */
+  size_t        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);
+static int timerfd_ioctl(FAR struct file *filep, int cmd,
+                         unsigned long arg);
+#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 size_t timerfd_get_unique_minor(void);
+static void timerfd_release_minor(size_t 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 */
+  timerfd_ioctl  /* 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(TIMERFDWORK, &dev->work);
+  nxsem_destroy(&dev->exclsem);
+  kmm_free(dev);
+}
+
+#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 size_t timerfd_get_unique_minor(void)
+{
+  static size_t minor;
+
+  return minor++;
+}
+
+static void timerfd_release_minor(size_t 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) + %d (3) + null char (1) */
+
+  char devpath[sizeof(CONFIG_TIMER_FD_VFS_PATH) + 4 + 3 + 1];

Review comment:
       I will fix eventfd too.




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [incubator-nuttx] pkarashchenko commented on a change in pull request #5014: fs/vfs: Add file descriptor based timers support

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on a change in pull request #5014:
URL: https://github.com/apache/incubator-nuttx/pull/5014#discussion_r771937530



##########
File path: include/sys/syscall_lookup.h
##########
@@ -218,6 +218,11 @@ SYSCALL_LOOKUP(pwrite,                     4)
 #ifdef CONFIG_EVENT_FD
   SYSCALL_LOOKUP(eventfd,                  2)
 #endif
+#ifdef CONFIG_TIMER_FD
+  SYSCALL_LOOKUP(timerfd_create,           2)
+  SYSCALL_LOOKUP(timerfd_settime,          4)
+  SYSCALL_LOOKUP(timerfd_gettime,          2)

Review comment:
       I see no problems with `timerfd_gettime` in such approach, but not sure about `timerfd_settime` that needs 3 arguments to be passed. In general `int flags, const struct itimerspec *new_value, struct itimerspec *old_value` should be packed into intermediate structure and pointer to that structure has to be passed to `ioctl` call. I also do not have any issues with that, but the question comes where the declaration of that intermediate type should be located? Should I call something like
   ```
   typedef struct
   {
     int flags;
     const struct itimerspec *new_value;
     struct itimerspec *old_value;
   } timerfd_settime_t;
   ```
   into `timerfd.h`? @xiaoxiang781216 what is your opinion?




-- 
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: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org