icount: prepare the code for future races in calling qemu_clock_warp
Computing the deadline of all vm_clocks is somewhat expensive and calls out to qemu-timer.c; two reasons not to do it in the seqlock's write-side critical section. This however opens the door for races in setting and reading vm_clock_warp_start. To plug them, we need to cover the case where a new deadline slips in between the call to qemu_clock_deadline_ns_all and the actual modification of the icount_warp_timer. Restrict changes to vm_clock_warp_start and the icount_warp_timer's expiration time, to only move them back (which would simply cause an early wakeup). If a vm_clock timer is cancelled while CPUs are idle, this might cause the icount_warp_timer to fire unnecessarily. This is not a problem, after it fires the timer becomes inactive and the next call to timer_mod_anticipate will be precise. In addition to this, we must deactivate the icount_warp_timer _before_ checking whether CPUs are idle. This way, if the "last" CPU becomes idle during the call to timer_del we will still set up the icount_warp_timer. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
8ed961d957
commit
ce78d18ced
22
cpus.c
22
cpus.c
@ -329,6 +329,7 @@ void qtest_clock_warp(int64_t dest)
|
||||
|
||||
void qemu_clock_warp(QEMUClockType type)
|
||||
{
|
||||
int64_t clock;
|
||||
int64_t deadline;
|
||||
|
||||
/*
|
||||
@ -348,8 +349,8 @@ void qemu_clock_warp(QEMUClockType type)
|
||||
* the earliest QEMU_CLOCK_VIRTUAL timer.
|
||||
*/
|
||||
icount_warp_rt(NULL);
|
||||
if (!all_cpu_threads_idle() || !qemu_clock_has_timers(QEMU_CLOCK_VIRTUAL)) {
|
||||
timer_del(icount_warp_timer);
|
||||
timer_del(icount_warp_timer);
|
||||
if (!all_cpu_threads_idle()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -358,17 +359,11 @@ void qemu_clock_warp(QEMUClockType type)
|
||||
return;
|
||||
}
|
||||
|
||||
vm_clock_warp_start = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||
/* We want to use the earliest deadline from ALL vm_clocks */
|
||||
clock = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||
deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
|
||||
|
||||
/* Maintain prior (possibly buggy) behaviour where if no deadline
|
||||
* was set (as there is no QEMU_CLOCK_VIRTUAL timer) or it is more than
|
||||
* INT32_MAX nanoseconds ahead, we still use INT32_MAX
|
||||
* nanoseconds.
|
||||
*/
|
||||
if ((deadline < 0) || (deadline > INT32_MAX)) {
|
||||
deadline = INT32_MAX;
|
||||
if (deadline < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (deadline > 0) {
|
||||
@ -389,7 +384,10 @@ void qemu_clock_warp(QEMUClockType type)
|
||||
* you will not be sending network packets continuously instead of
|
||||
* every 100ms.
|
||||
*/
|
||||
timer_mod(icount_warp_timer, vm_clock_warp_start + deadline);
|
||||
if (vm_clock_warp_start == -1 || vm_clock_warp_start > clock) {
|
||||
vm_clock_warp_start = clock;
|
||||
}
|
||||
timer_mod_anticipate(icount_warp_timer, clock + deadline);
|
||||
} else if (deadline == 0) {
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user