153 |
153 |
struct QEMUClock {
|
154 |
154 |
int type;
|
155 |
155 |
int enabled;
|
|
156 |
|
|
157 |
QEMUTimer *warp_timer;
|
156 |
158 |
};
|
157 |
159 |
|
158 |
160 |
struct QEMUTimer {
|
... | ... | |
386 |
388 |
clock->enabled = enabled;
|
387 |
389 |
}
|
388 |
390 |
|
|
391 |
static int64_t vm_clock_warp_start;
|
|
392 |
|
|
393 |
static void icount_warp_rt(void *opaque)
|
|
394 |
{
|
|
395 |
if (vm_clock_warp_start == -1) {
|
|
396 |
return;
|
|
397 |
}
|
|
398 |
|
|
399 |
if (vm_running) {
|
|
400 |
int64_t clock = qemu_get_clock_ns(rt_clock);
|
|
401 |
int64_t warp_delta = clock - vm_clock_warp_start;
|
|
402 |
if (use_icount == 1) {
|
|
403 |
qemu_icount_bias += warp_delta;
|
|
404 |
} else {
|
|
405 |
/*
|
|
406 |
* In adaptive mode, do not let the vm_clock run too
|
|
407 |
* far ahead of real time.
|
|
408 |
*/
|
|
409 |
int64_t cur_time = cpu_get_clock();
|
|
410 |
int64_t cur_icount = qemu_get_clock_ns(vm_clock);
|
|
411 |
int64_t delta = cur_time - cur_icount;
|
|
412 |
qemu_icount_bias += MIN(warp_delta, delta);
|
|
413 |
}
|
|
414 |
if (qemu_timer_expired(active_timers[QEMU_CLOCK_VIRTUAL],
|
|
415 |
qemu_get_clock_ns(vm_clock))) {
|
|
416 |
qemu_notify_event();
|
|
417 |
}
|
|
418 |
}
|
|
419 |
vm_clock_warp_start = -1;
|
|
420 |
}
|
|
421 |
|
|
422 |
void qemu_clock_warp(QEMUClock *clock)
|
|
423 |
{
|
|
424 |
int64_t deadline;
|
|
425 |
|
|
426 |
if (!clock->warp_timer) {
|
|
427 |
return;
|
|
428 |
}
|
|
429 |
|
|
430 |
/*
|
|
431 |
* There are too many global variables to make the "warp" behavior
|
|
432 |
* applicable to other clocks. But a clock argument removes the
|
|
433 |
* need for if statements all over the place.
|
|
434 |
*/
|
|
435 |
assert(clock == vm_clock);
|
|
436 |
|
|
437 |
/*
|
|
438 |
* If the CPUs have been sleeping, advance the vm_clock timer now. This
|
|
439 |
* ensures that the deadline for the timer is computed correctly below.
|
|
440 |
* This also makes sure that the insn counter is synchronized before the
|
|
441 |
* CPU starts running, in case the CPU is woken by an event other than
|
|
442 |
* the earliest vm_clock timer.
|
|
443 |
*/
|
|
444 |
icount_warp_rt(NULL);
|
|
445 |
if (!all_cpu_threads_idle() || !active_timers[clock->type]) {
|
|
446 |
qemu_del_timer(clock->warp_timer);
|
|
447 |
return;
|
|
448 |
}
|
|
449 |
|
|
450 |
vm_clock_warp_start = qemu_get_clock_ns(rt_clock);
|
|
451 |
deadline = qemu_next_deadline();
|
|
452 |
if (deadline > 0) {
|
|
453 |
/*
|
|
454 |
* Ensure the vm_clock proceeds even when the virtual CPU goes to
|
|
455 |
* sleep. Otherwise, the CPU might be waiting for a future timer
|
|
456 |
* interrupt to wake it up, but the interrupt never comes because
|
|
457 |
* the vCPU isn't running any insns and thus doesn't advance the
|
|
458 |
* vm_clock.
|
|
459 |
*
|
|
460 |
* An extreme solution for this problem would be to never let VCPUs
|
|
461 |
* sleep in icount mode if there is a pending vm_clock timer; rather
|
|
462 |
* time could just advance to the next vm_clock event. Instead, we
|
|
463 |
* do stop VCPUs and only advance vm_clock after some "real" time,
|
|
464 |
* (related to the time left until the next event) has passed. This
|
|
465 |
* rt_clock timer will do this. This avoids that the warps are too
|
|
466 |
* visible externally---for example, you will not be sending network
|
|
467 |
* packets continously instead of every 100ms.
|
|
468 |
*/
|
|
469 |
qemu_mod_timer(clock->warp_timer, vm_clock_warp_start + deadline);
|
|
470 |
} else {
|
|
471 |
qemu_notify_event();
|
|
472 |
}
|
|
473 |
}
|
|
474 |
|
389 |
475 |
QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale,
|
390 |
476 |
QEMUTimerCB *cb, void *opaque)
|
391 |
477 |
{
|
... | ... | |
454 |
540 |
qemu_rearm_alarm_timer(alarm_timer);
|
455 |
541 |
}
|
456 |
542 |
/* Interrupt execution to force deadline recalculation. */
|
457 |
|
if (use_icount)
|
|
543 |
qemu_clock_warp(ts->clock);
|
|
544 |
if (use_icount) {
|
458 |
545 |
qemu_notify_event();
|
|
546 |
}
|
459 |
547 |
}
|
460 |
548 |
}
|
461 |
549 |
|
... | ... | |
576 |
664 |
if (!option)
|
577 |
665 |
return;
|
578 |
666 |
|
|
667 |
#ifdef CONFIG_IOTHREAD
|
|
668 |
vm_clock->warp_timer = qemu_new_timer_ns(rt_clock, icount_warp_rt, NULL);
|
|
669 |
#endif
|
|
670 |
|
579 |
671 |
if (strcmp(option, "auto") != 0) {
|
580 |
672 |
icount_time_shift = strtol(option, NULL, 0);
|
581 |
673 |
use_icount = 1;
|