#include "qapi-event.h"
#include "hw/nmi.h"
#include "sysemu/replay.h"
+#include "hw/boards.h"
#ifdef CONFIG_LINUX
} else if (use_icount) {
error_setg(errp, "No MTTCG when icount is enabled");
} else {
-#ifndef TARGET_SUPPORT_MTTCG
+#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;
}
}
}
+/* 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. */
sleeptime_ns = (long)(throttle_ratio * CPU_THROTTLE_TIMESLICE_NS);
qemu_mutex_unlock_iothread();
- atomic_set(&cpu->throttle_thread_scheduled, 0);
g_usleep(sleeptime_ns / 1000); /* Convert ns to us for usleep call */
qemu_mutex_lock_iothread();
+ atomic_set(&cpu->throttle_thread_scheduled, 0);
}
static void cpu_throttle_timer_tick(void *opaque)
} 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)
{
- qemu_notify_event();
+ 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)
}
}
+void cpu_synchronize_all_pre_loadvm(void)
+{
+ CPUState *cpu;
+
+ CPU_FOREACH(cpu) {
+ cpu_synchronize_pre_loadvm(cpu);
+ }
+}
+
static int do_vm_stop(RunState state)
{
int ret = 0;
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;
#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);
#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;
}
/* 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;
}
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;
atomic_mb_set(&cpu->exit_request, 0);
}
- handle_icount_deadline();
-
qemu_tcg_wait_io_event(cpu ? cpu : QTAILQ_FIRST(&cpus));
deal_with_unplugged_cpus();
}
{
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;
{
CPUState *cpu = arg;
+ g_assert(!use_icount);
+
rcu_register_thread();
qemu_mutex_lock_iothread();
/* Ignore everything else? */
break;
}
+ } else if (cpu->unplug) {
+ qemu_tcg_destroy_vcpu(cpu);
+ cpu->created = false;
+ qemu_cond_signal(&qemu_cpu_cond);
+ qemu_mutex_unlock_iothread();
+ return NULL;
}
- handle_icount_deadline();
-
atomic_mb_set(&cpu->exit_request, 0);
qemu_tcg_wait_io_event(cpu);
}
CpuInfoList *qmp_query_cpus(Error **errp)
{
+ MachineState *ms = MACHINE(qdev_get_machine());
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
CpuInfoList *head = NULL, *cur_item = NULL;
CPUState *cpu;
#else
info->value->arch = CPU_INFO_ARCH_OTHER;
#endif
+ info->value->has_props = !!mc->cpu_index_to_instance_props;
+ if (info->value->has_props) {
+ CpuInstanceProperties *props;
+ props = g_malloc0(sizeof(*props));
+ *props = mc->cpu_index_to_instance_props(ms, cpu->cpu_index);
+ info->value->props = props;
+ }
/* XXX: waiting for the qapi to support GSList */
if (!cur_item) {