|
@@ -370,6 +370,92 @@ static const struct pv_time_ops xen_time_ops __initconst = {
|
|
|
.steal_clock = xen_steal_clock,
|
|
|
};
|
|
|
|
|
|
+static struct pvclock_vsyscall_time_info *xen_clock __read_mostly;
|
|
|
+
|
|
|
+void xen_save_time_memory_area(void)
|
|
|
+{
|
|
|
+ struct vcpu_register_time_memory_area t;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (!xen_clock)
|
|
|
+ return;
|
|
|
+
|
|
|
+ t.addr.v = NULL;
|
|
|
+
|
|
|
+ ret = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_time_memory_area, 0, &t);
|
|
|
+ if (ret != 0)
|
|
|
+ pr_notice("Cannot save secondary vcpu_time_info (err %d)",
|
|
|
+ ret);
|
|
|
+ else
|
|
|
+ clear_page(xen_clock);
|
|
|
+}
|
|
|
+
|
|
|
+void xen_restore_time_memory_area(void)
|
|
|
+{
|
|
|
+ struct vcpu_register_time_memory_area t;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (!xen_clock)
|
|
|
+ return;
|
|
|
+
|
|
|
+ t.addr.v = &xen_clock->pvti;
|
|
|
+
|
|
|
+ ret = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_time_memory_area, 0, &t);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We don't disable VCLOCK_PVCLOCK entirely if it fails to register the
|
|
|
+ * secondary time info with Xen or if we migrated to a host without the
|
|
|
+ * necessary flags. On both of these cases what happens is either
|
|
|
+ * process seeing a zeroed out pvti or seeing no PVCLOCK_TSC_STABLE_BIT
|
|
|
+ * bit set. Userspace checks the latter and if 0, it discards the data
|
|
|
+ * in pvti and fallbacks to a system call for a reliable timestamp.
|
|
|
+ */
|
|
|
+ if (ret != 0)
|
|
|
+ pr_notice("Cannot restore secondary vcpu_time_info (err %d)",
|
|
|
+ ret);
|
|
|
+}
|
|
|
+
|
|
|
+static void xen_setup_vsyscall_time_info(void)
|
|
|
+{
|
|
|
+ struct vcpu_register_time_memory_area t;
|
|
|
+ struct pvclock_vsyscall_time_info *ti;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ti = (struct pvclock_vsyscall_time_info *)get_zeroed_page(GFP_KERNEL);
|
|
|
+ if (!ti)
|
|
|
+ return;
|
|
|
+
|
|
|
+ t.addr.v = &ti->pvti;
|
|
|
+
|
|
|
+ ret = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_time_memory_area, 0, &t);
|
|
|
+ if (ret) {
|
|
|
+ pr_notice("xen: VCLOCK_PVCLOCK not supported (err %d)\n", ret);
|
|
|
+ free_page((unsigned long)ti);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If primary time info had this bit set, secondary should too since
|
|
|
+ * it's the same data on both just different memory regions. But we
|
|
|
+ * still check it in case hypervisor is buggy.
|
|
|
+ */
|
|
|
+ if (!(ti->pvti.flags & PVCLOCK_TSC_STABLE_BIT)) {
|
|
|
+ t.addr.v = NULL;
|
|
|
+ ret = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_time_memory_area,
|
|
|
+ 0, &t);
|
|
|
+ if (!ret)
|
|
|
+ free_page((unsigned long)ti);
|
|
|
+
|
|
|
+ pr_notice("xen: VCLOCK_PVCLOCK not supported (tsc unstable)\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ xen_clock = ti;
|
|
|
+ pvclock_set_pvti_cpu0_va(xen_clock);
|
|
|
+
|
|
|
+ xen_clocksource.archdata.vclock_mode = VCLOCK_PVCLOCK;
|
|
|
+}
|
|
|
+
|
|
|
static void __init xen_time_init(void)
|
|
|
{
|
|
|
struct pvclock_vcpu_time_info *pvti;
|
|
@@ -401,8 +487,10 @@ static void __init xen_time_init(void)
|
|
|
* bit is supported hence speeding up Xen clocksource.
|
|
|
*/
|
|
|
pvti = &__this_cpu_read(xen_vcpu)->time;
|
|
|
- if (pvti->flags & PVCLOCK_TSC_STABLE_BIT)
|
|
|
+ if (pvti->flags & PVCLOCK_TSC_STABLE_BIT) {
|
|
|
pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT);
|
|
|
+ xen_setup_vsyscall_time_info();
|
|
|
+ }
|
|
|
|
|
|
xen_setup_runstate_info(cpu);
|
|
|
xen_setup_timer(cpu);
|