@@ -150,6 +150,8 @@ extern int vfp_restore_user_hwstate(struct user_vfp *,
#define TIF_USING_IWMMXT 17
#define TIF_MEMDIE 18 /* is terminating due to OOM killer */
#define TIF_RESTORE_SIGMASK 19
+#define TIF_LOCAL_RESTART 20
+#define TIF_LOCAL_RESTART_BLOCK 21
#define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
@@ -157,6 +159,8 @@ extern int vfp_restore_user_hwstate(struct user_vfp *,
#define _TIF_UPROBE (1 << TIF_UPROBE)
#define _TIF_NOTIFY_SIGNAL (1 << TIF_NOTIFY_SIGNAL)
#define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT)
+#define _TIF_LOCAL_RESTART (1 << TIF_LOCAL_RESTART)
+#define _TIF_LOCAL_RESTART_BLOCK (1 << TIF_LOCAL_RESTART_BLOCK)
#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
_TIF_NOTIFY_RESUME | _TIF_UPROBE | \
@@ -541,7 +541,8 @@ void arch_do_signal_or_restart(struct pt_regs *regs)
unsigned int retval = 0, continue_addr = 0, restart_addr = 0;
bool syscall = (syscall_get_nr(current, regs) != -1);
struct ksignal ksig;
- int restart = 0;
+ bool restart = false;
+ bool restart_block = false;
/*
* If we were from a system call, check for system call restarting...
@@ -557,12 +558,12 @@ void arch_do_signal_or_restart(struct pt_regs *regs)
*/
switch (retval) {
case -ERESTART_RESTARTBLOCK:
- restart -= 2;
+ restart_block = true;
fallthrough;
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR:
- restart++;
+ restart = true;
regs->ARM_r0 = regs->ARM_ORIG_r0;
regs->ARM_pc = restart_addr;
break;
@@ -593,8 +594,16 @@ void arch_do_signal_or_restart(struct pt_regs *regs)
} else {
/* no handler */
restore_saved_sigmask();
- if (unlikely(restart) && regs->ARM_pc == restart_addr)
+ if (unlikely(restart) && regs->ARM_pc == restart_addr) {
+ /*
+ * These flags will be picked up in the syscall invocation code,
+ * and a local restart will be issued without exiting the kernel.
+ */
+ set_thread_flag(TIF_LOCAL_RESTART);
+ if (restart_block)
+ set_thread_flag(TIF_LOCAL_RESTART_BLOCK);
regs->ARM_pc = continue_addr;
+ }
}
return;
}
@@ -11,6 +11,7 @@ __visible void invoke_syscall(void *table, struct pt_regs *regs, int scno)
{
int ret;
+local_restart:
scno = syscall_enter_from_user_mode(regs, scno);
/* When tracing syscall -1 means "skip syscall" */
if (scno < 0) {
@@ -34,4 +35,25 @@ __visible void invoke_syscall(void *table, struct pt_regs *regs, int scno)
syscall_set_return_value(current, regs, 0, ret);
syscall_exit_to_user_mode(regs);
+
+ /*
+ * Handle local restart: this means that when generic entry
+ * calls arch_do_signal_or_restart() because a signal to
+ * restart the syscall arrived while processing a system call,
+ * we set these flags for the thread so that we don't even
+ * exit the kernel, we just restart right here and clear
+ * the restart condition.
+ *
+ * This is done because of signal race issues on ARM.
+ */
+ if (test_thread_flag(TIF_LOCAL_RESTART)) {
+ if (test_thread_flag(TIF_LOCAL_RESTART_BLOCK)) {
+ scno = __NR_restart_syscall - __NR_SYSCALL_BASE;
+ /* Make this change visible to tracers */
+ task_thread_info(current)->abi_syscall = scno;
+ clear_thread_flag(TIF_LOCAL_RESTART_BLOCK);
+ }
+ clear_thread_flag(TIF_LOCAL_RESTART);
+ goto local_restart;
+ }
}
The former local restart hack to restart syscalls inside the kernel if we get restart signals while processing a system call was deleted when converting the architecture to generic entry. This makes strace tests fail so the hack is necessary. Now, after the conversion to generic entry, restore the order by reimplementing this with two TIF flags that help us to issue system call restarts immediately in the kernel. This is essentially a reimplementation of commit 81783786d5cf "ARM: 7473/1: deal with handlerless restarts without leaving the kernel" from 2012, but in C, on top of generic entry. Link: http://lists.infradead.org/pipermail/linux-arm-kernel/2012-June/104733.html Link: https://lore.kernel.org/all/1340377626-17075-1-git-send-email-will.deacon@arm.com/ Signed-off-by: Linus Walleij <linus.walleij@linaro.org> --- arch/arm/include/asm/thread_info.h | 4 ++++ arch/arm/kernel/signal.c | 17 +++++++++++++---- arch/arm/kernel/syscall.c | 22 ++++++++++++++++++++++ 3 files changed, 39 insertions(+), 4 deletions(-)