[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PULL 50/74] cpus: protect all icount computation with seql
From: |
Paolo Bonzini |
Subject: |
[Qemu-devel] [PULL 50/74] cpus: protect all icount computation with seqlock |
Date: |
Tue, 21 Aug 2018 19:02:22 +0200 |
Move the icount->ns computation to cpu_get_icount, and make
cpu_get_icount_locked return the raw value. This makes the
atomic_read__nocheck safe, because it now happens always inside a
seqlock and any torn reads will be retried. qemu_icount_bias and
icount_time_shift also need to be accessed with atomics. At the
same time, however, you don't need atomic_read within the writer,
because no concurrent writes are possible.
The fix to vmstate lets us keep the struct nicely packed.
Signed-off-by: Paolo Bonzini <address@hidden>
---
cpus.c | 69 ++++++++++++++++++++++++++++++++++++++++--------------------------
1 file changed, 42 insertions(+), 27 deletions(-)
diff --git a/cpus.c b/cpus.c
index 9161349..3783651 100644
--- a/cpus.c
+++ b/cpus.c
@@ -121,8 +121,6 @@ static bool all_cpu_threads_idle(void)
/* Protected by TimersState seqlock */
static bool icount_sleep = true;
-/* Conversion factor from emulated instructions to virtual clock ticks. */
-static int icount_time_shift;
/* Arbitrarily pick 1MIPS as the minimum allowable speed. */
#define MAX_ICOUNT_SHIFT 10
@@ -137,8 +135,9 @@ typedef struct TimersState {
QemuSeqLock vm_clock_seqlock;
int64_t cpu_clock_offset;
int32_t cpu_ticks_enabled;
- int64_t dummy;
+ /* Conversion factor from emulated instructions to virtual clock ticks. */
+ int icount_time_shift;
/* Compensate for varying guest execution speed. */
int64_t qemu_icount_bias;
/* Only written by TCG thread */
@@ -247,14 +246,13 @@ void cpu_update_icount(CPUState *cpu)
#ifdef CONFIG_ATOMIC64
atomic_set__nocheck(&timers_state.qemu_icount,
- atomic_read__nocheck(&timers_state.qemu_icount) +
- executed);
+ timers_state.qemu_icount + executed);
#else /* FIXME: we need 64bit atomics to do this safely */
timers_state.qemu_icount += executed;
#endif
}
-int64_t cpu_get_icount_raw(void)
+static int64_t cpu_get_icount_raw_locked(void)
{
CPUState *cpu = current_cpu;
@@ -266,20 +264,30 @@ int64_t cpu_get_icount_raw(void)
/* Take into account what has run */
cpu_update_icount(cpu);
}
-#ifdef CONFIG_ATOMIC64
+ /* The read is protected by the seqlock, so __nocheck is okay. */
return atomic_read__nocheck(&timers_state.qemu_icount);
-#else /* FIXME: we need 64bit atomics to do this safely */
- return timers_state.qemu_icount;
-#endif
}
-/* Return the virtual CPU time, based on the instruction counter. */
static int64_t cpu_get_icount_locked(void)
{
- int64_t icount = cpu_get_icount_raw();
- return timers_state.qemu_icount_bias + cpu_icount_to_ns(icount);
+ int64_t icount = cpu_get_icount_raw_locked();
+ return atomic_read__nocheck(&timers_state.qemu_icount_bias) +
cpu_icount_to_ns(icount);
+}
+
+int64_t cpu_get_icount_raw(void)
+{
+ int64_t icount;
+ unsigned start;
+
+ do {
+ start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
+ icount = cpu_get_icount_raw_locked();
+ } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
+
+ return icount;
}
+/* Return the virtual CPU time, based on the instruction counter. */
int64_t cpu_get_icount(void)
{
int64_t icount;
@@ -295,7 +303,7 @@ int64_t cpu_get_icount(void)
int64_t cpu_icount_to_ns(int64_t icount)
{
- return icount << icount_time_shift;
+ return icount << atomic_read(&timers_state.icount_time_shift);
}
/* return the time elapsed in VM between vm_start and vm_stop. Unless
@@ -415,19 +423,22 @@ static void icount_adjust(void)
/* FIXME: This is a very crude algorithm, somewhat prone to oscillation.
*/
if (delta > 0
&& last_delta + ICOUNT_WOBBLE < delta * 2
- && icount_time_shift > 0) {
+ && timers_state.icount_time_shift > 0) {
/* The guest is getting too far ahead. Slow time down. */
- icount_time_shift--;
+ atomic_set(&timers_state.icount_time_shift,
+ timers_state.icount_time_shift - 1);
}
if (delta < 0
&& last_delta - ICOUNT_WOBBLE > delta * 2
- && icount_time_shift < MAX_ICOUNT_SHIFT) {
+ && timers_state.icount_time_shift < MAX_ICOUNT_SHIFT) {
/* The guest is getting too far behind. Speed time up. */
- icount_time_shift++;
+ atomic_set(&timers_state.icount_time_shift,
+ timers_state.icount_time_shift + 1);
}
last_delta = delta;
- timers_state.qemu_icount_bias = cur_icount
- - (timers_state.qemu_icount <<
icount_time_shift);
+ atomic_set__nocheck(&timers_state.qemu_icount_bias,
+ cur_icount - (timers_state.qemu_icount
+ << timers_state.icount_time_shift));
seqlock_write_end(&timers_state.vm_clock_seqlock);
}
@@ -448,7 +459,8 @@ static void icount_adjust_vm(void *opaque)
static int64_t qemu_icount_round(int64_t count)
{
- return (count + (1 << icount_time_shift) - 1) >> icount_time_shift;
+ int shift = atomic_read(&timers_state.icount_time_shift);
+ return (count + (1 << shift) - 1) >> shift;
}
static void icount_warp_rt(void)
@@ -484,7 +496,8 @@ static void icount_warp_rt(void)
int64_t delta = clock - cur_icount;
warp_delta = MIN(warp_delta, delta);
}
- timers_state.qemu_icount_bias += warp_delta;
+ atomic_set__nocheck(&timers_state.qemu_icount_bias,
+ timers_state.qemu_icount_bias + warp_delta);
}
timers_state.vm_clock_warp_start = -1;
seqlock_write_end(&timers_state.vm_clock_seqlock);
@@ -513,7 +526,8 @@ void qtest_clock_warp(int64_t dest)
int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
seqlock_write_begin(&timers_state.vm_clock_seqlock);
- timers_state.qemu_icount_bias += warp;
+ atomic_set__nocheck(&timers_state.qemu_icount_bias,
+ timers_state.qemu_icount_bias + warp);
seqlock_write_end(&timers_state.vm_clock_seqlock);
qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
@@ -582,7 +596,8 @@ void qemu_start_warp_timer(void)
* isolated from host latencies.
*/
seqlock_write_begin(&timers_state.vm_clock_seqlock);
- timers_state.qemu_icount_bias += deadline;
+ atomic_set__nocheck(&timers_state.qemu_icount_bias,
+ timers_state.qemu_icount_bias + deadline);
seqlock_write_end(&timers_state.vm_clock_seqlock);
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
} else {
@@ -700,7 +715,7 @@ static const VMStateDescription vmstate_timers = {
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_INT64(cpu_ticks_offset, TimersState),
- VMSTATE_INT64(dummy, TimersState),
+ VMSTATE_UNUSED(8),
VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2),
VMSTATE_END_OF_LIST()
},
@@ -812,7 +827,7 @@ void configure_icount(QemuOpts *opts, Error **errp)
}
if (strcmp(option, "auto") != 0) {
errno = 0;
- icount_time_shift = strtol(option, &rem_str, 0);
+ timers_state.icount_time_shift = strtol(option, &rem_str, 0);
if (errno != 0 || *rem_str != '\0' || !strlen(option)) {
error_setg(errp, "icount: Invalid shift value");
}
@@ -828,7 +843,7 @@ void configure_icount(QemuOpts *opts, Error **errp)
/* 125MIPS seems a reasonable initial guess at the guest speed.
It will be corrected fairly quickly anyway. */
- icount_time_shift = 3;
+ timers_state.icount_time_shift = 3;
/* Have both realtime and virtual time triggers for speed adjustment.
The realtime trigger catches emulated time passing too slowly,
--
1.8.3.1
- [Qemu-devel] [PULL 46/74] qom: convert the CPU list to RCU, (continued)
- [Qemu-devel] [PULL 46/74] qom: convert the CPU list to RCU, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 23/74] tests/atomic_add-bench: add -p to enable sync profiler, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 52/74] cpus: protect TimerState writes with a spinlock, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 38/74] rcu_queue: add RCU QSIMPLEQ, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 45/74] spapr: do not use CPU_FOREACH_REVERSE, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 30/74] i386: Fix arch_query_cpu_model_expansion() leak, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 40/74] test-rcu-list: access goflag with atomics, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 42/74] test-rcu-list: abstract the list implementation, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 55/74] vhost-scsi: unify vhost-scsi get_features implementations, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 53/74] cpus: allow cpu_get_ticks out of BQL, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 50/74] cpus: protect all icount computation with seqlock,
Paolo Bonzini <=
- [Qemu-devel] [PULL 26/74] hmp-commands-info: add sync-profile, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 56/74] vhost-scsi: expose 't10_pi' property for VIRTIO_SCSI_F_T10_PI, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 54/74] vhost-user-scsi: move host_features into VHostSCSICommon, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 29/74] fw_cfg: import & use linux/qemu_fw_cfg.h, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 35/74] target-i386: fix segment limit check in ljmp, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 69/74] target/i386: update MPX flags when CPL changes, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 61/74] pc-dimm: assign and verify the "addr" property during pre_plug, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 70/74] KVM: cleanup unnecessary #ifdef KVM_CAP_..., Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 73/74] char-socket: update all ioc handlers when changing context, Paolo Bonzini, 2018/08/21
- [Qemu-devel] [PULL 60/74] pc: drop memory region alignment check for 0, Paolo Bonzini, 2018/08/21