summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Graf <agraf@suse.de>2011-12-01 19:00:01 +0100
committerAnas Nashif <anas.nashif@intel.com>2013-01-15 13:32:15 -0800
commit1144c1070b952b94f43a7ac00412992ebb09b390 (patch)
tree34fe5b7b9ddb5ff141fc7fdaf5751b59afbba1f5
parentde36bfa6b708e9d1311cfc55fb5a67e86864f6f1 (diff)
downloadqemu-1144c1070b952b94f43a7ac00412992ebb09b390.tar.gz
qemu-1144c1070b952b94f43a7ac00412992ebb09b390.tar.bz2
qemu-1144c1070b952b94f43a7ac00412992ebb09b390.zip
XXX work around SA_RESTART race with boehm-gc (ARM only)
[AF: CPUState -> CPUArchState, adapt to reindentation]
-rw-r--r--linux-user/main.c25
-rw-r--r--linux-user/qemu.h3
-rw-r--r--linux-user/signal.c22
-rw-r--r--linux-user/syscall.c90
4 files changed, 130 insertions, 10 deletions
diff --git a/linux-user/main.c b/linux-user/main.c
index c339af83e..c4d10ac1d 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -834,15 +834,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 = ((CPUArchState*)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 5e53dca09..7cc7b876f 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -137,6 +137,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;
@@ -203,6 +205,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);
/* Creates the initial guest address space in the host memory space using
* the given host start address hint and size. The guest_start parameter
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 4758c11b3..703a9f526 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"
@@ -502,6 +503,11 @@ int queue_signal(CPUArchState *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 */
}
}
@@ -642,8 +648,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 e4291ed77..05d48097f 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -5134,6 +5134,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>. */
@@ -5146,6 +5227,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
struct stat st;
struct statfs stfs;
void *p;
+ TaskState *ts = ((CPUArchState*)cpu_env)->opaque;
+
+ if (!ts->signal_restart) {
+ /* remember syscall info for restart */
+ ts->signal_in_syscall = 1;
+ }
#ifdef DEBUG
gemu_log("syscall %d", num);
@@ -8154,7 +8241,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
cmd = target_to_host_fcntl_cmd(arg2);
if (cmd == -TARGET_EINVAL) {
ret = cmd;
- break;
+ goto fail;
}
switch(arg2) {
@@ -8911,6 +8998,7 @@ fail:
#endif
if(do_strace)
print_syscall_ret(num, ret);
+ ts->signal_in_syscall = 0;
return ret;
efault:
ret = -TARGET_EFAULT;