aboutsummaryrefslogtreecommitdiff
path: root/cpus.c
diff options
context:
space:
mode:
authorAnthony Liguori <aliguori@us.ibm.com>2011-02-16 08:47:07 -0600
committerAnthony Liguori <aliguori@us.ibm.com>2011-02-16 08:47:07 -0600
commitc5d69e6bbf37bf5e3882060764b15e018e6a5321 (patch)
treee246e96bdfffbc5e07682c5a39ea7a65482a5935 /cpus.c
parent630ecca0da959e65acad1ee0bf3a631bbb7ee052 (diff)
parent0ec329dab938e2d97d12a91f8ed15fec27b325e0 (diff)
downloadqemu-c5d69e6bbf37bf5e3882060764b15e018e6a5321.zip
qemu-c5d69e6bbf37bf5e3882060764b15e018e6a5321.tar.gz
qemu-c5d69e6bbf37bf5e3882060764b15e018e6a5321.tar.bz2
Merge remote branch 'qemu-kvm/uq/master' into staging
Diffstat (limited to 'cpus.c')
-rw-r--r--cpus.c725
1 files changed, 452 insertions, 273 deletions
diff --git a/cpus.c b/cpus.c
index 2d92090..0f33945 100644
--- a/cpus.c
+++ b/cpus.c
@@ -34,9 +34,6 @@
#include "cpus.h"
#include "compatfd.h"
-#ifdef CONFIG_LINUX
-#include <sys/prctl.h>
-#endif
#ifdef SIGRTMIN
#define SIG_IPI (SIGRTMIN+4)
@@ -44,10 +41,24 @@
#define SIG_IPI SIGUSR1
#endif
+#ifdef CONFIG_LINUX
+
+#include <sys/prctl.h>
+
#ifndef PR_MCE_KILL
#define PR_MCE_KILL 33
#endif
+#ifndef PR_MCE_KILL_SET
+#define PR_MCE_KILL_SET 1
+#endif
+
+#ifndef PR_MCE_KILL_EARLY
+#define PR_MCE_KILL_EARLY 1
+#endif
+
+#endif /* CONFIG_LINUX */
+
static CPUState *next_cpu;
/***********************************************************/
@@ -119,45 +130,117 @@ static void do_vm_stop(int reason)
static int cpu_can_run(CPUState *env)
{
- if (env->stop)
+ if (env->stop) {
return 0;
- if (env->stopped || !vm_running)
+ }
+ if (env->stopped || !vm_running) {
return 0;
+ }
return 1;
}
-static int cpu_has_work(CPUState *env)
+static bool cpu_thread_is_idle(CPUState *env)
{
- if (env->stop)
- return 1;
- if (env->queued_work_first)
- return 1;
- if (env->stopped || !vm_running)
- return 0;
- if (!env->halted)
- return 1;
- if (qemu_cpu_has_work(env))
- return 1;
- return 0;
+ if (env->stop || env->queued_work_first) {
+ return false;
+ }
+ if (env->stopped || !vm_running) {
+ return true;
+ }
+ if (!env->halted || qemu_cpu_has_work(env)) {
+ return false;
+ }
+ return true;
}
-static int any_cpu_has_work(void)
+static bool all_cpu_threads_idle(void)
{
CPUState *env;
- for (env = first_cpu; env != NULL; env = env->next_cpu)
- if (cpu_has_work(env))
- return 1;
- return 0;
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ if (!cpu_thread_is_idle(env)) {
+ return false;
+ }
+ }
+ return true;
}
-static void cpu_debug_handler(CPUState *env)
+static CPUDebugExcpHandler *debug_excp_handler;
+
+CPUDebugExcpHandler *cpu_set_debug_excp_handler(CPUDebugExcpHandler *handler)
+{
+ CPUDebugExcpHandler *old_handler = debug_excp_handler;
+
+ debug_excp_handler = handler;
+ return old_handler;
+}
+
+static void cpu_handle_debug_exception(CPUState *env)
{
+ CPUWatchpoint *wp;
+
+ if (!env->watchpoint_hit) {
+ QTAILQ_FOREACH(wp, &env->watchpoints, entry) {
+ wp->flags &= ~BP_WATCHPOINT_HIT;
+ }
+ }
+ if (debug_excp_handler) {
+ debug_excp_handler(env);
+ }
+
gdb_set_stop_cpu(env);
- debug_requested = EXCP_DEBUG;
- vm_stop(EXCP_DEBUG);
+ qemu_system_debug_request();
+#ifdef CONFIG_IOTHREAD
+ env->stopped = 1;
+#endif
}
+#ifdef CONFIG_LINUX
+static void sigbus_reraise(void)
+{
+ sigset_t set;
+ struct sigaction action;
+
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_DFL;
+ if (!sigaction(SIGBUS, &action, NULL)) {
+ raise(SIGBUS);
+ sigemptyset(&set);
+ sigaddset(&set, SIGBUS);
+ sigprocmask(SIG_UNBLOCK, &set, NULL);
+ }
+ perror("Failed to re-raise SIGBUS!\n");
+ abort();
+}
+
+static void sigbus_handler(int n, struct qemu_signalfd_siginfo *siginfo,
+ void *ctx)
+{
+ if (kvm_on_sigbus(siginfo->ssi_code,
+ (void *)(intptr_t)siginfo->ssi_addr)) {
+ sigbus_reraise();
+ }
+}
+
+static void qemu_init_sigbus(void)
+{
+ struct sigaction action;
+
+ memset(&action, 0, sizeof(action));
+ action.sa_flags = SA_SIGINFO;
+ action.sa_sigaction = (void (*)(int, siginfo_t*, void*))sigbus_handler;
+ sigaction(SIGBUS, &action, NULL);
+
+ prctl(PR_MCE_KILL, PR_MCE_KILL_SET, PR_MCE_KILL_EARLY, 0, 0);
+}
+
+#else /* !CONFIG_LINUX */
+
+static void qemu_init_sigbus(void)
+{
+}
+#endif /* !CONFIG_LINUX */
+
#ifndef _WIN32
static int io_thread_fd = -1;
@@ -167,9 +250,9 @@ static void qemu_event_increment(void)
static const uint64_t val = 1;
ssize_t ret;
- if (io_thread_fd == -1)
+ if (io_thread_fd == -1) {
return;
-
+ }
do {
ret = write(io_thread_fd, &val, sizeof(val));
} while (ret < 0 && errno == EINTR);
@@ -200,17 +283,17 @@ static int qemu_event_init(void)
int fds[2];
err = qemu_eventfd(fds);
- if (err == -1)
+ if (err == -1) {
return -errno;
-
+ }
err = fcntl_setfl(fds[0], O_NONBLOCK);
- if (err < 0)
+ if (err < 0) {
goto fail;
-
+ }
err = fcntl_setfl(fds[1], O_NONBLOCK);
- if (err < 0)
+ if (err < 0) {
goto fail;
-
+ }
qemu_set_fd_handler2(fds[0], NULL, qemu_event_read, NULL,
(void *)(unsigned long)fds[0]);
@@ -222,7 +305,109 @@ fail:
close(fds[1]);
return err;
}
-#else
+
+static void dummy_signal(int sig)
+{
+}
+
+/* If we have signalfd, we mask out the signals we want to handle and then
+ * use signalfd to listen for them. We rely on whatever the current signal
+ * handler is to dispatch the signals when we receive them.
+ */
+static void sigfd_handler(void *opaque)
+{
+ int fd = (unsigned long) opaque;
+ struct qemu_signalfd_siginfo info;
+ struct sigaction action;
+ ssize_t len;
+
+ while (1) {
+ do {
+ len = read(fd, &info, sizeof(info));
+ } while (len == -1 && errno == EINTR);
+
+ if (len == -1 && errno == EAGAIN) {
+ break;
+ }
+
+ if (len != sizeof(info)) {
+ printf("read from sigfd returned %zd: %m\n", len);
+ return;
+ }
+
+ sigaction(info.ssi_signo, NULL, &action);
+ if ((action.sa_flags & SA_SIGINFO) && action.sa_sigaction) {
+ action.sa_sigaction(info.ssi_signo,
+ (siginfo_t *)&info, NULL);
+ } else if (action.sa_handler) {
+ action.sa_handler(info.ssi_signo);
+ }
+ }
+}
+
+static int qemu_signalfd_init(sigset_t mask)
+{
+ int sigfd;
+
+ sigfd = qemu_signalfd(&mask);
+ if (sigfd == -1) {
+ fprintf(stderr, "failed to create signalfd\n");
+ return -errno;
+ }
+
+ fcntl_setfl(sigfd, O_NONBLOCK);
+
+ qemu_set_fd_handler2(sigfd, NULL, sigfd_handler, NULL,
+ (void *)(unsigned long) sigfd);
+
+ return 0;
+}
+
+static void qemu_kvm_eat_signals(CPUState *env)
+{
+ struct timespec ts = { 0, 0 };
+ siginfo_t siginfo;
+ sigset_t waitset;
+ sigset_t chkset;
+ int r;
+
+ sigemptyset(&waitset);
+ sigaddset(&waitset, SIG_IPI);
+ sigaddset(&waitset, SIGBUS);
+
+ do {
+ r = sigtimedwait(&waitset, &siginfo, &ts);
+ if (r == -1 && !(errno == EAGAIN || errno == EINTR)) {
+ perror("sigtimedwait");
+ exit(1);
+ }
+
+ switch (r) {
+ case SIGBUS:
+ if (kvm_on_sigbus_vcpu(env, siginfo.si_code, siginfo.si_addr)) {
+ sigbus_reraise();
+ }
+ break;
+ default:
+ break;
+ }
+
+ r = sigpending(&chkset);
+ if (r == -1) {
+ perror("sigpending");
+ exit(1);
+ }
+ } while (sigismember(&chkset, SIG_IPI) || sigismember(&chkset, SIGBUS));
+
+#ifndef CONFIG_IOTHREAD
+ if (sigismember(&chkset, SIGIO) || sigismember(&chkset, SIGALRM)) {
+ qemu_notify_event();
+ }
+#endif
+}
+
+#else /* _WIN32 */
+
HANDLE qemu_event_handle;
static void dummy_event_handler(void *opaque)
@@ -248,12 +433,78 @@ static void qemu_event_increment(void)
exit (1);
}
}
-#endif
+
+static void qemu_kvm_eat_signals(CPUState *env)
+{
+}
+#endif /* _WIN32 */
#ifndef CONFIG_IOTHREAD
+static void qemu_kvm_init_cpu_signals(CPUState *env)
+{
+#ifndef _WIN32
+ int r;
+ sigset_t set;
+ struct sigaction sigact;
+
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_handler = dummy_signal;
+ sigaction(SIG_IPI, &sigact, NULL);
+
+ sigemptyset(&set);
+ sigaddset(&set, SIG_IPI);
+ sigaddset(&set, SIGIO);
+ sigaddset(&set, SIGALRM);
+ pthread_sigmask(SIG_BLOCK, &set, NULL);
+
+ pthread_sigmask(SIG_BLOCK, NULL, &set);
+ sigdelset(&set, SIG_IPI);
+ sigdelset(&set, SIGBUS);
+ sigdelset(&set, SIGIO);
+ sigdelset(&set, SIGALRM);
+ r = kvm_set_signal_mask(env, &set);
+ if (r) {
+ fprintf(stderr, "kvm_set_signal_mask: %s\n", strerror(-r));
+ exit(1);
+ }
+#endif
+}
+
+#ifndef _WIN32
+static sigset_t block_synchronous_signals(void)
+{
+ sigset_t set;
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGBUS);
+ if (kvm_enabled()) {
+ /*
+ * We need to process timer signals synchronously to avoid a race
+ * between exit_request check and KVM vcpu entry.
+ */
+ sigaddset(&set, SIGIO);
+ sigaddset(&set, SIGALRM);
+ }
+
+ return set;
+}
+#endif
+
int qemu_init_main_loop(void)
{
- cpu_set_debug_excp_handler(cpu_debug_handler);
+#ifndef _WIN32
+ sigset_t blocked_signals;
+ int ret;
+
+ blocked_signals = block_synchronous_signals();
+
+ ret = qemu_signalfd_init(blocked_signals);
+ if (ret) {
+ return ret;
+ }
+#endif
+
+ qemu_init_sigbus();
return qemu_event_init();
}
@@ -265,12 +516,19 @@ void qemu_main_loop_start(void)
void qemu_init_vcpu(void *_env)
{
CPUState *env = _env;
+ int r;
env->nr_cores = smp_cores;
env->nr_threads = smp_threads;
- if (kvm_enabled())
- kvm_init_vcpu(env);
- return;
+
+ if (kvm_enabled()) {
+ r = kvm_init_vcpu(env);
+ if (r < 0) {
+ fprintf(stderr, "kvm_init_vcpu failed: %s\n", strerror(-r));
+ exit(1);
+ }
+ qemu_kvm_init_cpu_signals(env);
+ }
}
int qemu_cpu_self(void *env)
@@ -293,7 +551,17 @@ void pause_all_vcpus(void)
void qemu_cpu_kick(void *env)
{
- return;
+}
+
+void qemu_cpu_kick_self(void)
+{
+#ifndef _WIN32
+ assert(cpu_single_env);
+
+ raise(SIG_IPI);
+#else
+ abort();
+#endif
}
void qemu_notify_event(void)
@@ -307,11 +575,16 @@ void qemu_notify_event(void)
if (next_cpu && env != next_cpu) {
cpu_exit(next_cpu);
}
+ exit_request = 1;
}
void qemu_mutex_lock_iothread(void) {}
void qemu_mutex_unlock_iothread(void) {}
+void cpu_stop_current(void)
+{
+}
+
void vm_stop(int reason)
{
do_vm_stop(reason);
@@ -337,61 +610,65 @@ static QemuCond qemu_system_cond;
static QemuCond qemu_pause_cond;
static QemuCond qemu_work_cond;
-static void tcg_init_ipi(void);
-static void kvm_init_ipi(CPUState *env);
-static sigset_t block_io_signals(void);
-
-/* If we have signalfd, we mask out the signals we want to handle and then
- * use signalfd to listen for them. We rely on whatever the current signal
- * handler is to dispatch the signals when we receive them.
- */
-static void sigfd_handler(void *opaque)
+static void cpu_signal(int sig)
{
- int fd = (unsigned long) opaque;
- struct qemu_signalfd_siginfo info;
- struct sigaction action;
- ssize_t len;
-
- while (1) {
- do {
- len = read(fd, &info, sizeof(info));
- } while (len == -1 && errno == EINTR);
+ if (cpu_single_env) {
+ cpu_exit(cpu_single_env);
+ }
+ exit_request = 1;
+}
- if (len == -1 && errno == EAGAIN) {
- break;
- }
+static void qemu_kvm_init_cpu_signals(CPUState *env)
+{
+ int r;
+ sigset_t set;
+ struct sigaction sigact;
- if (len != sizeof(info)) {
- printf("read from sigfd returned %zd: %m\n", len);
- return;
- }
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_handler = dummy_signal;
+ sigaction(SIG_IPI, &sigact, NULL);
- sigaction(info.ssi_signo, NULL, &action);
- if ((action.sa_flags & SA_SIGINFO) && action.sa_sigaction) {
- action.sa_sigaction(info.ssi_signo,
- (siginfo_t *)&info, NULL);
- } else if (action.sa_handler) {
- action.sa_handler(info.ssi_signo);
- }
+ pthread_sigmask(SIG_BLOCK, NULL, &set);
+ sigdelset(&set, SIG_IPI);
+ sigdelset(&set, SIGBUS);
+ r = kvm_set_signal_mask(env, &set);
+ if (r) {
+ fprintf(stderr, "kvm_set_signal_mask: %s\n", strerror(-r));
+ exit(1);
}
}
-static int qemu_signalfd_init(sigset_t mask)
+static void qemu_tcg_init_cpu_signals(void)
{
- int sigfd;
+ sigset_t set;
+ struct sigaction sigact;
- sigfd = qemu_signalfd(&mask);
- if (sigfd == -1) {
- fprintf(stderr, "failed to create signalfd\n");
- return -errno;
- }
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_handler = cpu_signal;
+ sigaction(SIG_IPI, &sigact, NULL);
- fcntl_setfl(sigfd, O_NONBLOCK);
+ sigemptyset(&set);
+ sigaddset(&set, SIG_IPI);
+ pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+}
- qemu_set_fd_handler2(sigfd, NULL, sigfd_handler, NULL,
- (void *)(unsigned long) sigfd);
+static sigset_t block_io_signals(void)
+{
+ sigset_t set;
- return 0;
+ /* SIGUSR2 used by posix-aio-compat.c */
+ sigemptyset(&set);
+ sigaddset(&set, SIGUSR2);
+ pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGIO);
+ sigaddset(&set, SIGALRM);
+ sigaddset(&set, SIG_IPI);
+ sigaddset(&set, SIGBUS);
+ pthread_sigmask(SIG_BLOCK, &set, NULL);
+
+ return set;
}
int qemu_init_main_loop(void)
@@ -399,18 +676,20 @@ int qemu_init_main_loop(void)
int ret;
sigset_t blocked_signals;
- cpu_set_debug_excp_handler(cpu_debug_handler);
+ qemu_init_sigbus();
blocked_signals = block_io_signals();
ret = qemu_signalfd_init(blocked_signals);
- if (ret)
+ if (ret) {
return ret;
+ }
/* Note eventfd must be drained before signalfd handlers run */
ret = qemu_event_init();
- if (ret)
+ if (ret) {
return ret;
+ }
qemu_cond_init(&qemu_cpu_cond);
qemu_cond_init(&qemu_system_cond);
@@ -442,10 +721,11 @@ void run_on_cpu(CPUState *env, void (*func)(void *data), void *data)
wi.func = func;
wi.data = data;
- if (!env->queued_work_first)
+ if (!env->queued_work_first) {
env->queued_work_first = &wi;
- else
+ } else {
env->queued_work_last->next = &wi;
+ }
env->queued_work_last = &wi;
wi.next = NULL;
wi.done = false;
@@ -463,8 +743,9 @@ static void flush_queued_work(CPUState *env)
{
struct qemu_work_item *wi;
- if (!env->queued_work_first)
+ if (!env->queued_work_first) {
return;
+ }
while ((wi = env->queued_work_first)) {
env->queued_work_first = wi->next;
@@ -483,14 +764,16 @@ static void qemu_wait_io_event_common(CPUState *env)
qemu_cond_signal(&qemu_pause_cond);
}
flush_queued_work(env);
+ env->thread_kicked = false;
}
static void qemu_tcg_wait_io_event(void)
{
CPUState *env;
- while (!any_cpu_has_work())
+ while (all_cpu_threads_idle()) {
qemu_cond_timedwait(tcg_halt_cond, &qemu_global_mutex, 1000);
+ }
qemu_mutex_unlock(&qemu_global_mutex);
@@ -509,134 +792,72 @@ static void qemu_tcg_wait_io_event(void)
}
}
-static void sigbus_reraise(void)
-{
- sigset_t set;
- struct sigaction action;
-
- memset(&action, 0, sizeof(action));
- action.sa_handler = SIG_DFL;
- if (!sigaction(SIGBUS, &action, NULL)) {
- raise(SIGBUS);
- sigemptyset(&set);
- sigaddset(&set, SIGBUS);
- sigprocmask(SIG_UNBLOCK, &set, NULL);
- }
- perror("Failed to re-raise SIGBUS!\n");
- abort();
-}
-
-static void sigbus_handler(int n, struct qemu_signalfd_siginfo *siginfo,
- void *ctx)
-{
-#if defined(TARGET_I386)
- if (kvm_on_sigbus(siginfo->ssi_code, (void *)(intptr_t)siginfo->ssi_addr))
-#endif
- sigbus_reraise();
-}
-
-static void qemu_kvm_eat_signal(CPUState *env, int timeout)
-{
- struct timespec ts;
- int r, e;
- siginfo_t siginfo;
- sigset_t waitset;
- sigset_t chkset;
-
- ts.tv_sec = timeout / 1000;
- ts.tv_nsec = (timeout % 1000) * 1000000;
-
- sigemptyset(&waitset);
- sigaddset(&waitset, SIG_IPI);
- sigaddset(&waitset, SIGBUS);
-
- do {
- qemu_mutex_unlock(&qemu_global_mutex);
-
- r = sigtimedwait(&waitset, &siginfo, &ts);
- e = errno;
-
- qemu_mutex_lock(&qemu_global_mutex);
-
- if (r == -1 && !(e == EAGAIN || e == EINTR)) {
- fprintf(stderr, "sigtimedwait: %s\n", strerror(e));
- exit(1);
- }
-
- switch (r) {
- case SIGBUS:
-#ifdef TARGET_I386
- if (kvm_on_sigbus_vcpu(env, siginfo.si_code, siginfo.si_addr))
-#endif
- sigbus_reraise();
- break;
- default:
- break;
- }
-
- r = sigpending(&chkset);
- if (r == -1) {
- fprintf(stderr, "sigpending: %s\n", strerror(e));
- exit(1);
- }
- } while (sigismember(&chkset, SIG_IPI) || sigismember(&chkset, SIGBUS));
-}
-
static void qemu_kvm_wait_io_event(CPUState *env)
{
- while (!cpu_has_work(env))
+ while (cpu_thread_is_idle(env)) {
qemu_cond_timedwait(env->halt_cond, &qemu_global_mutex, 1000);
+ }
- qemu_kvm_eat_signal(env, 0);
+ qemu_kvm_eat_signals(env);
qemu_wait_io_event_common(env);
}
-static int qemu_cpu_exec(CPUState *env);
-
-static void *kvm_cpu_thread_fn(void *arg)
+static void *qemu_kvm_cpu_thread_fn(void *arg)
{
CPUState *env = arg;
+ int r;
qemu_mutex_lock(&qemu_global_mutex);
qemu_thread_self(env->thread);
- if (kvm_enabled())
- kvm_init_vcpu(env);
- kvm_init_ipi(env);
+ r = kvm_init_vcpu(env);
+ if (r < 0) {
+ fprintf(stderr, "kvm_init_vcpu failed: %s\n", strerror(-r));
+ exit(1);
+ }
+
+ qemu_kvm_init_cpu_signals(env);
/* signal CPU creation */
env->created = 1;
qemu_cond_signal(&qemu_cpu_cond);
/* and wait for machine initialization */
- while (!qemu_system_ready)
+ while (!qemu_system_ready) {
qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100);
+ }
while (1) {
- if (cpu_can_run(env))
- qemu_cpu_exec(env);
+ if (cpu_can_run(env)) {
+ r = kvm_cpu_exec(env);
+ if (r == EXCP_DEBUG) {
+ cpu_handle_debug_exception(env);
+ }
+ }
qemu_kvm_wait_io_event(env);
}
return NULL;
}
-static void *tcg_cpu_thread_fn(void *arg)
+static void *qemu_tcg_cpu_thread_fn(void *arg)
{
CPUState *env = arg;
- tcg_init_ipi();
+ qemu_tcg_init_cpu_signals();
qemu_thread_self(env->thread);
/* signal CPU creation */
qemu_mutex_lock(&qemu_global_mutex);
- for (env = first_cpu; env != NULL; env = env->next_cpu)
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
env->created = 1;
+ }
qemu_cond_signal(&qemu_cpu_cond);
/* and wait for machine initialization */
- while (!qemu_system_ready)
+ while (!qemu_system_ready) {
qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100);
+ }
while (1) {
cpu_exec_all();
@@ -649,89 +870,32 @@ static void *tcg_cpu_thread_fn(void *arg)
void qemu_cpu_kick(void *_env)
{
CPUState *env = _env;
- qemu_cond_broadcast(env->halt_cond);
- qemu_thread_signal(env->thread, SIG_IPI);
-}
-
-int qemu_cpu_self(void *_env)
-{
- CPUState *env = _env;
- QemuThread this;
-
- qemu_thread_self(&this);
-
- return qemu_thread_equal(&this, env->thread);
-}
-
-static void cpu_signal(int sig)
-{
- if (cpu_single_env)
- cpu_exit(cpu_single_env);
- exit_request = 1;
-}
-
-static void tcg_init_ipi(void)
-{
- sigset_t set;
- struct sigaction sigact;
-
- memset(&sigact, 0, sizeof(sigact));
- sigact.sa_handler = cpu_signal;
- sigaction(SIG_IPI, &sigact, NULL);
- sigemptyset(&set);
- sigaddset(&set, SIG_IPI);
- pthread_sigmask(SIG_UNBLOCK, &set, NULL);
-}
-
-static void dummy_signal(int sig)
-{
+ qemu_cond_broadcast(env->halt_cond);
+ if (!env->thread_kicked) {
+ qemu_thread_signal(env->thread, SIG_IPI);
+ env->thread_kicked = true;
+ }
}
-static void kvm_init_ipi(CPUState *env)
+void qemu_cpu_kick_self(void)
{
- int r;
- sigset_t set;
- struct sigaction sigact;
-
- memset(&sigact, 0, sizeof(sigact));
- sigact.sa_handler = dummy_signal;
- sigaction(SIG_IPI, &sigact, NULL);
+ assert(cpu_single_env);
- pthread_sigmask(SIG_BLOCK, NULL, &set);
- sigdelset(&set, SIG_IPI);
- sigdelset(&set, SIGBUS);
- r = kvm_set_signal_mask(env, &set);
- if (r) {
- fprintf(stderr, "kvm_set_signal_mask: %s\n", strerror(r));
- exit(1);
+ if (!cpu_single_env->thread_kicked) {
+ qemu_thread_signal(cpu_single_env->thread, SIG_IPI);
+ cpu_single_env->thread_kicked = true;
}
}
-static sigset_t block_io_signals(void)
+int qemu_cpu_self(void *_env)
{
- sigset_t set;
- struct sigaction action;
-
- /* SIGUSR2 used by posix-aio-compat.c */
- sigemptyset(&set);
- sigaddset(&set, SIGUSR2);
- pthread_sigmask(SIG_UNBLOCK, &set, NULL);
-
- sigemptyset(&set);
- sigaddset(&set, SIGIO);
- sigaddset(&set, SIGALRM);
- sigaddset(&set, SIG_IPI);
- sigaddset(&set, SIGBUS);
- pthread_sigmask(SIG_BLOCK, &set, NULL);
+ CPUState *env = _env;
+ QemuThread this;
- memset(&action, 0, sizeof(action));
- action.sa_flags = SA_SIGINFO;
- action.sa_sigaction = (void (*)(int, siginfo_t*, void*))sigbus_handler;
- sigaction(SIGBUS, &action, NULL);
- prctl(PR_MCE_KILL, 1, 1, 0, 0);
+ qemu_thread_self(&this);
- return set;
+ return qemu_thread_equal(&this, env->thread);
}
void qemu_mutex_lock_iothread(void)
@@ -758,8 +922,9 @@ static int all_vcpus_paused(void)
CPUState *penv = first_cpu;
while (penv) {
- if (!penv->stopped)
+ if (!penv->stopped) {
return 0;
+ }
penv = (CPUState *)penv->next_cpu;
}
@@ -798,17 +963,19 @@ void resume_all_vcpus(void)
}
}
-static void tcg_init_vcpu(void *_env)
+static void qemu_tcg_init_vcpu(void *_env)
{
CPUState *env = _env;
+
/* share a single thread for all cpus with TCG */
if (!tcg_cpu_thread) {
env->thread = qemu_mallocz(sizeof(QemuThread));
env->halt_cond = qemu_mallocz(sizeof(QemuCond));
qemu_cond_init(env->halt_cond);
- qemu_thread_create(env->thread, tcg_cpu_thread_fn, env);
- while (env->created == 0)
+ qemu_thread_create(env->thread, qemu_tcg_cpu_thread_fn, env);
+ while (env->created == 0) {
qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100);
+ }
tcg_cpu_thread = env->thread;
tcg_halt_cond = env->halt_cond;
} else {
@@ -817,14 +984,15 @@ static void tcg_init_vcpu(void *_env)
}
}
-static void kvm_start_vcpu(CPUState *env)
+static void qemu_kvm_start_vcpu(CPUState *env)
{
env->thread = qemu_mallocz(sizeof(QemuThread));
env->halt_cond = qemu_mallocz(sizeof(QemuCond));
qemu_cond_init(env->halt_cond);
- qemu_thread_create(env->thread, kvm_cpu_thread_fn, env);
- while (env->created == 0)
+ qemu_thread_create(env->thread, qemu_kvm_cpu_thread_fn, env);
+ while (env->created == 0) {
qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100);
+ }
}
void qemu_init_vcpu(void *_env)
@@ -833,10 +1001,11 @@ void qemu_init_vcpu(void *_env)
env->nr_cores = smp_cores;
env->nr_threads = smp_threads;
- if (kvm_enabled())
- kvm_start_vcpu(env);
- else
- tcg_init_vcpu(env);
+ if (kvm_enabled()) {
+ qemu_kvm_start_vcpu(env);
+ } else {
+ qemu_tcg_init_vcpu(env);
+ }
}
void qemu_notify_event(void)
@@ -844,10 +1013,12 @@ void qemu_notify_event(void)
qemu_event_increment();
}
-static void qemu_system_vmstop_request(int reason)
+void cpu_stop_current(void)
{
- vmstop_requested = reason;
- qemu_notify_event();
+ if (cpu_single_env) {
+ cpu_single_env->stopped = 1;
+ cpu_exit(cpu_single_env);
+ }
}
void vm_stop(int reason)
@@ -861,10 +1032,7 @@ void vm_stop(int reason)
* FIXME: should not return to device code in case
* vm_stop() has been requested.
*/
- if (cpu_single_env) {
- cpu_exit(cpu_single_env);
- cpu_single_env->stop = 1;
- }
+ cpu_stop_current();
return;
}
do_vm_stop(reason);
@@ -872,7 +1040,7 @@ void vm_stop(int reason)
#endif
-static int qemu_cpu_exec(CPUState *env)
+static int tcg_cpu_exec(CPUState *env)
{
int ret;
#ifdef CONFIG_PROFILER
@@ -912,18 +1080,29 @@ static int qemu_cpu_exec(CPUState *env)
bool cpu_exec_all(void)
{
- if (next_cpu == NULL)
+ int r;
+
+ if (next_cpu == NULL) {
next_cpu = first_cpu;
+ }
for (; next_cpu != NULL && !exit_request; next_cpu = next_cpu->next_cpu) {
CPUState *env = next_cpu;
qemu_clock_enable(vm_clock,
(env->singlestep_enabled & SSTEP_NOTIMER) == 0);
- if (qemu_alarm_pending())
+ if (qemu_alarm_pending()) {
break;
+ }
if (cpu_can_run(env)) {
- if (qemu_cpu_exec(env) == EXCP_DEBUG) {
+ if (kvm_enabled()) {
+ r = kvm_cpu_exec(env);
+ qemu_kvm_eat_signals(env);
+ } else {
+ r = tcg_cpu_exec(env);
+ }
+ if (r == EXCP_DEBUG) {
+ cpu_handle_debug_exception(env);
break;
}
} else if (env->stop) {
@@ -931,7 +1110,7 @@ bool cpu_exec_all(void)
}
}
exit_request = 0;
- return any_cpu_has_work();
+ return !all_cpu_threads_idle();
}
void set_numa_modes(void)