mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-02 23:33:54 -06:00
qemu-timer: move icount to cpus.c
None of this is needed by tools, and most of it can even be made static inside cpus.c. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
dc2dfcf000
commit
946fb27c1d
6 changed files with 296 additions and 323 deletions
279
qemu-timer.c
279
qemu-timer.c
|
@ -46,82 +46,6 @@
|
|||
|
||||
#include "qemu-timer.h"
|
||||
|
||||
/* Conversion factor from emulated instructions to virtual clock ticks. */
|
||||
int icount_time_shift;
|
||||
/* Arbitrarily pick 1MIPS as the minimum allowable speed. */
|
||||
#define MAX_ICOUNT_SHIFT 10
|
||||
/* Compensate for varying guest execution speed. */
|
||||
int64_t qemu_icount_bias;
|
||||
static QEMUTimer *icount_rt_timer;
|
||||
static QEMUTimer *icount_vm_timer;
|
||||
|
||||
/***********************************************************/
|
||||
/* guest cycle counter */
|
||||
|
||||
typedef struct TimersState {
|
||||
int64_t cpu_ticks_prev;
|
||||
int64_t cpu_ticks_offset;
|
||||
int64_t cpu_clock_offset;
|
||||
int32_t cpu_ticks_enabled;
|
||||
int64_t dummy;
|
||||
} TimersState;
|
||||
|
||||
TimersState timers_state;
|
||||
|
||||
/* return the host CPU cycle counter and handle stop/restart */
|
||||
int64_t cpu_get_ticks(void)
|
||||
{
|
||||
if (use_icount) {
|
||||
return cpu_get_icount();
|
||||
}
|
||||
if (!timers_state.cpu_ticks_enabled) {
|
||||
return timers_state.cpu_ticks_offset;
|
||||
} else {
|
||||
int64_t ticks;
|
||||
ticks = cpu_get_real_ticks();
|
||||
if (timers_state.cpu_ticks_prev > ticks) {
|
||||
/* Note: non increasing ticks may happen if the host uses
|
||||
software suspend */
|
||||
timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks;
|
||||
}
|
||||
timers_state.cpu_ticks_prev = ticks;
|
||||
return ticks + timers_state.cpu_ticks_offset;
|
||||
}
|
||||
}
|
||||
|
||||
/* return the host CPU monotonic timer and handle stop/restart */
|
||||
static int64_t cpu_get_clock(void)
|
||||
{
|
||||
int64_t ti;
|
||||
if (!timers_state.cpu_ticks_enabled) {
|
||||
return timers_state.cpu_clock_offset;
|
||||
} else {
|
||||
ti = get_clock();
|
||||
return ti + timers_state.cpu_clock_offset;
|
||||
}
|
||||
}
|
||||
|
||||
/* enable cpu_get_ticks() */
|
||||
void cpu_enable_ticks(void)
|
||||
{
|
||||
if (!timers_state.cpu_ticks_enabled) {
|
||||
timers_state.cpu_ticks_offset -= cpu_get_real_ticks();
|
||||
timers_state.cpu_clock_offset -= get_clock();
|
||||
timers_state.cpu_ticks_enabled = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* disable cpu_get_ticks() : the clock is stopped. You must not call
|
||||
cpu_get_ticks() after that. */
|
||||
void cpu_disable_ticks(void)
|
||||
{
|
||||
if (timers_state.cpu_ticks_enabled) {
|
||||
timers_state.cpu_ticks_offset = cpu_get_ticks();
|
||||
timers_state.cpu_clock_offset = cpu_get_clock();
|
||||
timers_state.cpu_ticks_enabled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
/* timers */
|
||||
|
||||
|
@ -133,7 +57,6 @@ struct QEMUClock {
|
|||
int type;
|
||||
int enabled;
|
||||
|
||||
QEMUTimer *warp_timer;
|
||||
QEMUTimer *active_timers;
|
||||
|
||||
NotifierList reset_notifiers;
|
||||
|
@ -252,61 +175,6 @@ static void dynticks_rearm_timer(struct qemu_alarm_timer *t, int64_t delta);
|
|||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
/* Correlation between real and virtual time is always going to be
|
||||
fairly approximate, so ignore small variation.
|
||||
When the guest is idle real and virtual time will be aligned in
|
||||
the IO wait loop. */
|
||||
#define ICOUNT_WOBBLE (get_ticks_per_sec() / 10)
|
||||
|
||||
static void icount_adjust(void)
|
||||
{
|
||||
int64_t cur_time;
|
||||
int64_t cur_icount;
|
||||
int64_t delta;
|
||||
static int64_t last_delta;
|
||||
/* If the VM is not running, then do nothing. */
|
||||
if (!runstate_is_running())
|
||||
return;
|
||||
|
||||
cur_time = cpu_get_clock();
|
||||
cur_icount = qemu_get_clock_ns(vm_clock);
|
||||
delta = cur_icount - cur_time;
|
||||
/* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */
|
||||
if (delta > 0
|
||||
&& last_delta + ICOUNT_WOBBLE < delta * 2
|
||||
&& icount_time_shift > 0) {
|
||||
/* The guest is getting too far ahead. Slow time down. */
|
||||
icount_time_shift--;
|
||||
}
|
||||
if (delta < 0
|
||||
&& last_delta - ICOUNT_WOBBLE > delta * 2
|
||||
&& icount_time_shift < MAX_ICOUNT_SHIFT) {
|
||||
/* The guest is getting too far behind. Speed time up. */
|
||||
icount_time_shift++;
|
||||
}
|
||||
last_delta = delta;
|
||||
qemu_icount_bias = cur_icount - (qemu_icount << icount_time_shift);
|
||||
}
|
||||
|
||||
static void icount_adjust_rt(void * opaque)
|
||||
{
|
||||
qemu_mod_timer(icount_rt_timer,
|
||||
qemu_get_clock_ms(rt_clock) + 1000);
|
||||
icount_adjust();
|
||||
}
|
||||
|
||||
static void icount_adjust_vm(void * opaque)
|
||||
{
|
||||
qemu_mod_timer(icount_vm_timer,
|
||||
qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 10);
|
||||
icount_adjust();
|
||||
}
|
||||
|
||||
int64_t qemu_icount_round(int64_t count)
|
||||
{
|
||||
return (count + (1 << icount_time_shift) - 1) >> icount_time_shift;
|
||||
}
|
||||
|
||||
static struct qemu_alarm_timer alarm_timers[] = {
|
||||
#ifndef _WIN32
|
||||
#ifdef __linux__
|
||||
|
@ -411,90 +279,6 @@ void qemu_clock_enable(QEMUClock *clock, int enabled)
|
|||
clock->enabled = enabled;
|
||||
}
|
||||
|
||||
static int64_t vm_clock_warp_start;
|
||||
|
||||
static void icount_warp_rt(void *opaque)
|
||||
{
|
||||
if (vm_clock_warp_start == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (runstate_is_running()) {
|
||||
int64_t clock = qemu_get_clock_ns(rt_clock);
|
||||
int64_t warp_delta = clock - vm_clock_warp_start;
|
||||
if (use_icount == 1) {
|
||||
qemu_icount_bias += warp_delta;
|
||||
} else {
|
||||
/*
|
||||
* In adaptive mode, do not let the vm_clock run too
|
||||
* far ahead of real time.
|
||||
*/
|
||||
int64_t cur_time = cpu_get_clock();
|
||||
int64_t cur_icount = qemu_get_clock_ns(vm_clock);
|
||||
int64_t delta = cur_time - cur_icount;
|
||||
qemu_icount_bias += MIN(warp_delta, delta);
|
||||
}
|
||||
if (qemu_timer_expired(vm_clock->active_timers,
|
||||
qemu_get_clock_ns(vm_clock))) {
|
||||
qemu_notify_event();
|
||||
}
|
||||
}
|
||||
vm_clock_warp_start = -1;
|
||||
}
|
||||
|
||||
void qemu_clock_warp(QEMUClock *clock)
|
||||
{
|
||||
int64_t deadline;
|
||||
|
||||
if (!clock->warp_timer) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* There are too many global variables to make the "warp" behavior
|
||||
* applicable to other clocks. But a clock argument removes the
|
||||
* need for if statements all over the place.
|
||||
*/
|
||||
assert(clock == vm_clock);
|
||||
|
||||
/*
|
||||
* If the CPUs have been sleeping, advance the vm_clock timer now. This
|
||||
* ensures that the deadline for the timer is computed correctly below.
|
||||
* This also makes sure that the insn counter is synchronized before the
|
||||
* CPU starts running, in case the CPU is woken by an event other than
|
||||
* the earliest vm_clock timer.
|
||||
*/
|
||||
icount_warp_rt(NULL);
|
||||
if (!all_cpu_threads_idle() || !clock->active_timers) {
|
||||
qemu_del_timer(clock->warp_timer);
|
||||
return;
|
||||
}
|
||||
|
||||
vm_clock_warp_start = qemu_get_clock_ns(rt_clock);
|
||||
deadline = qemu_next_icount_deadline();
|
||||
if (deadline > 0) {
|
||||
/*
|
||||
* Ensure the vm_clock proceeds even when the virtual CPU goes to
|
||||
* sleep. Otherwise, the CPU might be waiting for a future timer
|
||||
* interrupt to wake it up, but the interrupt never comes because
|
||||
* the vCPU isn't running any insns and thus doesn't advance the
|
||||
* vm_clock.
|
||||
*
|
||||
* An extreme solution for this problem would be to never let VCPUs
|
||||
* sleep in icount mode if there is a pending vm_clock timer; rather
|
||||
* time could just advance to the next vm_clock event. Instead, we
|
||||
* do stop VCPUs and only advance vm_clock after some "real" time,
|
||||
* (related to the time left until the next event) has passed. This
|
||||
* rt_clock timer will do this. This avoids that the warps are too
|
||||
* visible externally---for example, you will not be sending network
|
||||
* packets continously instead of every 100ms.
|
||||
*/
|
||||
qemu_mod_timer(clock->warp_timer, vm_clock_warp_start + deadline);
|
||||
} else {
|
||||
qemu_notify_event();
|
||||
}
|
||||
}
|
||||
|
||||
int64_t qemu_clock_has_timers(QEMUClock *clock)
|
||||
{
|
||||
return !!clock->active_timers;
|
||||
|
@ -709,52 +493,6 @@ void qemu_get_timer(QEMUFile *f, QEMUTimer *ts)
|
|||
}
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_timers = {
|
||||
.name = "timer",
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.fields = (VMStateField []) {
|
||||
VMSTATE_INT64(cpu_ticks_offset, TimersState),
|
||||
VMSTATE_INT64(dummy, TimersState),
|
||||
VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
void configure_icount(const char *option)
|
||||
{
|
||||
vmstate_register(NULL, 0, &vmstate_timers, &timers_state);
|
||||
if (!option)
|
||||
return;
|
||||
|
||||
vm_clock->warp_timer = qemu_new_timer_ns(rt_clock, icount_warp_rt, NULL);
|
||||
|
||||
if (strcmp(option, "auto") != 0) {
|
||||
icount_time_shift = strtol(option, NULL, 0);
|
||||
use_icount = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
use_icount = 2;
|
||||
|
||||
/* 125MIPS seems a reasonable initial guess at the guest speed.
|
||||
It will be corrected fairly quickly anyway. */
|
||||
icount_time_shift = 3;
|
||||
|
||||
/* Have both realtime and virtual time triggers for speed adjustment.
|
||||
The realtime trigger catches emulated time passing too slowly,
|
||||
the virtual time trigger catches emulated time passing too fast.
|
||||
Realtime triggers occur even when idle, so use them less frequently
|
||||
than VM triggers. */
|
||||
icount_rt_timer = qemu_new_timer_ms(rt_clock, icount_adjust_rt, NULL);
|
||||
qemu_mod_timer(icount_rt_timer,
|
||||
qemu_get_clock_ms(rt_clock) + 1000);
|
||||
icount_vm_timer = qemu_new_timer_ns(vm_clock, icount_adjust_vm, NULL);
|
||||
qemu_mod_timer(icount_vm_timer,
|
||||
qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 10);
|
||||
}
|
||||
|
||||
void qemu_run_all_timers(void)
|
||||
{
|
||||
alarm_timer->pending = 0;
|
||||
|
@ -821,23 +559,6 @@ static void host_alarm_handler(int host_signum)
|
|||
}
|
||||
}
|
||||
|
||||
int64_t qemu_next_icount_deadline(void)
|
||||
{
|
||||
/* To avoid problems with overflow limit this to 2^32. */
|
||||
int64_t delta = INT32_MAX;
|
||||
|
||||
assert(use_icount);
|
||||
if (vm_clock->active_timers) {
|
||||
delta = vm_clock->active_timers->expire_time -
|
||||
qemu_get_clock_ns(vm_clock);
|
||||
}
|
||||
|
||||
if (delta < 0)
|
||||
delta = 0;
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#include "compatfd.h"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue