QEMU-KVMの内部のタイマー

qemu-kvm-1.2.0

[qemu-timer.h qemu-timer.c]
新しいタイマーを生成
static inline QEMUTimer *qemu_new_timer_ns(QEMUClock *clock, QEMUTimerCB *cb, void *opaque)

タイマーをセットする?
void qemu_mod_timer_ns(QEMUTimer *ts, int64_t expire_time);
使い方:qemu_mod_timer_ns( n->tx_timer, qemu_get_clock_ns(vm_clock) + n->tx_timerout );

タイマーをキャンセルする?
void qemu_del_timer(QEMUTimer *ts);
使い方:qemu_del_timer(n->tx_timer)

タイマーを消去する?
void qemu_free_timer(QEMUTimer *ts);


タイマーには3種類ある
/* The real time clock should be used only for stuff which does not
change the virtual machine state, as it is run even if the virtual
machine is stopped. The real time clock has a frequency of 1000
Hz. */
extern QEMUClock *rt_clock;

/* The virtual clock is only run during the emulation. It is stopped
when the virtual machine is stopped. Virtual timers use a high
precision clock, usually cpu cycles (use ticks_per_sec). */
extern QEMUClock *vm_clock;

/* The host clock should be use for device models that emulate accurate
real time sources. It will continue to run when the virtual machine
is suspended, and it will reflect system time changes the host may
undergo (e.g. due to NTP). The host clock has the same precision as
the virtual clock. */
extern QEMUClock *host_clock;


それぞれは、こんな感じで振り分けられて
[qemu-timer.c]

int64_t qemu_get_clock_ns(QEMUClock *clock)
{
int64_t now, last;

switch(clock->type) {
case QEMU_CLOCK_REALTIME:
return get_clock();
default:
case QEMU_CLOCK_VIRTUAL:
if (use_icount) {
return cpu_get_icount();
} else {
return cpu_get_clock();
}
case QEMU_CLOCK_HOST:
now = get_clock_realtime();
last = clock->last;
clock->last = now;
if (now < last) {
notifier_list_notify(&clock->reset_notifiers, &now);
}
return now;
}
}


最終的にこのあたりの関数を使っている
[qemu-timer.h]

/* real time host monotonic timer */
static inline int64_t get_clock_realtime(void)
{
struct timeval tv;

gettimeofday(&tv, NULL);
return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000);
}

/* Warning: don't insert tracepoints into these functions, they are
also used by simpletrace backend and tracepoints would cause
an infinite recursion! */
#ifdef _WIN32
extern int64_t clock_freq;

static inline int64_t get_clock(void)
{
LARGE_INTEGER ti;
QueryPerformanceCounter(&ti);
return muldiv64(ti.QuadPart, get_ticks_per_sec(), clock_freq);
}

#else

extern int use_rt_clock;

static inline int64_t get_clock(void)
{
#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \
|| defined(__DragonFly__) || defined(__FreeBSD_kernel__)
if (use_rt_clock) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000000000LL + ts.tv_nsec;
} else
#endif
{
/* XXX: using gettimeofday leads to problems if the date
changes, so it should be avoided. */
return get_clock_realtime();
}
}


vm_clockはこんな感じ
[cpus.c]

/* Return the virtual CPU time, based on the instruction counter. */
int64_t cpu_get_icount(void)
{
int64_t icount;
CPUArchState *env = cpu_single_env;

icount = qemu_icount;
if (env) {
if (!can_do_io(env)) {
fprintf(stderr, "Bad clock read\n");
}
icount -= (env->icount_decr.u16.low + env->icount_extra);
}
return qemu_icount_bias + (icount << icount_time_shift);
}

...

/* return the host CPU monotonic timer and handle stop/restart */
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;
}
}


そして、ここからqemu_run_all_timers();によって呼び出されて実行される。
[main_loop.c]

int main_loop_wait(int nonblocking)
{
int ret;
uint32_t timeout = UINT32_MAX;

if (nonblocking) {
timeout = 0;
} else {
qemu_bh_update_timeout(&timeout);
}

/* poll any events */
/* XXX: separate device handlers from system ones */
nfds = -1;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_ZERO(&xfds);

#ifdef CONFIG_SLIRP
slirp_update_timeout(&timeout);
slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
#endif
qemu_iohandler_fill(&nfds, &rfds, &wfds, &xfds);
ret = os_host_main_loop_wait(timeout);
qemu_iohandler_poll(&rfds, &wfds, &xfds, ret);
#ifdef CONFIG_SLIRP
slirp_select_poll(&rfds, &wfds, &xfds, (ret < 0));
#endif

qemu_run_all_timers();

/* Check bottom-halves last in case any of the earlier events triggered
them. */
qemu_bh_poll();

return ret;
}

コールバック関数が呼ばれているのはこんな感じ
[qemu-timer.c]
void qemu_run_timers(QEMUClock *clock)
{
QEMUTimer **ptimer_head, *ts;
int64_t current_time;

if (!clock->enabled)
return;

current_time = qemu_get_clock_ns(clock);
ptimer_head = &clock->active_timers;
for(;;) {
ts = *ptimer_head;
if (!qemu_timer_expired_ns(ts, current_time)) {
break;
}
/* remove timer from the list before calling the callback */
*ptimer_head = ts->next;
ts->next = NULL;

/* run the callback (the timer list can be modified) */
ts->cb(ts->opaque);
}
}