248 lines
8.0 KiB
Diff
248 lines
8.0 KiB
Diff
|
From e45be60a8f2e6148b40f358922a4f472fa0b2f8b Mon Sep 17 00:00:00 2001
|
||
|
From: Alexander Graf <agraf@suse.de>
|
||
|
Date: Thu, 1 Dec 2011 19:00:01 +0100
|
||
|
Subject: [PATCH 14/32] XXX work around SA_RESTART race with boehm-gc (ARM only)
|
||
|
|
||
|
---
|
||
|
linux-user/main.c | 25 ++++++++-----
|
||
|
linux-user/qemu.h | 3 ++
|
||
|
linux-user/signal.c | 22 ++++++++++++
|
||
|
linux-user/syscall.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++-
|
||
|
4 files changed, 133 insertions(+), 11 deletions(-)
|
||
|
|
||
|
diff --git a/linux-user/main.c b/linux-user/main.c
|
||
|
index 1cd8eb7..788ff98 100644
|
||
|
--- a/linux-user/main.c
|
||
|
+++ b/linux-user/main.c
|
||
|
@@ -818,15 +818,22 @@ void cpu_loop(CPUARMState *env)
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
- env->regs[0] = do_syscall(env,
|
||
|
- n,
|
||
|
- env->regs[0],
|
||
|
- env->regs[1],
|
||
|
- env->regs[2],
|
||
|
- env->regs[3],
|
||
|
- env->regs[4],
|
||
|
- env->regs[5],
|
||
|
- 0, 0);
|
||
|
+ TaskState *ts = ((CPUState*)env)->opaque;
|
||
|
+ target_ulong r;
|
||
|
+ r = do_syscall(env, n, env->regs[0], env->regs[1],
|
||
|
+ env->regs[2], env->regs[3], env->regs[4],
|
||
|
+ env->regs[5], 0, 0);
|
||
|
+ if ((r == -EINTR) && ts->signal_restart &&
|
||
|
+ syscall_restartable(n)) {
|
||
|
+ if (env->thumb) {
|
||
|
+ env->regs[15] -= 2;
|
||
|
+ } else {
|
||
|
+ env->regs[15] -= 4;
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ env->regs[0] = r;
|
||
|
+ }
|
||
|
+ ts->signal_restart = 0;
|
||
|
}
|
||
|
} else {
|
||
|
goto error;
|
||
|
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
|
||
|
index ef08d39..aa06acf 100644
|
||
|
--- a/linux-user/qemu.h
|
||
|
+++ b/linux-user/qemu.h
|
||
|
@@ -136,6 +136,8 @@ typedef struct TaskState {
|
||
|
struct sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */
|
||
|
struct sigqueue *first_free; /* first free siginfo queue entry */
|
||
|
int signal_pending; /* non zero if a signal may be pending */
|
||
|
+ int signal_in_syscall; /* non zero if we are in do_syscall() */
|
||
|
+ int signal_restart; /* non zero if we need to restart a syscall */
|
||
|
} __attribute__((aligned(16))) TaskState;
|
||
|
|
||
|
extern char *exec_path;
|
||
|
@@ -202,6 +204,7 @@ char *target_strerror(int err);
|
||
|
int get_osversion(void);
|
||
|
void fork_start(void);
|
||
|
void fork_end(int child);
|
||
|
+int syscall_restartable(int syscall_nr);
|
||
|
|
||
|
/* Return true if the proposed guest_base is suitable for the guest.
|
||
|
* The guest code may leave a page mapped and populate it if the
|
||
|
diff --git a/linux-user/signal.c b/linux-user/signal.c
|
||
|
index cfa92b9..b7b8bd8 100644
|
||
|
--- a/linux-user/signal.c
|
||
|
+++ b/linux-user/signal.c
|
||
|
@@ -25,6 +25,7 @@
|
||
|
#include <assert.h>
|
||
|
#include <sys/ucontext.h>
|
||
|
#include <sys/resource.h>
|
||
|
+#include <sched.h>
|
||
|
|
||
|
#include "qemu.h"
|
||
|
#include "qemu-common.h"
|
||
|
@@ -481,6 +482,11 @@ int queue_signal(CPUState *env, int sig, target_siginfo_t *info)
|
||
|
k->pending = 1;
|
||
|
/* signal that a new signal is pending */
|
||
|
ts->signal_pending = 1;
|
||
|
+ /* check if we have to restart the current syscall */
|
||
|
+ if ((sigact_table[sig - 1].sa_flags & SA_RESTART) &&
|
||
|
+ ts->signal_in_syscall) {
|
||
|
+ ts->signal_restart = 1;
|
||
|
+ }
|
||
|
return 1; /* indicates that the signal was queued */
|
||
|
}
|
||
|
}
|
||
|
@@ -613,8 +619,24 @@ int do_sigaction(int sig, const struct target_sigaction *act,
|
||
|
if (host_sig != SIGSEGV && host_sig != SIGBUS) {
|
||
|
sigfillset(&act1.sa_mask);
|
||
|
act1.sa_flags = SA_SIGINFO;
|
||
|
+#ifdef TARGET_ARM
|
||
|
+ /* Breaks boehm-gc, we have to do this manually */
|
||
|
+ /*
|
||
|
+ * Unfortunately our hacks only work as long as we don't do parallel
|
||
|
+ * signal delivery and futexes, so let's do a dirty hack here to
|
||
|
+ * pin our guest process to a single host CPU if we're using the
|
||
|
+ * boehm-gc.
|
||
|
+ */
|
||
|
+ if ((k->sa_flags & TARGET_SA_RESTART) && host_sig == SIGPWR) {
|
||
|
+ cpu_set_t mask;
|
||
|
+ CPU_ZERO(&mask);
|
||
|
+ CPU_SET(0, &mask);
|
||
|
+ sched_setaffinity(0, sizeof(mask), &mask);
|
||
|
+ }
|
||
|
+#else
|
||
|
if (k->sa_flags & TARGET_SA_RESTART)
|
||
|
act1.sa_flags |= SA_RESTART;
|
||
|
+#endif
|
||
|
/* NOTE: it is important to update the host kernel signal
|
||
|
ignore state to avoid getting unexpected interrupted
|
||
|
syscalls */
|
||
|
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
|
||
|
index 4af0edb..97c3303 100644
|
||
|
--- a/linux-user/syscall.c
|
||
|
+++ b/linux-user/syscall.c
|
||
|
@@ -4758,6 +4758,87 @@ static int do_open(void *cpu_env, const char *pathname, int flags, mode_t mode)
|
||
|
return get_errno(open(path(pathname), flags, mode));
|
||
|
}
|
||
|
|
||
|
+int syscall_restartable(int syscall_nr)
|
||
|
+{
|
||
|
+ switch (syscall_nr) {
|
||
|
+#ifdef TARGET_NR_sigsuspend
|
||
|
+ case TARGET_NR_sigsuspend:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_pause
|
||
|
+ case TARGET_NR_pause:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_setsockopt
|
||
|
+ case TARGET_NR_setsockopt:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_accept
|
||
|
+ case TARGET_NR_accept:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_recv
|
||
|
+ case TARGET_NR_recv:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_recvfrom
|
||
|
+ case TARGET_NR_recvfrom:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_recvmsg
|
||
|
+ case TARGET_NR_recvmsg:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_socketcall
|
||
|
+ case TARGET_NR_socketcall:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_connect
|
||
|
+ case TARGET_NR_connect:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_send
|
||
|
+ case TARGET_NR_send:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_sendmsg
|
||
|
+ case TARGET_NR_sendmsg:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_sendto
|
||
|
+ case TARGET_NR_sendto:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_poll
|
||
|
+ case TARGET_NR_poll:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_ppoll
|
||
|
+ case TARGET_NR_ppoll:
|
||
|
+#endif
|
||
|
+#if defined(TARGET_NR_select)
|
||
|
+ case TARGET_NR_select:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_pselect6
|
||
|
+ case TARGET_NR_pselect6:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR__newselect
|
||
|
+ case TARGET_NR__newselect:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_msgrcv
|
||
|
+ case TARGET_NR_msgrcv:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_msgsnd
|
||
|
+ case TARGET_NR_msgsnd:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_semop
|
||
|
+ case TARGET_NR_semop:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_ipc
|
||
|
+ case TARGET_NR_ipc:
|
||
|
+#endif
|
||
|
+#ifdef TARGET_NR_clock_nanosleep
|
||
|
+ case TARGET_NR_clock_nanosleep:
|
||
|
+#endif
|
||
|
+ case TARGET_NR_rt_sigsuspend:
|
||
|
+ case TARGET_NR_rt_sigtimedwait:
|
||
|
+ case TARGET_NR_nanosleep:
|
||
|
+ case TARGET_NR_close:
|
||
|
+ /* can not be restarted */
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* every other syscall can be restarted */
|
||
|
+ return 1;
|
||
|
+}
|
||
|
+
|
||
|
/* do_syscall() should always have a single exit point at the end so
|
||
|
that actions, such as logging of syscall results, can be performed.
|
||
|
All errnos that do_syscall() returns must be -TARGET_<errcode>. */
|
||
|
@@ -4770,6 +4851,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
||
|
struct stat st;
|
||
|
struct statfs stfs;
|
||
|
void *p;
|
||
|
+ TaskState *ts = ((CPUState*)cpu_env)->opaque;
|
||
|
+
|
||
|
+ if (!ts->signal_restart) {
|
||
|
+ /* remember syscall info for restart */
|
||
|
+ ts->signal_in_syscall = 1;
|
||
|
+ }
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
gemu_log("syscall %d", num);
|
||
|
@@ -7679,8 +7766,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
||
|
#endif
|
||
|
|
||
|
cmd = target_to_host_fcntl_cmd(arg2);
|
||
|
- if (cmd == -TARGET_EINVAL)
|
||
|
- return cmd;
|
||
|
+ if (cmd == -TARGET_EINVAL) {
|
||
|
+ ret = cmd;
|
||
|
+ goto fail;
|
||
|
+ }
|
||
|
|
||
|
switch(arg2) {
|
||
|
case TARGET_F_GETLK64:
|
||
|
@@ -8312,6 +8401,7 @@ fail:
|
||
|
#endif
|
||
|
if(do_strace)
|
||
|
print_syscall_ret(num, ret);
|
||
|
+ ts->signal_in_syscall = 0;
|
||
|
return ret;
|
||
|
efault:
|
||
|
ret = -TARGET_EFAULT;
|
||
|
--
|
||
|
1.6.0.2
|
||
|
|