Module: xenomai-forge
Branch: master
Commit: a67c4eb81298bb7c9122e0a53b3e9c6c26fc9019
URL:    
http://git.xenomai.org/?p=xenomai-forge.git;a=commit;h=a67c4eb81298bb7c9122e0a53b3e9c6c26fc9019

Author: Gilles Chanteperdrix <gilles.chanteperd...@xenomai.org>
Date:   Thu Dec 26 22:29:04 2013 +0100

cobalt: add timerfd services

---

 doc/asciidoc/MIGRATION.adoc            |    7 +
 include/cobalt/sys/timerfd.h           |   42 +++++
 include/cobalt/uapi/syscall.h          |    3 +
 include/cobalt/uapi/time.h             |    8 +
 kernel/cobalt/posix/Makefile           |    3 +-
 kernel/cobalt/posix/internal.h         |    1 +
 kernel/cobalt/posix/syscall.c          |    5 +
 kernel/cobalt/posix/timer.c            |   70 +++++---
 kernel/cobalt/posix/timer.h            |    7 +
 kernel/cobalt/posix/timerfd.c          |  296 ++++++++++++++++++++++++++++++++
 kernel/cobalt/posix/timerfd.h          |   14 ++
 lib/cobalt/Makefile.am                 |    1 +
 lib/cobalt/cobalt.wrappers             |    3 +
 lib/cobalt/timerfd.c                   |   73 ++++++++
 lib/copperplate/regd/regd.c            |   28 +--
 testsuite/regression/posix/Makefile.am |    5 +-
 testsuite/regression/posix/timerfd.c   |  294 +++++++++++++++++++++++++++++++
 17 files changed, 817 insertions(+), 43 deletions(-)

diff --git a/doc/asciidoc/MIGRATION.adoc b/doc/asciidoc/MIGRATION.adoc
index e6fa554..cd2a1a4 100644
--- a/doc/asciidoc/MIGRATION.adoc
+++ b/doc/asciidoc/MIGRATION.adoc
@@ -598,6 +598,13 @@ int pthread_make_periodic_np(pthread_t thread, clockid_t 
clk_id,
     struct timespec *starttp, struct timespec *periodtp);
 ---------------------------------------------------------------
 
+.Timerfd
+
+[normal] Cobalt replacements for +timerfd_create()+,
++timerfd_settime()+ and +timerfd_gettime()+ have been introduced. The
+implementation delivers I/O notifications to RTDM file descriptors
+upon Cobalt-originated real-time signals.
+
 .Message queues
 
 - +mq_open()+ default attributes align on the regular kernel values,
diff --git a/include/cobalt/sys/timerfd.h b/include/cobalt/sys/timerfd.h
new file mode 100644
index 0000000..a7df836
--- /dev/null
+++ b/include/cobalt/sys/timerfd.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 Gilles Chanteperdrix <gilles.chanteperd...@xenomai.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
+ */
+#ifndef _COBALT_SYS_TIMERFD_H
+#define _COBALT_SYS_TIMERFD_H
+
+#pragma GCC system_header
+#include_next <sys/timerfd.h>
+#include <cobalt/wrappers.h>
+#include <cobalt/uapi/time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+COBALT_DECL(int, timerfd_create(int clockid, int flags));
+
+COBALT_DECL(int, timerfd_settime(int fd, int flags,
+               const struct itimerspec *new_value,
+               struct itimerspec *old_value));
+
+COBALT_DECL(int, timerfd_gettime(int fd, struct itimerspec *curr_value));
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _COBALT_SYS_TIMERFD_H */
diff --git a/include/cobalt/uapi/syscall.h b/include/cobalt/uapi/syscall.h
index f18659a..57a6b65 100644
--- a/include/cobalt/uapi/syscall.h
+++ b/include/cobalt/uapi/syscall.h
@@ -115,5 +115,8 @@
 #define sc_cobalt_event_destroy         92
 #define sc_cobalt_sched_setconfig_np   93
 #define sc_cobalt_sched_getconfig_np   94
+#define sc_cobalt_timerfd_create       95
+#define sc_cobalt_timerfd_settime      96
+#define sc_cobalt_timerfd_gettime      97
 
 #endif /* !_COBALT_UAPI_SYSCALL_H */
diff --git a/include/cobalt/uapi/time.h b/include/cobalt/uapi/time.h
index 349d659..764c74c 100644
--- a/include/cobalt/uapi/time.h
+++ b/include/cobalt/uapi/time.h
@@ -39,4 +39,12 @@
 
 #define CLOCK_HOST_REALTIME  __COBALT_CLOCK_CODE(42)
 
+/*
+ * Additional timerfd defines
+ *
+ * when passing TFD_WAKEUP to timer_settime, any timer expiration
+ * unblocks the thread having issued timer_settime.
+ */
+#define TFD_WAKEUP     (1 << 2)
+
 #endif /* !_COBALT_UAPI_TIME_H */
diff --git a/kernel/cobalt/posix/Makefile b/kernel/cobalt/posix/Makefile
index 40c682e..9d77f72 100644
--- a/kernel/cobalt/posix/Makefile
+++ b/kernel/cobalt/posix/Makefile
@@ -16,6 +16,7 @@ posix-y :=            \
        signal.o        \
        syscall.o       \
        thread.o        \
