Module: xenomai-gch Branch: for-forge Commit: 9cde6b3196d1ade667494d1ca5953b0328064651 URL: http://git.xenomai.org/?p=xenomai-gch.git;a=commit;h=9cde6b3196d1ade667494d1ca5953b0328064651
Author: Gilles Chanteperdrix <gilles.chanteperd...@xenomai.org> Date: Fri Dec 27 12:25:40 2013 +0100 cobalt/timerfd: implement a timerfd_settime TFD_WAKEUP flag allowing to use timerfds as a general timeout mechanism --- include/cobalt/sys/timerfd.h | 3 +- include/cobalt/uapi/time.h | 8 +++ kernel/cobalt/posix/timerfd.c | 32 ++++++++-- testsuite/regression/posix/Makefile.am | 2 +- testsuite/regression/posix/timerfd.c | 101 ++++++++++++++++++++++++++++++++ 5 files changed, 140 insertions(+), 6 deletions(-) diff --git a/include/cobalt/sys/timerfd.h b/include/cobalt/sys/timerfd.h index 82f08bb..a7df836 100644 --- a/include/cobalt/sys/timerfd.h +++ b/include/cobalt/sys/timerfd.h @@ -21,6 +21,7 @@ #pragma GCC system_header #include_next <sys/timerfd.h> #include <cobalt/wrappers.h> +#include <cobalt/uapi/time.h> #ifdef __cplusplus extern "C" { @@ -38,4 +39,4 @@ COBALT_DECL(int, timerfd_gettime(int fd, struct itimerspec *curr_value)); } #endif /* __cplusplus */ -#endif /* TIMERFD_H */ +#endif /* _COBALT_SYS_TIMERFD_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/timerfd.c b/kernel/cobalt/posix/timerfd.c index 292666c..4f05d97 100644 --- a/kernel/cobalt/posix/timerfd.c +++ b/kernel/cobalt/posix/timerfd.c @@ -33,10 +33,13 @@ struct cobalt_tfd { 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 *xnfd, void __user *buf, size_t size) { unsigned long long __user *u_ticks; @@ -69,6 +72,8 @@ static ssize_t timerfd_read(struct xnfd *xnfd, void __user *buf, size_t size) 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; @@ -114,7 +119,7 @@ static int timerfd_select_bind(struct xnfd *xnfd, struct xnselector *selector, 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)); + tfd->flags & COBALT_TFD_TICKED); xnlock_put_irqrestore(&nklock, s); return err; @@ -151,6 +156,8 @@ static void timerfd_handler(struct xntimer *xntimer) 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) @@ -177,6 +184,7 @@ int cobalt_timerfd_create(int ufd, int clockid, int flags) 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(&tfd->xnfd, COBALT_TIMERFD_MAGIC, ufd, p, &timerfd_ops); @@ -208,9 +216,13 @@ int cobalt_timerfd_settime(int fd, int flags, { 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); @@ -221,20 +233,32 @@ int cobalt_timerfd_settime(int fd, int flags, goto out; } - flags = (flags & TFD_TIMER_ABSTIME) ? TIMER_ABSTIME : 0; - + 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); err = cobalt_xntimer_settime(&tfd->timer, - clock_flag(flags, tfd->clockid), &value); + 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; diff --git a/testsuite/regression/posix/Makefile.am b/testsuite/regression/posix/Makefile.am index 951db44..1a534a0 100644 --- a/testsuite/regression/posix/Makefile.am +++ b/testsuite/regression/posix/Makefile.am @@ -9,7 +9,7 @@ test_PROGRAMS = \ 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 index 58c55b7..6e9e0c1 100644 --- a/testsuite/regression/posix/timerfd.c +++ b/testsuite/regression/posix/timerfd.c @@ -21,6 +21,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#undef NDEBUG #include <stdio.h> #include <unistd.h> #include <errno.h> @@ -181,6 +182,104 @@ static void timerfd_select_overruns2_check(void) 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(); @@ -188,6 +287,8 @@ int main(void) 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