diff options
author | Anthony Liguori <aliguori@us.ibm.com> | 2011-02-16 08:47:07 -0600 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2011-02-16 08:47:07 -0600 |
commit | c5d69e6bbf37bf5e3882060764b15e018e6a5321 (patch) | |
tree | e246e96bdfffbc5e07682c5a39ea7a65482a5935 /cpus.c | |
parent | 630ecca0da959e65acad1ee0bf3a631bbb7ee052 (diff) | |
parent | 0ec329dab938e2d97d12a91f8ed15fec27b325e0 (diff) |
Merge remote branch 'qemu-kvm/uq/master' into staging
Diffstat (limited to 'cpus.c')
-rw-r--r-- | cpus.c | 725 |
1 files changed, 452 insertions, 273 deletions
@@ -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) |