-       timer.o
+       timer.o         \
+       timerfd.o
 
 ccflags-y := -Iarch/$(SRCARCH)/xenomai/include -Iinclude/xenomai
diff --git a/kernel/cobalt/posix/internal.h b/kernel/cobalt/posix/internal.h
index 642ca05..393fe22 100644
--- a/kernel/cobalt/posix/internal.h
+++ b/kernel/cobalt/posix/internal.h
@@ -43,6 +43,7 @@
 #define COBALT_TIMER_MAGIC       COBALT_MAGIC(0E)
 #define COBALT_EVENT_MAGIC       COBALT_MAGIC(0F)
 #define COBALT_MONITOR_MAGIC     COBALT_MAGIC(10)
+#define COBALT_TIMERFD_MAGIC    COBALT_MAGIC(11)
 
 #define cobalt_obj_active(h,m,t)                       \
        ((h) && ((t *)(h))->magic == (m))
diff --git a/kernel/cobalt/posix/syscall.c b/kernel/cobalt/posix/syscall.c
index 922471c..e41d3dc 100644
--- a/kernel/cobalt/posix/syscall.c
+++ b/kernel/cobalt/posix/syscall.c
@@ -27,6 +27,7 @@
 #include <cobalt/kernel/tree.h>
 #include <asm/xenomai/syscall.h>
 #include <rtdm/driver.h>
+#include <rtdm/fd.h>
 #include "internal.h"
 #include "thread.h"
 #include "mutex.h"
@@ -39,6 +40,7 @@
 #include "clock.h"
 #include "event.h"
 #include "select.h"
+#include "timerfd.h"
 
 int cobalt_muxid;
 
@@ -138,6 +140,9 @@ static struct xnsyscall cobalt_syscalls[] = {
        SKINCALL_DEF(sc_cobalt_timer_settime, cobalt_timer_settime, primary),
        SKINCALL_DEF(sc_cobalt_timer_gettime, cobalt_timer_gettime, any),
        SKINCALL_DEF(sc_cobalt_timer_getoverrun, cobalt_timer_getoverrun, any),
+       SKINCALL_DEF(sc_cobalt_timerfd_create, cobalt_timerfd_create, lostage),
+       SKINCALL_DEF(sc_cobalt_timerfd_gettime, cobalt_timerfd_gettime, any),
+       SKINCALL_DEF(sc_cobalt_timerfd_settime, cobalt_timerfd_settime, any),
        SKINCALL_DEF(sc_cobalt_mutexattr_init, cobalt_mutexattr_init, any),
        SKINCALL_DEF(sc_cobalt_mutexattr_destroy, cobalt_mutexattr_destroy, 
any),
        SKINCALL_DEF(sc_cobalt_mutexattr_gettype, cobalt_mutexattr_gettype, 
any),
diff --git a/kernel/cobalt/posix/timer.c b/kernel/cobalt/posix/timer.c
index 5ef0696..4c9acbf 100644
--- a/kernel/cobalt/posix/timer.c
+++ b/kernel/cobalt/posix/timer.c
@@ -26,7 +26,6 @@
 #include <linux/err.h>
 #include "internal.h"
 #include "thread.h"
-#include "signal.h"
 #include "timer.h"
 #include "clock.h"
 
@@ -306,6 +305,21 @@ out:
        return ret;
 }
 
+void cobalt_xntimer_gettime(struct xntimer *__restrict__ timer,
+                       struct itimerspec *__restrict__ value)
+{
+       if (!xntimer_running_p(timer)) {
+               value->it_value.tv_sec = 0;
+               value->it_value.tv_nsec = 0;
+               value->it_interval.tv_sec = 0;
+               value->it_interval.tv_nsec = 0;
+               return;
+       }
+
+       ns2ts(&value->it_value, xntimer_get_timeout(timer));
+       ns2ts(&value->it_interval, xntimer_interval(timer));
+}
+
 static inline void
 timer_gettimeout(struct cobalt_timer *__restrict__ timer,
                 struct itimerspec *__restrict__ value)
