Module: xenomai-gch
Branch: for-forge
Commit: 7d42421f5e9534a583499a6fee44b50d4d937208
URL:    
http://git.xenomai.org/?p=xenomai-gch.git;a=commit;h=7d42421f5e9534a583499a6fee44b50d4d937208

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

cobalt: add timerfd services

---

 include/cobalt/sys/timerfd.h           |   42 +++++
 include/cobalt/uapi/syscall.h          |    4 +-
 include/cobalt/uapi/time.h             |    8 +
 kernel/cobalt/posix/Makefile           |    3 +-
 kernel/cobalt/posix/internal.h         |    1 +
 kernel/cobalt/posix/syscall.c          |    4 +
 kernel/cobalt/posix/timer.c            |   72 +++++---
 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 ++++++++
 testsuite/regression/posix/Makefile.am |    5 +-
 testsuite/regression/posix/timerfd.c   |  294 +++++++++++++++++++++++++++++++
 15 files changed, 796 insertions(+), 31 deletions(-)

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 125cb75..0f6d27c 100644
--- a/include/cobalt/uapi/syscall.h
+++ b/include/cobalt/uapi/syscall.h
@@ -78,7 +78,9 @@
 #define sc_cobalt_sigtimedwait         55
 #define sc_cobalt_sigpending           56
 #define sc_cobalt_kill                 57
-/* 58-60 unimplemented */
+#define sc_cobalt_timerfd_create       58
+#define sc_cobalt_timerfd_settime      59
+#define sc_cobalt_timerfd_gettime      60
 #define sc_cobalt_mutexattr_init        61
 #define sc_cobalt_mutexattr_destroy     62
 #define sc_cobalt_mutexattr_gettype     63
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 595e737..358a35a 100644
--- a/kernel/cobalt/posix/Makefile
+++ b/kernel/cobalt/posix/Makefile
@@ -17,6 +17,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 777daa6..710b2bb 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 43a1904..a82a8ce 100644
--- a/kernel/cobalt/posix/syscall.c
+++ b/kernel/cobalt/posix/syscall.c
@@ -40,6 +40,7 @@
 #include "event.h"
 #include "select.h"
 #include "fdio.h"
+#include "timerfd.h"
 
 int cobalt_muxid;
 
@@ -137,6 +138,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, any),
+       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 cbb9938..e3ef15e 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"
 
@@ -303,6 +302,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)
@@ -318,19 +332,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;
 
        /* First, try offloading the work to an extension. */
@@ -344,19 +378,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.
@@ -370,12 +391,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..0a50d3e
--- /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/fd.h>
+#include <cobalt/kernel/timer.h>
+#include <cobalt/kernel/select.h>
+#include "internal.h"
+#include "clock.h"
+#include "timer.h"
+#include "timerfd.h"
+
+struct cobalt_tfd {
+       int flags;
+       clockid_t clockid;
+       struct xnfd xnfd;
+       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 xnfd *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, xnfd);
+
+       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 xnfd *fd, struct xnselector *selector, 
+               unsigned type, unsigned index)
+{
+       struct cobalt_tfd *tfd = container_of(fd, struct cobalt_tfd, xnfd);
+       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 xnfd *fd)
+{
+       struct cobalt_tfd *tfd = container_of(fd, struct cobalt_tfd, xnfd);
+       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 xnfd_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 xnfd_enter(p, &tfd->xnfd, ufd, COBALT_TIMERFD_MAGIC, 
+                       &timerfd_ops);
+}
+
+static inline struct cobalt_tfd *tfd_get(int fd)
+{
+       struct xnfd *xnfd;
+       
+       xnfd = xnfd_get(xnsys_ppd_get(0), fd, COBALT_TIMERFD_MAGIC);
+       if (IS_ERR(xnfd)) {
+               int err = PTR_ERR(xnfd);
+               if (err == -EBADF && cobalt_process_context() == NULL)
+                       err = -EPERM;
+               return ERR_PTR(err);
+       }
+       
+       return container_of(xnfd, struct cobalt_tfd, xnfd);
+}
+
+static inline void tfd_put(struct cobalt_tfd *tfd)
+{
+       xnfd_put(&tfd->xnfd);
+}
+
+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 dae8c11..2dcb62a 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/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