summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSven Schnelle <svens@linux.ibm.com>2021-06-30 13:50:55 +0200
committerVasily Gorbik <gor@linux.ibm.com>2021-07-08 22:12:17 +0200
commite3c7a8d7f44f4b36eb299563526ef8c5cb8011b0 (patch)
tree52cfa082f382217276692a600044625c4c93768a
parentfbf50f47ea99d07aec59859027352d4837e84ce1 (diff)
s390: move restart of execve() syscall
On s390, execve might have to be restarted for PGSTE binaries like kvm. In the past this was done via the PIF_SYSCALL_RESTART bit. However, with the recent changes, syscalls are now restarted differently. Now that execve() is the only call that might get restarted via PIF_SYSCALL_RESTART, move the loop to do_syscall(). This also has the advantage that the restart is no longer visible to userspace. Signed-off-by: Sven Schnelle <svens@linux.ibm.com> Reviewed-by: Heiko Carstens <hca@linux.ibm.com> Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
-rw-r--r--arch/s390/include/asm/ptrace.h8
-rw-r--r--arch/s390/kernel/syscall.c27
2 files changed, 20 insertions, 15 deletions
diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h
index c7850d649373..808da21235ab 100644
--- a/arch/s390/include/asm/ptrace.h
+++ b/arch/s390/include/asm/ptrace.h
@@ -162,6 +162,14 @@ static inline int test_pt_regs_flag(struct pt_regs *regs, int flag)
return !!(regs->flags & (1UL << flag));
}
+static inline int test_and_clear_pt_regs_flag(struct pt_regs *regs, int flag)
+{
+ int ret = test_pt_regs_flag(regs, flag);
+
+ clear_pt_regs_flag(regs, flag);
+ return ret;
+}
+
/*
* These are defined as per linux/ptrace.h, which see.
*/
diff --git a/arch/s390/kernel/syscall.c b/arch/s390/kernel/syscall.c
index ec73d2c61e58..0322f00f84ac 100644
--- a/arch/s390/kernel/syscall.c
+++ b/arch/s390/kernel/syscall.c
@@ -134,13 +134,15 @@ static void do_syscall(struct pt_regs *regs)
* work, the ptrace code sets PIF_SYSCALL_RET_SET, which is checked here
* and if set, the syscall will be skipped.
*/
- if (!test_pt_regs_flag(regs, PIF_SYSCALL_RET_SET)) {
- regs->gprs[2] = -ENOSYS;
- if (likely(nr < NR_syscalls))
- regs->gprs[2] = current->thread.sys_call_table[nr](regs);
- } else {
- clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET);
- }
+ if (unlikely(test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET)))
+ goto out;
+ regs->gprs[2] = -ENOSYS;
+ if (likely(nr >= NR_syscalls))
+ goto out;
+ do {
+ regs->gprs[2] = current->thread.sys_call_table[nr](regs);
+ } while (test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RESTART));
+out:
syscall_exit_to_user_mode_work(regs);
}
@@ -158,13 +160,8 @@ void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
if (per_trap)
set_thread_flag(TIF_PER_TRAP);
- for (;;) {
- regs->flags = 0;
- set_pt_regs_flag(regs, PIF_SYSCALL);
- do_syscall(regs);
- if (!test_pt_regs_flag(regs, PIF_SYSCALL_RESTART))
- break;
- local_irq_enable();
- }
+ regs->flags = 0;
+ set_pt_regs_flag(regs, PIF_SYSCALL);
+ do_syscall(regs);
exit_to_user_mode();
}