This allows guests to have a different timebase origin from the host.
This is needed for migration, where a guest can migrate from one host
to another and the two hosts might have a different timebase origin.
However, the timebase seen by the guest must not go backwards, and
should go forwards only by a small amount corresponding to the time
taken for the migration.
This is only supported for recent POWER hardware which has the TBU40
(timebase upper 40 bits) register. That includes POWER6, 7, 8 but not
970.
This adds kvm_access_one_reg() to access a special register which is not
in env->spr.
The feature must be present in the host kernel.
Signed-off-by: Alexey Kardashevskiy <address@hidden>
---
Changes:
v4:
* made it per machine timebase offser rather than per CPU
v3:
* kvm_access_one_reg moved out to a separate patch
* tb_offset and host_timebase were replaced with guest_timebase as
the destionation does not really care of offset on the source
v2:
* bumped the vmstate_ppc_cpu version
* defined version for the env.tb_env field
---
hw/ppc/ppc.c | 120
+++++++++++++++++++++++++++++++++++++++++++++++++
hw/ppc/spapr.c | 3 +-
include/hw/ppc/spapr.h | 2 +
target-ppc/cpu-qom.h | 16 +++++++
target-ppc/kvm.c | 5 +++
target-ppc/machine.c | 4 +-
trace-events | 3 ++
7 files changed, 151 insertions(+), 2 deletions(-)
diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
index 9c2a132..b51db1b 100644
--- a/hw/ppc/ppc.c
+++ b/hw/ppc/ppc.c
@@ -29,9 +29,11 @@
#include "sysemu/cpus.h"
#include "hw/timer/m48t59.h"
#include "qemu/log.h"
+#include "qemu/error-report.h"
#include "hw/loader.h"
#include "sysemu/kvm.h"
#include "kvm_ppc.h"
+#include "trace.h"
//#define PPC_DEBUG_IRQ
//#define PPC_DEBUG_TB
@@ -797,6 +799,124 @@ static void cpu_ppc_set_tb_clk (void *opaque,
uint32_t freq)
cpu_ppc_store_purr(cpu, 0x0000000000000000ULL);
}
+/*
+ * Calculate timebase on the destination side of migration
+ *
+ * We calculate new timebase offset as shown below:
+ * 1) Gtb2 = Gtb1 + max(tod2 - tod1, 0)
+ * Gtb2 = tb2 + off2
+ * 2) tb2 + off2 = Gtb1 + max(tod2 - tod1, 0)
+ * 3) off2 = Gtb1 - tb2 + max(tod2 - tod1, 0)
+ *
+ * where:
+ * Gtb2 - destination guest timebase
+ * tb2 - destination host timebase
+ * off2 - destination timebase offset
+ * tod2 - destination time of the day
+ * Gtb1 - source guest timebase
+ * tod1 - source time of the day
+ *
+ * The result we want is in @off2
+ *
+ * Two conditions must be met for @off2:
+ * 1) off2 must be multiple of 2^24 ticks as it will be set via TBU40 SPR
+ * 2) Gtb2 >= Gtb1
+ */
+static int64_t cpu_ppc_adjust_tb_offset(PPCTimebaseOffset *tb)
+{
+ uint64_t tb2, tod2;
+ int64_t off2;
+ int ratio = tb->freq / 1000000;
+ struct timeval tv;
+
+ tb2 = cpu_get_real_ticks();
+ gettimeofday(&tv, NULL);
+ tod2 = tv.tv_sec * 1000000 + tv.tv_usec;
+
+ off2 = tb->guest_timebase - tb2;
+ if ((tod2 > tb->time_of_the_day) &&
+ (tod2 - tb->time_of_the_day < 1000000)) {
+ off2 += (tod2 - tb->time_of_the_day) * ratio;
+ }
+ off2 = ROUND_UP(off2, 1 << 24);
+
+ return off2;
+}