X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=cpus.c;h=740b8dc3f808b320cce92c434af93f8bc315eb79;hb=e59084b5b2daa22427d3dc4e1fd241f69910615e;hp=8200ac6b754bd0dffd5bb3d2e5219188a91889a6;hpb=6528a4c1f20c1ba5a22ab84bec6788a574ac04c8;p=mirror_qemu.git diff --git a/cpus.c b/cpus.c index 8200ac6b75..740b8dc3f8 100644 --- a/cpus.c +++ b/cpus.c @@ -51,10 +51,6 @@ #include "hw/nmi.h" #include "sysemu/replay.h" -#ifndef _WIN32 -#include "qemu/compatfd.h" -#endif - #ifdef CONFIG_LINUX #include @@ -185,10 +181,7 @@ static bool check_tcg_memory_orders_compatible(void) static bool default_mttcg_enabled(void) { - QemuOpts *icount_opts = qemu_find_opts_singleton("icount"); - const char *rr = qemu_opt_get(icount_opts, "rr"); - - if (rr || TCG_OVERSIZED_GUEST) { + if (use_icount || TCG_OVERSIZED_GUEST) { return false; } else { #ifdef TARGET_SUPPORTS_MTTCG @@ -206,11 +199,17 @@ void qemu_tcg_configure(QemuOpts *opts, Error **errp) if (strcmp(t, "multi") == 0) { if (TCG_OVERSIZED_GUEST) { error_setg(errp, "No MTTCG when guest word size > hosts"); + } else if (use_icount) { + error_setg(errp, "No MTTCG when icount is enabled"); } else { +#ifndef TARGET_SUPPORTS_MTTCG + error_report("Guest not yet converted to MTTCG - " + "you may get unexpected results"); +#endif if (!check_tcg_memory_orders_compatible()) { error_report("Guest expects a stronger memory ordering " "than the host provides"); - error_printf("This may cause strange/hard to debug errors"); + error_printf("This may cause strange/hard to debug errors\n"); } mttcg_enabled = true; } @@ -224,20 +223,51 @@ void qemu_tcg_configure(QemuOpts *opts, Error **errp) } } +/* The current number of executed instructions is based on what we + * originally budgeted minus the current state of the decrementing + * icount counters in extra/u16.low. + */ +static int64_t cpu_get_icount_executed(CPUState *cpu) +{ + return cpu->icount_budget - (cpu->icount_decr.u16.low + cpu->icount_extra); +} + +/* + * Update the global shared timer_state.qemu_icount to take into + * account executed instructions. This is done by the TCG vCPU + * thread so the main-loop can see time has moved forward. + */ +void cpu_update_icount(CPUState *cpu) +{ + int64_t executed = cpu_get_icount_executed(cpu); + cpu->icount_budget -= executed; + +#ifdef CONFIG_ATOMIC64 + atomic_set__nocheck(&timers_state.qemu_icount, + atomic_read__nocheck(&timers_state.qemu_icount) + + executed); +#else /* FIXME: we need 64bit atomics to do this safely */ + timers_state.qemu_icount += executed; +#endif +} + int64_t cpu_get_icount_raw(void) { - int64_t icount; CPUState *cpu = current_cpu; - icount = timers_state.qemu_icount; - if (cpu) { + if (cpu && cpu->running) { if (!cpu->can_do_io) { fprintf(stderr, "Bad icount read\n"); exit(1); } - icount -= (cpu->icount_decr.u16.low + cpu->icount_extra); + /* Take into account what has run */ + cpu_update_icount(cpu); } - return icount; +#ifdef CONFIG_ATOMIC64 + return atomic_read__nocheck(&timers_state.qemu_icount); +#else /* FIXME: we need 64bit atomics to do this safely */ + return timers_state.qemu_icount; +#endif } /* Return the virtual CPU time, based on the instruction counter. */ @@ -801,6 +831,27 @@ static void qemu_cpu_kick_rr_cpu(void) } while (cpu != atomic_mb_read(&tcg_current_rr_cpu)); } +static void do_nothing(CPUState *cpu, run_on_cpu_data unused) +{ +} + +void qemu_timer_notify_cb(void *opaque, QEMUClockType type) +{ + if (!use_icount || type != QEMU_CLOCK_VIRTUAL) { + qemu_notify_event(); + return; + } + + if (!qemu_in_vcpu_thread() && first_cpu) { + /* qemu_cpu_kick is not enough to kick a halted CPU out of + * qemu_tcg_wait_io_event. async_run_on_cpu, instead, + * causes cpu_thread_is_idle to return false. This way, + * handle_icount_deadline can run. + */ + async_run_on_cpu(first_cpu, do_nothing, RUN_ON_CPU_NULL); + } +} + static void kick_tcg_thread(void *opaque) { timer_mod(tcg_kick_vcpu_timer, qemu_tcg_next_kick()); @@ -924,13 +975,23 @@ static void sigbus_reraise(void) abort(); } -static void sigbus_handler(int n, struct qemu_signalfd_siginfo *siginfo, - void *ctx) +static void sigbus_handler(int n, siginfo_t *siginfo, void *ctx) { - if (kvm_on_sigbus(siginfo->ssi_code, - (void *)(intptr_t)siginfo->ssi_addr)) { + if (siginfo->si_code != BUS_MCEERR_AO && siginfo->si_code != BUS_MCEERR_AR) { sigbus_reraise(); } + + if (current_cpu) { + /* Called asynchronously in VCPU thread. */ + if (kvm_on_sigbus_vcpu(current_cpu, siginfo->si_code, siginfo->si_addr)) { + sigbus_reraise(); + } + } else { + /* Called synchronously (via signalfd) in main thread. */ + if (kvm_on_sigbus(siginfo->si_code, siginfo->si_addr)) { + sigbus_reraise(); + } + } } static void qemu_init_sigbus(void) @@ -939,92 +1000,17 @@ static void qemu_init_sigbus(void) memset(&action, 0, sizeof(action)); action.sa_flags = SA_SIGINFO; - action.sa_sigaction = (void (*)(int, siginfo_t*, void*))sigbus_handler; + action.sa_sigaction = sigbus_handler; sigaction(SIGBUS, &action, NULL); prctl(PR_MCE_KILL, PR_MCE_KILL_SET, PR_MCE_KILL_EARLY, 0, 0); } - -static void qemu_kvm_eat_signals(CPUState *cpu) -{ - 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(cpu, 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)); -} - #else /* !CONFIG_LINUX */ - static void qemu_init_sigbus(void) { } - -static void qemu_kvm_eat_signals(CPUState *cpu) -{ -} #endif /* !CONFIG_LINUX */ -#ifndef _WIN32 -static void dummy_signal(int sig) -{ -} - -static void qemu_kvm_init_cpu_signals(CPUState *cpu) -{ - int r; - sigset_t set; - struct sigaction sigact; - - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_handler = dummy_signal; - sigaction(SIG_IPI, &sigact, NULL); - - pthread_sigmask(SIG_BLOCK, NULL, &set); - sigdelset(&set, SIG_IPI); - sigdelset(&set, SIGBUS); - r = kvm_set_signal_mask(cpu, &set); - if (r) { - fprintf(stderr, "kvm_set_signal_mask: %s\n", strerror(-r)); - exit(1); - } -} - -#else /* _WIN32 */ -static void qemu_kvm_init_cpu_signals(CPUState *cpu) -{ - abort(); -} -#endif /* _WIN32 */ - static QemuMutex qemu_global_mutex; static QemuThread io_thread; @@ -1099,7 +1085,6 @@ static void qemu_kvm_wait_io_event(CPUState *cpu) qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex); } - qemu_kvm_eat_signals(cpu); qemu_wait_io_event_common(cpu); } @@ -1122,7 +1107,7 @@ static void *qemu_kvm_cpu_thread_fn(void *arg) exit(1); } - qemu_kvm_init_cpu_signals(cpu); + kvm_init_cpu_signals(cpu); /* signal CPU creation */ cpu->created = true; @@ -1212,16 +1197,54 @@ static int64_t tcg_get_icount_limit(void) static void handle_icount_deadline(void) { + assert(qemu_in_vcpu_thread()); if (use_icount) { int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL); if (deadline == 0) { + /* Wake up other AioContexts. */ qemu_clock_notify(QEMU_CLOCK_VIRTUAL); + qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); } } } +static void prepare_icount_for_run(CPUState *cpu) +{ + if (use_icount) { + int insns_left; + + /* These should always be cleared by process_icount_data after + * each vCPU execution. However u16.high can be raised + * asynchronously by cpu_exit/cpu_interrupt/tcg_handle_interrupt + */ + g_assert(cpu->icount_decr.u16.low == 0); + g_assert(cpu->icount_extra == 0); + + cpu->icount_budget = tcg_get_icount_limit(); + insns_left = MIN(0xffff, cpu->icount_budget); + cpu->icount_decr.u16.low = insns_left; + cpu->icount_extra = cpu->icount_budget - insns_left; + } +} + +static void process_icount_data(CPUState *cpu) +{ + if (use_icount) { + /* Account for executed instructions */ + cpu_update_icount(cpu); + + /* Reset the counters */ + cpu->icount_decr.u16.low = 0; + cpu->icount_extra = 0; + cpu->icount_budget = 0; + + replay_account_executed_instructions(); + } +} + + static int tcg_cpu_exec(CPUState *cpu) { int ret; @@ -1232,20 +1255,6 @@ static int tcg_cpu_exec(CPUState *cpu) #ifdef CONFIG_PROFILER ti = profile_getclock(); #endif - if (use_icount) { - int64_t count; - int decr; - timers_state.qemu_icount -= (cpu->icount_decr.u16.low - + cpu->icount_extra); - cpu->icount_decr.u16.low = 0; - cpu->icount_extra = 0; - count = tcg_get_icount_limit(); - timers_state.qemu_icount += count; - decr = (count > 0xffff) ? 0xffff : count; - count -= decr; - cpu->icount_decr.u16.low = decr; - cpu->icount_extra = count; - } qemu_mutex_unlock_iothread(); cpu_exec_start(cpu); ret = cpu_exec(cpu); @@ -1254,15 +1263,6 @@ static int tcg_cpu_exec(CPUState *cpu) #ifdef CONFIG_PROFILER tcg_time += profile_getclock() - ti; #endif - if (use_icount) { - /* Fold pending instructions back into the - instruction counter, and clear the interrupt flag. */ - timers_state.qemu_icount -= (cpu->icount_decr.u16.low - + cpu->icount_extra); - cpu->icount_decr.u32 = 0; - cpu->icount_extra = 0; - replay_account_executed_instructions(); - } return ret; } @@ -1330,6 +1330,11 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg) /* Account partial waits to QEMU_CLOCK_VIRTUAL. */ qemu_account_warp_timer(); + /* Run the timers here. This is much more efficient than + * waking up the I/O thread and waiting for completion. + */ + handle_icount_deadline(); + if (!cpu) { cpu = first_cpu; } @@ -1344,7 +1349,13 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg) if (cpu_can_run(cpu)) { int r; + + prepare_icount_for_run(cpu); + r = tcg_cpu_exec(cpu); + + process_icount_data(cpu); + if (r == EXCP_DEBUG) { cpu_handle_guest_debug(cpu); break; @@ -1371,8 +1382,6 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg) atomic_mb_set(&cpu->exit_request, 0); } - handle_icount_deadline(); - qemu_tcg_wait_io_event(cpu ? cpu : QTAILQ_FIRST(&cpus)); deal_with_unplugged_cpus(); } @@ -1384,8 +1393,9 @@ static void *qemu_hax_cpu_thread_fn(void *arg) { CPUState *cpu = arg; int r; + + qemu_mutex_lock_iothread(); qemu_thread_get_self(cpu->thread); - qemu_mutex_lock(&qemu_global_mutex); cpu->thread_id = qemu_get_thread_id(); cpu->created = true; @@ -1431,6 +1441,8 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) { CPUState *cpu = arg; + g_assert(!use_icount); + rcu_register_thread(); qemu_mutex_lock_iothread(); @@ -1473,8 +1485,6 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) } } - handle_icount_deadline(); - atomic_mb_set(&cpu->exit_request, 0); qemu_tcg_wait_io_event(cpu); }