Skip to content

Commit

Permalink
timer: protect timers_state's clock with seqlock
Browse files Browse the repository at this point in the history
QEMU_CLOCK_VIRTUAL may be read outside BQL. This will make its
foundation, i.e. cpu_clock_offset exposed to race condition.
Using private lock to protect it.

After this patch, reading QEMU_CLOCK_VIRTUAL is thread safe
unless use_icount is true, in which case the existing callers
still rely on the BQL.

Lock rule: private lock innermost, ie BQL->"this lock"

Signed-off-by: Liu Ping Fan <[email protected]>
Signed-off-by: Paolo Bonzini <[email protected]>
  • Loading branch information
pfliu authored and bonzini committed Oct 17, 2013
1 parent ea753d8 commit cb36564
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 7 deletions.
49 changes: 42 additions & 7 deletions cpus.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "sysemu/qtest.h"
#include "qemu/main-loop.h"
#include "qemu/bitmap.h"
#include "qemu/seqlock.h"

#ifndef _WIN32
#include "qemu/compatfd.h"
Expand Down Expand Up @@ -110,8 +111,14 @@ static int64_t vm_clock_warp_start;
static int64_t qemu_icount;

typedef struct TimersState {
/* Protected by BQL. */
int64_t cpu_ticks_prev;
int64_t cpu_ticks_offset;

/* cpu_clock_offset can be read out of BQL, so protect it with
* this lock.
*/
QemuSeqLock vm_clock_seqlock;
int64_t cpu_clock_offset;
int32_t cpu_ticks_enabled;
int64_t dummy;
Expand All @@ -137,6 +144,7 @@ int64_t cpu_get_icount(void)
}

/* return the host CPU cycle counter and handle stop/restart */
/* Caller must hold the BQL */
int64_t cpu_get_ticks(void)
{
if (use_icount) {
Expand All @@ -157,37 +165,63 @@ int64_t cpu_get_ticks(void)
}
}

/* return the host CPU monotonic timer and handle stop/restart */
int64_t cpu_get_clock(void)
static int64_t cpu_get_clock_locked(void)
{
int64_t ti;

if (!timers_state.cpu_ticks_enabled) {
return timers_state.cpu_clock_offset;
ti = timers_state.cpu_clock_offset;
} else {
ti = get_clock();
return ti + timers_state.cpu_clock_offset;
ti += timers_state.cpu_clock_offset;
}

return ti;
}

/* enable cpu_get_ticks() */
/* return the host CPU monotonic timer and handle stop/restart */
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 server as mutex for vm_clock_seqlock.
*/
void cpu_enable_ticks(void)
{
/* Here, the really thing protected by seqlock is cpu_clock_offset. */
seqlock_write_lock(&timers_state.vm_clock_seqlock);
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;
}
seqlock_write_unlock(&timers_state.vm_clock_seqlock);
}

/* disable cpu_get_ticks() : the clock is stopped. You must not call
cpu_get_ticks() after that. */
* cpu_get_ticks() after that.
* Caller must hold BQL which server as mutex for vm_clock_seqlock.
*/
void cpu_disable_ticks(void)
{
/* Here, the really thing protected by seqlock is cpu_clock_offset. */
seqlock_write_lock(&timers_state.vm_clock_seqlock);
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_clock_offset = cpu_get_clock_locked();
timers_state.cpu_ticks_enabled = 0;
}
seqlock_write_unlock(&timers_state.vm_clock_seqlock);
}

/* Correlation between real and virtual time is always going to be
Expand Down Expand Up @@ -371,6 +405,7 @@ static const VMStateDescription vmstate_timers = {

void configure_icount(const char *option)
{
seqlock_init(&timers_state.vm_clock_seqlock, NULL);
vmstate_register(NULL, 0, &vmstate_timers, &timers_state);
if (!option) {
return;
Expand Down
2 changes: 2 additions & 0 deletions include/qemu/timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,9 @@ static inline int64_t qemu_soonest_timeout(int64_t timeout1, int64_t timeout2)
void init_clocks(void);

int64_t cpu_get_ticks(void);
/* Caller must hold BQL */
void cpu_enable_ticks(void);
/* Caller must hold BQL */
void cpu_disable_ticks(void);

static inline int64_t get_ticks_per_sec(void)
Expand Down

0 comments on commit cb36564

Please sign in to comment.