/* * QEMU System Emulator * * Copyright (c) 2003-2008 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/cutils.h" #include "migration/vmstate.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "sysemu/cpus.h" #include "qemu/main-loop.h" #include "qemu/option.h" #include "qemu/seqlock.h" #include "sysemu/replay.h" #include "sysemu/runstate.h" #include "hw/core/cpu.h" #include "sysemu/cpu-timers.h" #include "sysemu/cpu-throttle.h" #include "timers-state.h" /* clock and ticks */ static int64_t cpu_get_ticks_locked(void) { int64_t ticks = timers_state.cpu_ticks_offset; if (timers_state.cpu_ticks_enabled) { ticks += cpu_get_host_ticks(); } if (timers_state.cpu_ticks_prev > ticks) { /* Non increasing ticks may happen if the host uses software suspend. */ timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks; ticks = timers_state.cpu_ticks_prev; } timers_state.cpu_ticks_prev = ticks; return ticks; } /* * return the time elapsed in VM between vm_start and vm_stop. * cpu_get_ticks() uses units of the host CPU cycle counter. */ int64_t cpu_get_ticks(void) { int64_t ticks; qemu_spin_lock(&timers_state.vm_clock_lock); ticks = cpu_get_ticks_locked(); qemu_spin_unlock(&timers_state.vm_clock_lock); return ticks; } int64_t cpu_get_clock_locked(void) { int64_t time; time = timers_state.cpu_clock_offset; if (timers_state.cpu_ticks_enabled) { time += get_clock(); } return time; } /* * Return the monotonic time elapsed in VM, i.e., * the time between vm_start and vm_stop */ int64_t cpu_get_clock(void) { int64_t ti; unsigned start; do { start = seqlock_read_begin(&timers_state.vm_clock_seqlock); ti = cpu_get_clock_locked(); } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start)); return ti; } /* * enable cpu_get_ticks() * Caller must hold BQL which serves as mutex for vm_clock_seqlock. */ void cpu_enable_ticks(void) { seqlock_write_lock(&timers_state.vm_clock_seqlock, &timers_state.vm_clock_lock); if (!timers_state.cpu_ticks_enabled) { timers_state.cpu_ticks_offset -= cpu_get_host_ticks(); timers_state.cpu_clock_offset -= get_clock(); timers_state.cpu_ticks_enabled = 1; } seqlock_write_unlock(&timers_state.vm_clock_seqlock, &timers_state.vm_clock_lock); } /* * disable cpu_get_ticks() : the clock is stopped. You must not call * cpu_get_ticks() after that. * Caller must hold BQL which serves as mutex for vm_clock_seqlock. */ void cpu_disable_ticks(void) { seqlock_write_lock(&timers_state.vm_clock_seqlock, &timers_state.vm_clock_lock); if (timers_state.cpu_ticks_enabled) { timers_state.cpu_ticks_offset += cpu_get_host_ticks(); timers_state.cpu_clock_offset = cpu_get_clock_locked(); timers_state.cpu_ticks_enabled = 0; } seqlock_write_unlock(&timers_state.vm_clock_seqlock, &timers_state.vm_clock_lock); } static bool icount_state_needed(void *opaque) { return icount_enabled(); } static bool warp_timer_state_needed(void *opaque) { TimersState *s = opaque; return s->icount_warp_timer != NULL; } static bool adjust_timers_state_needed(void *opaque) { TimersState *s = opaque; return s->icount_rt_timer != NULL; } static bool icount_shift_state_needed(void *opaque) { return icount_enabled() == 2; } /* * Subsection for warp timer migration is optional, because may not be created */ static const VMStateDescription icount_vmstate_warp_timer = { .name = "timer/icount/warp_timer", .version_id = 1, .minimum_version_id = 1, .needed = warp_timer_state_needed, .fields = (VMStateField[]) { VMSTATE_INT64(vm_clock_warp_start, TimersState), VMSTATE_TIMER_PTR(icount_warp_timer, TimersState), VMSTATE_END_OF_LIST() } }; static const VMStateDescription icount_vmstate_adjust_timers = { .name = "timer/icount/timers", .version_id = 1, .minimum_version_id = 1, .needed = adjust_timers_state_needed, .fields = (VMStateField[]) { VMSTATE_TIMER_PTR(icount_rt_timer, TimersState), VMSTATE_TIMER_PTR(icount_vm_timer, TimersState), VMSTATE_END_OF_LIST() } }; static const VMStateDescription icount_vmstate_shift = { .name = "timer/icount/shift", .version_id = 2, .minimum_version_id = 2, .needed = icount_shift_state_needed, .fields = (VMStateField[]) { VMSTATE_INT16(icount_time_shift, TimersState), VMSTATE_INT64(last_delta, TimersState), VMSTATE_END_OF_LIST() } }; /* * This is a subsection for icount migration. */ static const VMStateDescription icount_vmstate_timers = { .name = "timer/icount", .version_id = 1, .minimum_version_id = 1, .needed = icount_state_needed, .fields = (VMStateField[]) { VMSTATE_INT64(qemu_icount_bias, TimersState), VMSTATE_INT64(qemu_icount, TimersState), VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription * []) { &icount_vmstate_warp_timer, &icount_vmstate_adjust_timers, &icount_vmstate_shift, NULL } }; static const VMStateDescription vmstate_timers = { .name = "timer", .version_id = 2, .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_INT64(cpu_ticks_offset, TimersState), VMSTATE_UNUSED(8), VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2), VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription * []) { &icount_vmstate_timers, NULL } }; static void do_nothing(CPUState *cpu, run_on_cpu_data unused) { } void qemu_timer_notify_cb(void *opaque, QEMUClockType type) { if (!icount_enabled() || type != QEMU_CLOCK_VIRTUAL) { qemu_notify_event(); return; } if (qemu_in_vcpu_thread()) { /* * A CPU is currently running; kick it back out to the * tcg_cpu_exec() loop so it will recalculate its * icount deadline immediately. */ qemu_cpu_kick(current_cpu); } else if (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. * If we have no CPUs at all for some reason, we don't * need to do anything. */ async_run_on_cpu(first_cpu, do_nothing, RUN_ON_CPU_NULL); } } TimersState timers_state; /* initialize timers state and the cpu throttle for convenience */ void cpu_timers_init(void) { seqlock_init(&timers_state.vm_clock_seqlock); qemu_spin_init(&timers_state.vm_clock_lock); vmstate_register(NULL, 0, &vmstate_timers, &timers_state); cpu_throttle_init(); }