Module: xenomai-jki Branch: for-forge Commit: 29c69f003f41df94a1fc03c91d383ba728e05abd URL: http://git.xenomai.org/?p=xenomai-jki.git;a=commit;h=29c69f003f41df94a1fc03c91d383ba728e05abd
Author: Jan Kiszka <jan.kis...@siemens.com> Date: Fri May 13 20:35:26 2016 +0200 cobalt/kernel: Allow to restart clock_nanosleep and select after signal processing Only if a signal was actually delivered to a thread that was blocked on sleep, [clock_]nanosleep or select, those calls should return -EINTR. Otherwise, they should resume with the timeout, accordingly adjusted in case of relative timeout. So far we returned -EINTR immediately which particularly disturbed the debugging of applications (SIGSTOP/CONT terminated those syscalls). This approach reuses the Linux restart mechanism to find out if those syscalls should be restarted or actually terminated after the signal was handled: Linux sets current->restart_block.fn in case a termination is required, unconditionally, thus also when the syscall did not return ERESTART_RESTARTBLOCK. We also use the restart_block.nanosleep.expires to transfer the remaining timeout to the restarted syscall. We can't use the original restart mechanism of Linux because it directs all ERESTART_RESTARTBLOCK through a special, Linux-only syscall. In our case, we would have to migrate the caller in that context to primary in order to resume the sleep, but this is not possible under Xenomai (we need to migration from within the syscall hooks). Signed-off-by: Jan Kiszka <jan.kis...@siemens.com> --- include/cobalt/uapi/kernel/thread.h | 1 + .../cobalt/include/asm-generic/xenomai/wrappers.h | 6 +++ kernel/cobalt/posix/clock.c | 38 +++++++++++++++++-- kernel/cobalt/posix/internal.h | 2 + kernel/cobalt/posix/io.c | 39 ++++++++++++++++---- kernel/cobalt/posix/syscall.c | 5 +++ 6 files changed, 81 insertions(+), 10 deletions(-) diff --git a/include/cobalt/uapi/kernel/thread.h b/include/cobalt/uapi/kernel/thread.h index 8d26f16..1f1dca7 100644 --- a/include/cobalt/uapi/kernel/thread.h +++ b/include/cobalt/uapi/kernel/thread.h @@ -77,6 +77,7 @@ #define XNMOVED 0x00000001 /**< CPU migration in primary mode occurred */ #define XNLBALERT 0x00000002 /**< Scheduler lock break alert (SIGDEBUG sent) */ +#define XNRESTART 0x00000004 /**< Thread awaiting syscall restart after signal */ /** @} */ diff --git a/kernel/cobalt/include/asm-generic/xenomai/wrappers.h b/kernel/cobalt/include/asm-generic/xenomai/wrappers.h index 060ce85..0f9ab14 100644 --- a/kernel/cobalt/include/asm-generic/xenomai/wrappers.h +++ b/kernel/cobalt/include/asm-generic/xenomai/wrappers.h @@ -133,4 +133,10 @@ devm_hwmon_device_register_with_groups(struct device *dev, const char *name, #error "Xenomai/cobalt requires Linux kernel 3.10 or above" #endif /* < 3.10 */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) +#define cobalt_get_restart_block(p) (&task_thread_info(p)->restart_block) +#else +#define cobalt_get_restart_block(p) (&(p)->restart_block) +#endif + #endif /* _COBALT_ASM_GENERIC_WRAPPERS_H */ diff --git a/kernel/cobalt/posix/clock.c b/kernel/cobalt/posix/clock.c index b51cb4c..c977bf8 100644 --- a/kernel/cobalt/posix/clock.c +++ b/kernel/cobalt/posix/clock.c @@ -236,8 +236,9 @@ int __cobalt_clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqt, struct timespec *rmt) { + struct restart_block *restart; struct xnthread *cur; - xnsticks_t rem; + xnsticks_t timeout, rem; int ret = 0; spl_t s; @@ -261,10 +262,41 @@ int __cobalt_clock_nanosleep(clockid_t clock_id, int flags, xnlock_get_irqsave(&nklock, s); - xnthread_suspend(cur, XNDELAY, ts2ns(rqt) + 1, + if (xnthread_test_localinfo(cur, XNLBALERT)) { + xnthread_clear_localinfo(cur, XNLBALERT); + + restart = cobalt_get_restart_block(current); + + if (restart->fn != cobalt_restart_syscall_placeholder) { + xnlock_put_irqrestore(&nklock, s); + + if (rmt) + ns2ts(rmt, rem > 1 ? rem : 0); + return -EINTR; + } + + timeout = restart->nanosleep.expires; + } else + timeout = ts2ns(rqt); + + xnthread_suspend(cur, XNDELAY, timeout + 1, clock_flag(flags, clock_id), NULL); if (xnthread_test_info(cur, XNBREAK)) { + if (signal_pending(current)) { + xnthread_set_localinfo(cur, XNLBALERT); + + restart = cobalt_get_restart_block(current); + restart->fn = cobalt_restart_syscall_placeholder; + restart->nanosleep.expires = + (flags & TIMER_ABSTIME) ? timeout : + xntimer_get_timeout_stopped(&cur->rtimer); + + xnlock_put_irqrestore(&nklock, s); + + return -ERESTARTSYS; + } + if (flags == 0 && rmt) { rem = xntimer_get_timeout_stopped(&cur->rtimer); xnlock_put_irqrestore(&nklock, s); @@ -280,7 +312,7 @@ int __cobalt_clock_nanosleep(clockid_t clock_id, int flags, return ret; } -COBALT_SYSCALL(clock_nanosleep, nonrestartable, +COBALT_SYSCALL(clock_nanosleep, primary, (clockid_t clock_id, int flags, const struct timespec __user *u_rqt, struct timespec __user *u_rmt)) diff --git a/kernel/cobalt/posix/internal.h b/kernel/cobalt/posix/internal.h index ba33ebe..15dfc34 100644 --- a/kernel/cobalt/posix/internal.h +++ b/kernel/cobalt/posix/internal.h @@ -55,4 +55,6 @@ static inline xnhandle_t cobalt_get_handle_from_user(xnhandle_t *u_h) int cobalt_init(void); +long cobalt_restart_syscall_placeholder(struct restart_block *param); + #endif /* !_COBALT_POSIX_INTERNAL_H */ diff --git a/kernel/cobalt/posix/io.c b/kernel/cobalt/posix/io.c index 7110d21..bb2dfdd 100644 --- a/kernel/cobalt/posix/io.c +++ b/kernel/cobalt/posix/io.c @@ -170,7 +170,7 @@ int __cobalt_select_bind_all(struct xnselector *selector, } /* int select(int, fd_set *, fd_set *, fd_set *, struct timeval *) */ -COBALT_SYSCALL(select, nonrestartable, +COBALT_SYSCALL(select, primary, (int nfds, fd_set __user *u_rfds, fd_set __user *u_wfds, @@ -187,6 +187,7 @@ COBALT_SYSCALL(select, nonrestartable, fd_set in_fds_storage[XNSELECT_MAX_TYPES], out_fds_storage[XNSELECT_MAX_TYPES]; xnticks_t timeout = XN_INFINITE; + struct restart_block *restart; xntmode_t mode = XN_RELATIVE; struct xnselector *selector; struct xnthread *curr; @@ -197,14 +198,27 @@ COBALT_SYSCALL(select, nonrestartable, curr = xnthread_current(); if (u_tv) { - if (!access_wok(u_tv, sizeof(tv)) - || cobalt_copy_from_user(&tv, u_tv, sizeof(tv))) - return -EFAULT; + if (xnthread_test_localinfo(curr, XNLBALERT)) { + xnthread_clear_localinfo(curr, XNLBALERT); + + restart = cobalt_get_restart_block(current); + timeout = restart->nanosleep.expires; + + if (restart->fn != cobalt_restart_syscall_placeholder) { + err = -EINTR; + goto out; + } + } else { + if (!access_wok(u_tv, sizeof(tv)) + || cobalt_copy_from_user(&tv, u_tv, sizeof(tv))) + return -EFAULT; - if (tv.tv_usec > 1000000) - return -EINVAL; + if (tv.tv_usec > 1000000) + return -EINVAL; + + timeout = clock_get_ticks(CLOCK_MONOTONIC) + tv2ns(&tv); + } - timeout = clock_get_ticks(CLOCK_MONOTONIC) + tv2ns(&tv); mode = XN_ABSOLUTE; } @@ -253,6 +267,17 @@ COBALT_SYSCALL(select, nonrestartable, } } while (err == -ECHRNG); + if (err == -EINTR && signal_pending(current)) { + xnthread_set_localinfo(curr, XNLBALERT); + + restart = cobalt_get_restart_block(current); + restart->fn = cobalt_restart_syscall_placeholder; + restart->nanosleep.expires = timeout; + + return -ERESTARTSYS; + } + +out: if (u_tv && (err > 0 || err == -EINTR)) { xnsticks_t diff = timeout - clock_get_ticks(CLOCK_MONOTONIC); if (diff > 0) diff --git a/kernel/cobalt/posix/syscall.c b/kernel/cobalt/posix/syscall.c index 9893b30..8aede43 100644 --- a/kernel/cobalt/posix/syscall.c +++ b/kernel/cobalt/posix/syscall.c @@ -791,3 +791,8 @@ int ipipe_fastcall_hook(struct pt_regs *regs) return ret; } + +long cobalt_restart_syscall_placeholder(struct restart_block *param) +{ + return -EINVAL; +} _______________________________________________ Xenomai-git mailing list Xenomai-git@xenomai.org https://xenomai.org/mailman/listinfo/xenomai-git