Module: xenomai-gch Branch: for-forge Commit: 2d583b153e602bd959e2a9a9aee5740d3132da21 URL: http://git.xenomai.org/?p=xenomai-gch.git;a=commit;h=2d583b153e602bd959e2a9a9aee5740d3132da21
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