@@ -321,19 +335,39 @@ timer_gettimeout(struct cobalt_timer *__restrict__ timer,
        }
 
        if (!cobalt_call_extension(timer_gettime, &timer->extref,
-                                  ret, value) || ret == 0) {
-               ns2ts(&value->it_value,
-                     xntimer_get_timeout(&timer->timerbase));
-               ns2ts(&value->it_interval,
-                     xntimer_interval(&timer->timerbase));
+                                  ret, value) || ret == 0)
+               cobalt_xntimer_gettime(&timer->timerbase, value);
+}
+
+int cobalt_xntimer_settime(struct xntimer *__restrict__ timer, int clock_flag,
+                       const struct itimerspec *__restrict__ value)
+{
+       xnticks_t start, period;
+
+       if (value->it_value.tv_nsec == 0 && value->it_value.tv_sec == 0) {
+               xntimer_stop(timer);
+               return 0;
        }
+
+       if ((unsigned long)value->it_value.tv_nsec >= ONE_BILLION ||
+           ((unsigned long)value->it_interval.tv_nsec >= ONE_BILLION &&
+            (value->it_value.tv_sec != 0 || value->it_value.tv_nsec != 0)))
+               return -EINVAL;
+
+       start = ts2ns(&value->it_value) + 1;
+       period = ts2ns(&value->it_interval);
+
+       /*
+        * Now start the timer. If the timeout data has already
+        * passed, the caller will handle the case.
+        */
+       return xntimer_start(timer, start, period, clock_flag);
 }
 
 static inline int timer_set(struct cobalt_timer *timer, int flags,
                            const struct itimerspec *__restrict__ value)
 {                              /* nklocked, IRQs off. */
        struct cobalt_thread *thread;
-       xnticks_t start, period;
        int ret = 0;
 
        /* First, try offloading the work to an extension. */
@@ -347,19 +381,6 @@ static inline int timer_set(struct cobalt_timer *timer, 
int flags,
         * POSIX behavior.
         */
 
-       if (value->it_value.tv_nsec == 0 && value->it_value.tv_sec == 0) {
-               xntimer_stop(&timer->timerbase);
-               return 0;
-       }
-
-       if ((unsigned long)value->it_value.tv_nsec >= ONE_BILLION ||
-           ((unsigned long)value->it_interval.tv_nsec >= ONE_BILLION &&
-            (value->it_value.tv_sec != 0 || value->it_value.tv_nsec != 0)))
-               return -EINVAL;
-
-       start = ts2ns(&value->it_value) + 1;
-       period = ts2ns(&value->it_interval);
-
        /*
         * If the target thread vanished, simply don't start the
         * timer.
@@ -373,12 +394,9 @@ static inline int timer_set(struct cobalt_timer *timer, 
int flags,
         * signaled.
         */
        xntimer_set_sched(&timer->timerbase, thread->threadbase.sched);
-       /*
-        * Now start the timer. If the timeout data has already
-        * passed, the caller will handle the case.
-        */
-       return xntimer_start(&timer->timerbase, start, period,
-                            clock_flag(flags, timer->clockid));
+
+       return cobalt_xntimer_settime(&timer->timerbase,
+                               clock_flag(flags, timer->clockid), value);
 }
 
 static inline void
diff --git a/kernel/cobalt/posix/timer.h b/kernel/cobalt/posix/timer.h
index 999f414..7e8f4c8 100644
--- a/kernel/cobalt/posix/timer.h
+++ b/kernel/cobalt/posix/timer.h
@@ -22,6 +22,7 @@
 #include <linux/time.h>
 #include <linux/list.h>
 #include <cobalt/kernel/timer.h>
+#include "signal.h"
 
 struct cobalt_thread;
 struct cobalt_kqueues;
@@ -63,6 +64,12 @@ static inline timer_t cobalt_timer_id(const struct 
cobalt_timer *timer)
 struct cobalt_timer *
 cobalt_timer_by_id(struct cobalt_process *p, timer_t timer_id);
 
+void cobalt_xntimer_gettime(struct xntimer *__restrict__ timer, 
+                       struct itimerspec *__restrict__ value);
+
+int cobalt_xntimer_settime(struct xntimer *__restrict__ timer, int clock_flag, 
+                       const struct itimerspec *__restrict__ value);
+
 void cobalt_timer_handler(struct xntimer *xntimer);
 
 #endif /* !_COBALT_POSIX_TIMER_H */
diff --git a/kernel/cobalt/posix/timerfd.c b/kernel/cobalt/posix/timerfd.c
new file mode 100644
index 0000000..2c1fdd8
--- /dev/null
+++ b/kernel/cobalt/posix/timerfd.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2013 Gilles Chanteperdrix <gilles.chanteperd...@xenomai.org>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/timerfd.h>
+#include <cobalt/kernel/timer.h>
+#include <cobalt/kernel/select.h>
+#include <rtdm/fd.h>
+#include "internal.h"
+#include "clock.h"
+#include "timer.h"
+#include "timerfd.h"
+
+struct cobalt_tfd {
+       int flags;
+       clockid_t clockid;
+       struct rtdm_fd fd;
+       struct xntimer timer;
+       DECLARE_XNSELECT(read_select);
+       struct itimerspec value;
+       struct xnsynch readers;
+       struct xnthread *target;
+};
+
+#define COBALT_TFD_TICKED      (1 << 2)
+
+#define COBALT_TFD_SETTIME_FLAGS (TFD_TIMER_ABSTIME | TFD_WAKEUP)
+
+static ssize_t timerfd_read(struct rtdm_fd *fd, void __user *buf, size_t size)
+{
+       unsigned long long __user *u_ticks;
+       unsigned long long ticks;
+       struct cobalt_tfd *tfd;
+       bool aligned;
+       spl_t s;
+       int err;
+
+       if (size < sizeof(ticks))
+               return -EINVAL;
+
+       u_ticks = buf;
+       aligned = (((unsigned long)buf) & (sizeof(ticks) - 1)) == 0;
+
+       tfd = container_of(fd, struct cobalt_tfd, fd);
+
+       xnlock_get_irqsave(&nklock, s);
+       if (tfd->flags & COBALT_TFD_TICKED) {
+               err = 0;
+               goto out;
+       }
+       if (tfd->flags & TFD_NONBLOCK) {
+               err = -EAGAIN;
+               goto out;
+       }
+
+       do {
+               err = xnsynch_sleep_on(&tfd->readers, XN_INFINITE, XN_RELATIVE);
+       } while (err == 0 && (tfd->flags & COBALT_TFD_TICKED) == 0);
+
+       if (err & XNBREAK)
+               err = -EINTR;
+  out:
+       if (err == 0) {
+               xnticks_t now;
+
+               if (xntimer_interval(&tfd->timer)) {
+                       now = xnclock_read_raw(xntimer_clock(&tfd->timer));
+                       ticks = 1 + xntimer_get_overruns(&tfd->timer, now);
+               } else
+                       ticks = 1;
+
+               tfd->flags &= ~COBALT_TFD_TICKED;
+               xnselect_signal(&tfd->read_select, 0);
+       }
+       xnlock_put_irqrestore(&nklock, s);
+
+       if (err == 0) {
+               if (aligned)
+                       err = __xn_put_user(ticks, u_ticks);
+               else
+                       err = __xn_copy_to_user(buf, &ticks, sizeof(ticks));
+               if (err)
+                       err = -EFAULT;
+       }
+
+       return err ?: sizeof(ticks);
+}
+
+static int
+timerfd_select_bind(struct rtdm_fd *fd, struct xnselector *selector,
+               unsigned type, unsigned index)
+{
+       struct cobalt_tfd *tfd = container_of(fd, struct cobalt_tfd, fd);
+       struct xnselect_binding *binding;
+       spl_t s;
+       int err;
+
+       if (type != XNSELECT_READ)
+               return -EBADF;
+
+       binding = xnmalloc(sizeof(*binding));
+       if (binding == NULL)
+               return -ENOMEM;
+
+       xnlock_get_irqsave(&nklock, s);
+       xntimer_set_sched(&tfd->timer, xnsched_current());
+       err = xnselect_bind(&tfd->read_select, binding, selector, type,
+                       index, tfd->flags & COBALT_TFD_TICKED);
+       xnlock_put_irqrestore(&nklock, s);
+
+       return err;
+}
+
+static void timerfd_close(struct rtdm_fd *fd)
+{
+       struct cobalt_tfd *tfd = container_of(fd, struct cobalt_tfd, fd);
+       int resched;
+       spl_t s;
+
+       xnlock_get_irqsave(&nklock, s);
+       xntimer_destroy(&tfd->timer);
+       resched = xnsynch_destroy(&tfd->readers) == XNSYNCH_RESCHED;
+       xnlock_put_irqrestore(&nklock, s);
+       xnselect_destroy(&tfd->read_select);
+       xnfree(tfd);
+
+       if (resched)
+               xnsched_run();
+}
+
+static struct rtdm_fd_ops timerfd_ops = {
+       .read_rt = timerfd_read,
+       .select_bind = timerfd_select_bind,
+       .close = timerfd_close,
+};
+
+static void timerfd_handler(struct xntimer *xntimer)
+{
+       struct cobalt_tfd *tfd;
+
+       tfd = container_of(xntimer, struct cobalt_tfd, timer);
+       tfd->flags |= COBALT_TFD_TICKED;
+       xnselect_signal(&tfd->read_select, 1);
+       xnsynch_wakeup_one_sleeper(&tfd->readers);
+       if (tfd->target)
+               xnthread_unblock(tfd->target);
+}
+
+int cobalt_timerfd_create(int ufd, int clockid, int flags)
+{
+       struct cobalt_tfd *tfd;
+       struct xnsys_ppd *p;
+
+       p = xnsys_ppd_get(0);
+       if (p == &__xnsys_global_ppd)
+               return -EPERM;
+
+       if (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC)
+               return -EINVAL;
+
+       if (flags & ~TFD_CREATE_FLAGS)
+               return -EINVAL;
+
+       tfd = xnmalloc(sizeof(*tfd));
+       if (tfd == NULL)
+               return -ENOMEM;
+
+       tfd->flags = flags;
+       tfd->clockid = clockid;
+       xntimer_init(&tfd->timer, &nkclock, timerfd_handler, NULL);
+       xnsynch_init(&tfd->readers, XNSYNCH_PRIO | XNSYNCH_NOPIP, NULL);
+       xnselect_init(&tfd->read_select);
+       tfd->target = NULL;
+
+       return rtdm_fd_enter(p, &tfd->fd, ufd, COBALT_TIMERFD_MAGIC,
+                       &timerfd_ops);
+}
+
+static inline struct cobalt_tfd *tfd_get(int ufd)
+{
+       struct rtdm_fd *fd;
+
+       fd = rtdm_fd_get(xnsys_ppd_get(0), ufd, COBALT_TIMERFD_MAGIC);
+       if (IS_ERR(fd)) {
+               int err = PTR_ERR(fd);
+               if (err == -EBADF && cobalt_process_context() == NULL)
+                       err = -EPERM;
+               return ERR_PTR(err);
+       }
+
+       return container_of(fd, struct cobalt_tfd, fd);
+}
+
+static inline void tfd_put(struct cobalt_tfd *tfd)
+{
+       rtdm_fd_put(&tfd->fd);
+}
+
+int cobalt_timerfd_settime(int fd, int flags,
+                       const struct itimerspec __user *new_value,
+                       struct itimerspec __user *old_value)
+{
+       struct itimerspec ovalue, value;
+       struct cobalt_tfd *tfd;
+       int cflag;
+       int err;
+       spl_t s;
+
+       if (flags & ~COBALT_TFD_SETTIME_FLAGS)
+               return -EINVAL;
+
+       tfd = tfd_get(fd);
+       if (IS_ERR(tfd))
+               return PTR_ERR(tfd);
+
+       if (!new_value ||
+               __xn_copy_from_user(&value, new_value, sizeof(value))) {
+               err = -EFAULT;
+               goto out;
+       }
+
+       cflag = (flags & TFD_TIMER_ABSTIME) ? TIMER_ABSTIME : 0;
+
+       xnlock_get_irqsave(&nklock, s);
+
+       if (flags & TFD_WAKEUP) {
+               tfd->target = xnshadow_thread(current);
+               if (tfd->target == NULL) {
+                       err = -EPERM;
+                       goto out_unlock;
+               }
+       } else
+               tfd->target = NULL;
+
+       if (old_value)
+               cobalt_xntimer_gettime(&tfd->timer, &ovalue);
+
+       xntimer_set_sched(&tfd->timer, xnsched_current());
+
+       err = cobalt_xntimer_settime(&tfd->timer,
+                               clock_flag(cflag, tfd->clockid), &value);
+  out_unlock:
+       xnlock_put_irqrestore(&nklock, s);
+
+       if (err == 0 && old_value &&
+               __xn_copy_to_user(old_value, &ovalue, sizeof(ovalue))) {
+               xnlock_get_irqsave(&nklock, s);
+               xntimer_stop(&tfd->timer);
+               tfd->target = NULL;
+               xnlock_put_irqrestore(&nklock, s);
+
+               err = -EFAULT;
+       }
+
+  out:
+       tfd_put(tfd);
+
+       return err;
+}
+
+int cobalt_timerfd_gettime(int fd, struct itimerspec __user *curr_value)
+{
+       struct itimerspec value;
+       struct cobalt_tfd *tfd;
+       int err = 0;
+       spl_t s;
+
+       tfd = tfd_get(fd);
+       if (IS_ERR(tfd))
+               return PTR_ERR(tfd);
+
+       xnlock_get_irqsave(&nklock, s);
+       cobalt_xntimer_gettime(&tfd->timer, &value);
+       xnlock_put_irqrestore(&nklock, s);
+
+       if (!curr_value || __xn_copy_to_user(curr_value, &value, sizeof(value)))
+               err = -EFAULT;
+
+       tfd_put(tfd);
+
+       return err;
+}
diff --git a/kernel/cobalt/posix/timerfd.h b/kernel/cobalt/posix/timerfd.h
new file mode 100644
index 0000000..12535e4
--- /dev/null
+++ b/kernel/cobalt/posix/timerfd.h
@@ -0,0 +1,14 @@
+#ifndef TIMERFD_H
+#define TIMERFD_H
+
+#include <linux/time.h>
+
+int cobalt_timerfd_create(int fd, int clockid, int flags);
+
+int cobalt_timerfd_settime(int fd, int flags,
+                       const struct itimerspec __user *new_value,
+                       struct itimerspec __user *old_value);
+
+int cobalt_timerfd_gettime(int fd, struct itimerspec __user *curr_value);
+
+#endif /* TIMERFD_H */
diff --git a/lib/cobalt/Makefile.am b/lib/cobalt/Makefile.am
index 9f87929..9f3edec 100644
--- a/lib/cobalt/Makefile.am
+++ b/lib/cobalt/Makefile.am
@@ -34,6 +34,7 @@ libcobalt_la_SOURCES =                \
        thread.c                \
        ticks.c                 \
        timer.c                 \
+       timerfd.c               \
        trace.c                 \
        wrappers.c
 
diff --git a/lib/cobalt/cobalt.wrappers b/lib/cobalt/cobalt.wrappers
index ecbfe94..8964648 100644
--- a/lib/cobalt/cobalt.wrappers
+++ b/lib/cobalt/cobalt.wrappers
@@ -85,6 +85,9 @@
 --wrap timer_settime
 --wrap timer_getoverrun
 --wrap timer_gettime
+--wrap timerfd_create
+--wrap timerfd_gettime
+--wrap timerfd_settime
 --wrap select
 --wrap vfprintf
 --wrap vprintf
diff --git a/lib/cobalt/timerfd.c b/lib/cobalt/timerfd.c
new file mode 100644
index 0000000..a975127
--- /dev/null
+++ b/lib/cobalt/timerfd.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 Gilles Chanteperdrix <gilles.chanteperd...@xenomai.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
+ */
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/timerfd.h>
+#include <asm/xenomai/syscall.h>
+#include "internal.h"
+
+COBALT_IMPL(int, timerfd_create, (int clockid, int flags))
+{
+       int ret;
+       int fd;
+
+       fd = __STD(open("/dev/null", O_RDWR, 0));
+       if (fd == -1)
+               return fd;
+
+       ret = -XENOMAI_SKINCALL3(__cobalt_muxid, 
+                               sc_cobalt_timerfd_create,
+                               fd, clockid, flags);
+       if (ret == 0)
+               return fd;
+       
+       __STD(close(fd));
+       errno = ret;
+       return -1;
+}
+
+COBALT_IMPL(int, timerfd_settime, (int fd, int flags,
+               const struct itimerspec *new_value,
+               struct itimerspec *old_value))
+{
+       int ret;
+       
+       ret = -XENOMAI_SKINCALL4(__cobalt_muxid,
+                               sc_cobalt_timerfd_settime,
+                               fd, flags, new_value, old_value);
+       if (ret == 0)
+               return ret;
+       
+       errno = ret;
+       return -1;
+}
+
+COBALT_IMPL(int, timerfd_gettime, (int fd, struct itimerspec *curr_value))
+{
+       int ret;
+       
+       ret = -XENOMAI_SKINCALL2(__cobalt_muxid,
+                               sc_cobalt_timerfd_gettime,
+                               fd, curr_value);
+       if (ret == 0)
+               return ret;
+       
+       errno = ret;
+       return -1;
+}
diff --git a/lib/copperplate/regd/regd.c b/lib/copperplate/regd/regd.c
index 547f71d..d5249f2 100644
--- a/lib/copperplate/regd/regd.c
+++ b/lib/copperplate/regd/regd.c
@@ -173,7 +173,7 @@ static void bind_socket(void)
        socklen_t addrlen;
        int ret;
 
-       sockfd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+       sockfd = __STD(socket(AF_UNIX, SOCK_SEQPACKET, 0));
        if (sockfd < 0)
                error(1, errno, "bind_socket/socket");
 
@@ -183,14 +183,14 @@ static void bind_socket(void)
        snprintf(sun.sun_path, sizeof(sun.sun_path), "X%X-xenomai", hash);
        addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(sun.sun_path);
        sun.sun_path[0] = '\0';
-       ret = bind(sockfd, (struct sockaddr *)&sun, addrlen);
+       ret = __STD(bind(sockfd, (struct sockaddr *)&sun, addrlen));
        if (ret) {
                if (errno == EADDRINUSE)
                        exit(0);
                error(1, errno, "bind_socket/bind");
        }
 
-       ret = listen(sockfd, SOMAXCONN);
+       ret = __STD(listen(sockfd, SOMAXCONN));
        if (ret)
                error(1, errno, "bind_socket/listen");
 }
