Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 50d0a0f9 authored by Gerd Hoffmann's avatar Gerd Hoffmann Committed by Avi Kivity
Browse files

KVM: Make kvm host use the paravirt clocksource structs



This patch updates the kvm host code to use the pvclock structs.
It also makes the paravirt clock compatible with Xen.

Signed-off-by: default avatarGerd Hoffmann <kraxel@redhat.com>
Signed-off-by: default avatarAvi Kivity <avi@qumranet.com>
parent 1c7b67f7
Loading
Loading
Loading
Loading
+62 −13
Original line number Diff line number Diff line
@@ -492,8 +492,8 @@ static int do_set_msr(struct kvm_vcpu *vcpu, unsigned index, u64 *data)
static void kvm_write_wall_clock(struct kvm *kvm, gpa_t wall_clock)
{
	static int version;
	struct kvm_wall_clock wc;
	struct timespec wc_ts;
	struct pvclock_wall_clock wc;
	struct timespec now, sys, boot;

	if (!wall_clock)
		return;
@@ -502,10 +502,19 @@ static void kvm_write_wall_clock(struct kvm *kvm, gpa_t wall_clock)

	kvm_write_guest(kvm, wall_clock, &version, sizeof(version));

	wc_ts = current_kernel_time();
	wc.wc_sec = wc_ts.tv_sec;
	wc.wc_nsec = wc_ts.tv_nsec;
	wc.wc_version = version;
	/*
	 * The guest calculates current wall clock time by adding
	 * system time (updated by kvm_write_guest_time below) to the
	 * wall clock specified here.  guest system time equals host
	 * system time for us, thus we must fill in host boot time here.
	 */
	now = current_kernel_time();
	ktime_get_ts(&sys);
	boot = ns_to_timespec(timespec_to_ns(&now) - timespec_to_ns(&sys));

	wc.sec = boot.tv_sec;
	wc.nsec = boot.tv_nsec;
	wc.version = version;

	kvm_write_guest(kvm, wall_clock, &wc, sizeof(wc));

@@ -513,6 +522,45 @@ static void kvm_write_wall_clock(struct kvm *kvm, gpa_t wall_clock)
	kvm_write_guest(kvm, wall_clock, &version, sizeof(version));
}

static uint32_t div_frac(uint32_t dividend, uint32_t divisor)
{
	uint32_t quotient, remainder;

	/* Don't try to replace with do_div(), this one calculates
	 * "(dividend << 32) / divisor" */
	__asm__ ( "divl %4"
		  : "=a" (quotient), "=d" (remainder)
		  : "0" (0), "1" (dividend), "r" (divisor) );
	return quotient;
}

static void kvm_set_time_scale(uint32_t tsc_khz, struct pvclock_vcpu_time_info *hv_clock)
{
	uint64_t nsecs = 1000000000LL;
	int32_t  shift = 0;
	uint64_t tps64;
	uint32_t tps32;

	tps64 = tsc_khz * 1000LL;
	while (tps64 > nsecs*2) {
		tps64 >>= 1;
		shift--;
	}

	tps32 = (uint32_t)tps64;
	while (tps32 <= (uint32_t)nsecs) {
		tps32 <<= 1;
		shift++;
	}

	hv_clock->tsc_shift = shift;
	hv_clock->tsc_to_system_mul = div_frac(nsecs, tps32);

	pr_debug("%s: tsc_khz %u, tsc_shift %d, tsc_mul %u\n",
		 __FUNCTION__, tsc_khz, hv_clock->tsc_shift,
		 hv_clock->tsc_to_system_mul);
}

static void kvm_write_guest_time(struct kvm_vcpu *v)
{
	struct timespec ts;
@@ -523,6 +571,11 @@ static void kvm_write_guest_time(struct kvm_vcpu *v)
	if ((!vcpu->time_page))
		return;

	if (unlikely(vcpu->hv_clock_tsc_khz != tsc_khz)) {
		kvm_set_time_scale(tsc_khz, &vcpu->hv_clock);
		vcpu->hv_clock_tsc_khz = tsc_khz;
	}

	/* Keep irq disabled to prevent changes to the clock */
	local_irq_save(flags);
	kvm_get_msr(v, MSR_IA32_TIME_STAMP_COUNTER,
@@ -537,9 +590,9 @@ static void kvm_write_guest_time(struct kvm_vcpu *v)
	/*
	 * The interface expects us to write an even number signaling that the
	 * update is finished. Since the guest won't see the intermediate
	 * state, we just write "2" at the end
	 * state, we just increase by 2 at the end.
	 */
	vcpu->hv_clock.version = 2;
	vcpu->hv_clock.version += 2;

	shared_kaddr = kmap_atomic(vcpu->time_page, KM_USER0);

@@ -599,10 +652,6 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data)
		/* ...but clean it before doing the actual write */
		vcpu->arch.time_offset = data & ~(PAGE_MASK | 1);

		vcpu->arch.hv_clock.tsc_to_system_mul =
					clocksource_khz2mult(tsc_khz, 22);
		vcpu->arch.hv_clock.tsc_shift = 22;

		down_read(&current->mm->mmap_sem);
		vcpu->arch.time_page =
				gfn_to_page(vcpu->kvm, data >> PAGE_SHIFT);
+3 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <linux/kvm_para.h>
#include <linux/kvm_types.h>

#include <asm/pvclock-abi.h>
#include <asm/desc.h>

#define KVM_MAX_VCPUS 16
@@ -282,7 +283,8 @@ struct kvm_vcpu_arch {
	struct x86_emulate_ctxt emulate_ctxt;

	gpa_t time;
	struct kvm_vcpu_time_info hv_clock;
	struct pvclock_vcpu_time_info hv_clock;
	unsigned int hv_clock_tsc_khz;
	unsigned int time_offset;
	struct page *time_page;
};