@@ -204,7 +204,7 @@ static int register_client(int s)
        int ret;
 
        optlen = sizeof(ucred);
-       ret = getsockopt(s, SOL_SOCKET, SO_PEERCRED, &ucred, &optlen);
+       ret = __STD(getsockopt(s, SOL_SOCKET, SO_PEERCRED, &ucred, &optlen));
        if (ret)
                return -errno;
 
@@ -231,7 +231,7 @@ static int register_client(int s)
        note("created mount point %s", mountpt);
 
        /* Send the mount point back to the client. */
-       ret = send(s, mountpt, strlen(mountpt) + 1, 0);
+       ret = __STD(send(s, mountpt, strlen(mountpt) + 1, 0));
        if (ret < 0)
                goto fail;
 
@@ -283,7 +283,7 @@ static void handle_requests(void)
        FD_SET(sockfd, &refset);
 
        if (!linger) {
-               tmfd = timerfd_create(CLOCK_MONOTONIC, 0);
+               tmfd = __STD(timerfd_create(CLOCK_MONOTONIC, 0));
                if (tmfd < 0)
                        error(1, errno, "handle_requests/timerfd_create");
                /* Silently exit after 30s being idle. */
@@ -291,30 +291,30 @@ static void handle_requests(void)
                its.it_value.tv_nsec = 0;
                its.it_interval.tv_sec = 30;
                its.it_interval.tv_nsec = 0;
-               timerfd_settime(tmfd, 0, &its, NULL);
+               __STD(timerfd_settime(tmfd, 0, &its, NULL));
                FD_SET(tmfd, &refset);
        }
 
        for (;;) {
                set = refset;
-               ret = select(FD_SETSIZE, &set, NULL, NULL, NULL);
+               ret = __STD(select(FD_SETSIZE, &set, NULL, NULL, NULL));
                if (ret < 0)
                        error(1, errno, "handle_requests/select");
                if (FD_ISSET(sockfd, &set)) {
-                       s = accept(sockfd, NULL, 0);
+                       s = __STD(accept(sockfd, NULL, 0));
                        if (s < 0)
                                error(1, errno, "handle_requests/accept");
                        ret = register_client(s);
                        if (ret) {
-                               close(s);
+                               __STD(close(s));
                                continue;
                        }
                        FD_SET(s, &refset);
                        if (!linger)
-                               timerfd_settime(tmfd, 0, &its, NULL);
+                               __STD(timerfd_settime(tmfd, 0, &its, NULL));
                }
                if (!linger && FD_ISSET(tmfd, &set)) {
-                       ret = read(tmfd, &exp, sizeof(exp));
+                       ret = __STD(read(tmfd, &exp, sizeof(exp)));
                        (void)ret;
                        if (pvlist_empty(&client_list)) {
                                delete_system_fs();
@@ -324,10 +324,10 @@ static void handle_requests(void)
                for (s = sockfd + 1; s < FD_SETSIZE; s++) {
                        if (!FD_ISSET(s, &set) || linger || s == tmfd)
                                continue;
-                       ret = recv(s, &c, sizeof(c), 0);
+                       ret = __STD(recv(s, &c, sizeof(c), 0));
                        if (ret <= 0) {
                                unregister_client(s);
-                               close(s);
+                               __STD(close(s));
                                FD_CLR(s, &refset);
                        }
                }
diff --git a/testsuite/regression/posix/Makefile.am 
b/testsuite/regression/posix/Makefile.am
index af2c246..1a534a0 100644
--- a/testsuite/regression/posix/Makefile.am
+++ b/testsuite/regression/posix/Makefile.am
@@ -6,9 +6,10 @@ noinst_HEADERS = check.h
 
 test_PROGRAMS = \
        leaks \
-       mq_select
+       mq_select \
+       timerfd
 
-CPPFLAGS = $(XENO_USER_CFLAGS)                                 \
+CPPFLAGS = $(XENO_USER_CFLAGS)                 \
        -I$(top_srcdir)/include
 
 LDFLAGS = $(XENO_POSIX_WRAPPERS)
diff --git a/testsuite/regression/posix/timerfd.c 
b/testsuite/regression/posix/timerfd.c
new file mode 100644
index 0000000..6e9e0c1
--- /dev/null
+++ b/testsuite/regression/posix/timerfd.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2013 Gilles Chanteperdrix <g...@xenomai.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *  
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#undef NDEBUG
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/timerfd.h>
+#include "check.h"
+
+static void timerfd_basic_check(void)
+{
+       struct itimerspec its;
+       int fd, i;
+       
+       check_unix(fd = timerfd_create(CLOCK_MONOTONIC, 0));
+
+       its.it_value.tv_sec = 0;
+       its.it_value.tv_nsec = 100000000;
+       its.it_interval.tv_sec = 0;
+       its.it_interval.tv_nsec = 100000000;
+
+       check_unix(timerfd_settime(fd, 0, &its, NULL));
+       
+       for (i = 0; i < 10; i++) {
+               unsigned long long ticks;
+               
+               assert(check_unix(read(fd, &ticks, sizeof(ticks))) == 8);
+               fprintf(stderr, "%Ld direct read ticks\n", ticks);
+               assert(ticks >= 1);
+       }
+       
+       close(fd);
+}
+
+static void timerfd_select_check(void)
+{
+       unsigned long long ticks;
+       struct itimerspec its;
+       fd_set inset;
+       int fd, i;
+       
+       check_unix(fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK));
+
+       FD_ZERO(&inset);
+       FD_SET(fd, &inset);
+
+       its.it_value.tv_sec = 0;
+       its.it_value.tv_nsec = 100000000;
+       its.it_interval.tv_sec = 0;
+       its.it_interval.tv_nsec = 100000000;
+
+       check_unix(timerfd_settime(fd, 0, &its, NULL));
+       assert(read(fd, &ticks, sizeof(ticks)) == -1 && errno == EAGAIN);
+       
+       for (i = 0; i < 10; i++) {
+               fd_set tmp_inset = inset;
+
+               check_unix(select(fd + 1, &tmp_inset, NULL, NULL, NULL));
+               
+               assert(check_unix(read(fd, &ticks, sizeof(ticks))) == 8);
+               fprintf(stderr, "%Ld select+read ticks\n", ticks);
+               assert(ticks >= 1);
+       }
+       
+       close(fd);
+}
+
+static void timerfd_basic_overruns_check(void)
+{
+       struct itimerspec its;
+       int fd, i;
+       
+       check_unix(fd = timerfd_create(CLOCK_MONOTONIC, 0));
+
+       its.it_value.tv_sec = 0;
+       its.it_value.tv_nsec = 100000000;
+       its.it_interval.tv_sec = 0;
+       its.it_interval.tv_nsec = 100000000;
+
+       check_unix(timerfd_settime(fd, 0, &its, NULL));
+       
+       for (i = 0; i < 3; i++) {
+               unsigned long long ticks;
+               
+               sleep(1);
+               assert(check_unix(read(fd, &ticks, sizeof(ticks))) == 8);
+               fprintf(stderr, "%Ld direct read ticks\n", ticks);
+               assert(ticks >= 10);
+       }
+       
+       close(fd);
+}
+
+static void timerfd_select_overruns_check(void)
+{
+       unsigned long long ticks;
+       struct itimerspec its;
+       fd_set inset;
+       int fd, i;
+       
+       check_unix(fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK));
+
+       FD_ZERO(&inset);
+       FD_SET(fd, &inset);
+
+       its.it_value.tv_sec = 0;
+       its.it_value.tv_nsec = 100000000;
+       its.it_interval.tv_sec = 0;
+       its.it_interval.tv_nsec = 100000000;
+
+       check_unix(timerfd_settime(fd, 0, &its, NULL));
+       assert(read(fd, &ticks, sizeof(ticks)) == -1 && errno == EAGAIN);
+       
+       for (i = 0; i < 3; i++) {
+               fd_set tmp_inset = inset;
+
+               sleep(1);
+               check_unix(select(fd + 1, &tmp_inset, NULL, NULL, NULL));
+               
+               assert(check_unix(read(fd, &ticks, sizeof(ticks))) == 8);
+               fprintf(stderr, "%Ld select+read ticks\n", ticks);
+               assert(ticks >= 10);
+       }
+       
+       close(fd);
+}
+
+static void timerfd_select_overruns2_check(void)
+{
+       unsigned long long ticks;
+       struct itimerspec its;
+       fd_set inset;
+       int fd, i;
+       
+       check_unix(fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK));
+
+       FD_ZERO(&inset);
+       FD_SET(fd, &inset);
+
+       its.it_value.tv_sec = 0;
+       its.it_value.tv_nsec = 100000000;
+       its.it_interval.tv_sec = 0;
+       its.it_interval.tv_nsec = 100000000;
+
+       check_unix(timerfd_settime(fd, 0, &its, NULL));
+       assert(read(fd, &ticks, sizeof(ticks)) == -1 && errno == EAGAIN);
+       
+       for (i = 0; i < 3; i++) {
+               fd_set tmp_inset = inset;
+
+               check_unix(select(fd + 1, &tmp_inset, NULL, NULL, NULL));
+
+               sleep(1);
+               
+               assert(check_unix(read(fd, &ticks, sizeof(ticks))) == 8);
+               fprintf(stderr, "%Ld select+read ticks\n", ticks);
+               assert(ticks >= 11);
+       }
+       
+       close(fd);
+}
+
+static void timerfd_select_overruns_before_check(void)
+{
+       unsigned long long ticks;
+       struct itimerspec its;
+       fd_set inset;
+       int fd, i;
+       
+       check_unix(fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK));
+
+       FD_ZERO(&inset);
+       FD_SET(fd, &inset);
+
+       its.it_value.tv_sec = 0;
+       its.it_value.tv_nsec = 100000000;
+       its.it_interval.tv_sec = 0;
+       its.it_interval.tv_nsec = 100000000;
+
+       check_unix(timerfd_settime(fd, 0, &its, NULL));
+       assert(read(fd, &ticks, sizeof(ticks)) == -1 && errno == EAGAIN);
+
+       sleep(1);
+
+       for (i = 0; i < 3; i++) {
+               fd_set tmp_inset = inset;
+
+               check_unix(select(fd + 1, &tmp_inset, NULL, NULL, NULL));
+
+               assert(check_unix(read(fd, &ticks, sizeof(ticks))) == 8);
+               fprintf(stderr, "%Ld select+read ticks\n", ticks);
+               assert(ticks >= 10);
+               sleep(1);
+       }
+       
+       close(fd);
+}
+
+static ssize_t
+timed_read(int fd, void *buf, size_t len, struct timespec *ts)
+{
+       struct itimerspec its;
+       ssize_t err;
+       int tfd;
+       
+       check_unix(tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK));
+       
+       its.it_value = *ts;
+       its.it_interval.tv_sec = 0;
+       its.it_interval.tv_nsec = 0;
+       
+       check_unix(timerfd_settime(tfd, TFD_WAKEUP, &its, NULL));
+       
+       err = read(fd, buf, len);
+       if (err < 0)
+               err = -errno;
+       if (err == -EINTR) {
+               unsigned long long ticks;
+               
+               err = read(tfd, &ticks, sizeof(ticks));
+               if (err > 0)
+                       err = -ETIMEDOUT;
+               else
+                       err = -EINTR;
+       }
+       
+       check_unix(close(tfd));
+
+       if (err >= 0)
+               return err;
+       
+       errno = -err;
+       return -1;
+}
+
+static void timerfd_unblock_check(void)
+{
+       unsigned long long ticks;
+       struct itimerspec its;
+       int fd;
+       
+       check_unix(fd = timerfd_create(CLOCK_MONOTONIC, 0));
+       
+       its.it_value.tv_sec = 5;
+       its.it_value.tv_nsec = 0;
+       its.it_interval.tv_sec = 0;
+       its.it_interval.tv_nsec = 0;
+       
+       check_unix(timerfd_settime(fd, 0, &its, NULL));
+
+       its.it_value.tv_sec = 0;
+       its.it_value.tv_nsec = 100000000;
+
+       assert(timed_read(fd, &ticks, sizeof(ticks), &its.it_value) < 0 && 
+               errno == ETIMEDOUT);
+
+       check_unix(close(fd));
+}
+
+
+int main(void)
+{
+       timerfd_basic_check();
+       timerfd_select_check();
+       timerfd_basic_overruns_check();
+       timerfd_select_overruns_check();
+       timerfd_select_overruns2_check();
+       timerfd_select_overruns_before_check();
+       timerfd_unblock_check();
+
+       return 0;
+}


_______________________________________________
Xenomai-git mailing list
Xenomai-git@xenomai.org
http://www.xenomai.org/mailman/listinfo/xenomai-git

Reply via email to