Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1320708 > unrolled thread

[RFC PATCH 0/2] Hyper-V TSC page support.

Started byPaolo Bonzini <pbonzini@redhat.com>
First post2016-01-28 15:10 +0100
Last post2016-01-28 15:10 +0100
Articles 3 — 1 participant

Back to article view | Back to linux.kernel


Contents

  [RFC PATCH 0/2] Hyper-V TSC page support. Paolo Bonzini <pbonzini@redhat.com> - 2016-01-28 15:10 +0100
    [PATCH 2/2] kvm/x86: Hyper-V tsc page setup Paolo Bonzini <pbonzini@redhat.com> - 2016-01-28 15:10 +0100
    [PATCH 1/2] KVM: x86: introduce do_shl32_div32 Paolo Bonzini <pbonzini@redhat.com> - 2016-01-28 15:10 +0100

#1320708 — [RFC PATCH 0/2] Hyper-V TSC page support.

FromPaolo Bonzini <pbonzini@redhat.com>
Date2016-01-28 15:10 +0100
Subject[RFC PATCH 0/2] Hyper-V TSC page support.
Message-ID<qVVqP-4wq-13@gated-at.bofh.it>
Here is a small revamping of Andrey's Hyper-V TSC page patch.  Unfortunately
I see an accuracy problem with the patch, for which I can provide a test
case.

Thanks,

Paolo

Andrey Smetanin (1):
  kvm/x86: Hyper-V tsc page setup

Paolo Bonzini (1):
  KVM: x86: introduce do_shl32_div32

 arch/x86/kvm/hyperv.c | 121 +++++++++++++++++++++++++++++++++++++++++++-------
 arch/x86/kvm/hyperv.h |   2 +
 arch/x86/kvm/x86.c    |  16 +++----
 arch/x86/kvm/x86.h    |  15 +++++++
 4 files changed, 129 insertions(+), 25 deletions(-)

-- 
1.8.3.1

[toc] | [next] | [standalone]


#1320709 — [PATCH 2/2] kvm/x86: Hyper-V tsc page setup

FromPaolo Bonzini <pbonzini@redhat.com>
Date2016-01-28 15:10 +0100
Subject[PATCH 2/2] kvm/x86: Hyper-V tsc page setup
Message-ID<qVVqP-4wq-11@gated-at.bofh.it>
In reply to#1320708
From: Andrey Smetanin <asmetanin@virtuozzo.com>

Lately tsc page was implemented but filled with empty
values. This patch setup tsc page scale and offset based
on vcpu tsc, tsc_khz and  HV_X64_MSR_TIME_REF_COUNT value.

The valid tsc page drops HV_X64_MSR_TIME_REF_COUNT msr
reads count to zero which potentially improves performance.

Signed-off-by: Andrey Smetanin <asmetanin@virtuozzo.com>
Reviewed-by: Peter Hornyack <peterhornyack@google.com>
CC: Paolo Bonzini <pbonzini@redhat.com>
CC: Roman Kagan <rkagan@virtuozzo.com>
CC: Denis V. Lunev <den@openvz.org>
CC: qemu-devel@nongnu.org
[Skip sequence number 0xFFFFFFFF as suggested by Peter Hornyack,
 enable only if KVM master clock is active. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
	There is an accuracy problem with this patch.  See
	the kvm-unit-tests patch I'll send shortly.

 arch/x86/kvm/hyperv.c | 121 +++++++++++++++++++++++++++++++++++++++++++-------
 arch/x86/kvm/hyperv.h |   2 +
 arch/x86/kvm/x86.c    |   6 ++-
 3 files changed, 112 insertions(+), 17 deletions(-)

diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index c58ba67175ac..adb0e72464c0 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -756,6 +756,109 @@ static int kvm_hv_msr_set_crash_data(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static u64 calc_tsc_page_scale(u32 tsc_khz)
+{
+	/*
+	 * reftime (in 100ns) = tsc * tsc_scale / 2^64 + tsc_offset
+	 * so reftime_delta = (tsc_delta * tsc_scale) / 2^64
+	 * so tsc_scale = (2^64 * reftime_delta)/tsc_delta
+	 * so tsc_scale = (2^64 * 10 * 10^6) / tsc_hz = (2^64 * 10000) / tsc_khz
+	 */
+	u32 h, l;
+
+	/* Long division */
+	h = 10000;
+	l = do_shl32_div32(h, tsc_khz);
+	(void) do_shl32_div32(l, tsc_khz);
+
+	return ((u64)h << 32) | l;
+}
+
+/* If tsc_khz <= 10000, the scale doesn't fit in 64 bits.  */
+#define MIN_VALID_TSC_KHZ 10001
+
+static int write_tsc_page(struct kvm *kvm, u64 gfn,
+			  PHV_REFERENCE_TSC_PAGE tsc_ref)
+{
+	if (kvm_write_guest(kvm, gfn_to_gpa(gfn),
+			    tsc_ref, sizeof(*tsc_ref)))
+		return 1;
+	mark_page_dirty(kvm, gfn);
+	return 0;
+}
+
+static int read_tsc_page(struct kvm *kvm, u64 gfn,
+			 PHV_REFERENCE_TSC_PAGE tsc_ref)
+{
+	if (kvm_read_guest(kvm, gfn_to_gpa(gfn),
+			   tsc_ref, sizeof(*tsc_ref)))
+		return 1;
+	return 0;
+}
+
+static u64 calc_tsc_page_time(struct kvm_vcpu *vcpu,
+			      PHV_REFERENCE_TSC_PAGE tsc_ref)
+{
+
+	u64 tsc = kvm_read_l1_tsc(vcpu, rdtsc());
+
+	return mul_u64_u64_shr(tsc, tsc_ref->tsc_scale, 64)
+		+ tsc_ref->tsc_offset;
+}
+
+int kvm_hv_setup_tsc_page(struct kvm *kvm, bool has_master_clock)
+{
+	struct kvm_vcpu *vcpu = kvm_get_vcpu(kvm, 0);
+	struct kvm_hv *hv = &kvm->arch.hyperv;
+	HV_REFERENCE_TSC_PAGE tsc_ref = { 0 };
+	u32 tsc_khz;
+	int r;
+	u64 gfn, ref_time, tsc_scale, tsc_offset, tsc;
+
+	if (!(hv->hv_tsc_page & HV_X64_MSR_TSC_REFERENCE_ENABLE))
+		return 0;
+
+	gfn = hv->hv_tsc_page >> HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT;
+	kvm_debug("tsc page gfn 0x%llx\n", gfn);
+
+	tsc_khz = vcpu->arch.virtual_tsc_khz;
+	if (!has_master_clock || WARN_ON_ONCE(!tsc_khz) ||
+	    tsc_khz < MIN_VALID_TSC_KHZ) {
+		/* Use reference time MSR.  */
+		goto done;
+	}
+
+	r = read_tsc_page(kvm, gfn, &tsc_ref);
+	if (r) {
+		kvm_err("can't access tsc page gfn 0x%llx\n", gfn);
+		return r;
+	}
+
+	tsc_scale = calc_tsc_page_scale(tsc_khz);
+	ref_time = get_time_ref_counter(kvm);
+	tsc = kvm_read_l1_tsc(vcpu, rdtsc());
+
+	/* tsc_offset = reftime - tsc * tsc_scale / 2^64 */
+	tsc_offset = ref_time - mul_u64_u64_shr(tsc, tsc_scale, 64);
+	kvm_debug("tsc khz %u tsc %llu scale %llu offset %llu\n",
+		   tsc_khz, tsc, tsc_scale, tsc_offset);
+
+	tsc_ref.tsc_sequence++;
+	if (tsc_ref.tsc_sequence == 0xFFFFFFFF ||
+	    tsc_ref.tsc_sequence == 0)
+		tsc_ref.tsc_sequence = 1;
+
+	tsc_ref.tsc_scale = tsc_scale;
+	tsc_ref.tsc_offset = tsc_offset;
+
+	kvm_debug("tsc page calibration time %llu vs. reftime %llu\n",
+		  calc_tsc_page_time(vcpu, &tsc_ref),
+		  get_time_ref_counter(kvm));
+
+done:
+	return write_tsc_page(kvm, gfn, &tsc_ref);
+}
+
 static int kvm_hv_set_msr_pw(struct kvm_vcpu *vcpu, u32 msr, u64 data,
 			     bool host)
 {
@@ -793,23 +896,11 @@ static int kvm_hv_set_msr_pw(struct kvm_vcpu *vcpu, u32 msr, u64 data,
 		mark_page_dirty(kvm, gfn);
 		break;
 	}
-	case HV_X64_MSR_REFERENCE_TSC: {
-		u64 gfn;
-		HV_REFERENCE_TSC_PAGE tsc_ref;
-
-		memset(&tsc_ref, 0, sizeof(tsc_ref));
+	case HV_X64_MSR_REFERENCE_TSC:
 		hv->hv_tsc_page = data;
-		if (!(data & HV_X64_MSR_TSC_REFERENCE_ENABLE))
-			break;
-		gfn = data >> HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT;
-		if (kvm_write_guest(
-				kvm,
-				gfn << HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT,
-				&tsc_ref, sizeof(tsc_ref)))
-			return 1;
-		mark_page_dirty(kvm, gfn);
+		if (hv->hv_tsc_page & HV_X64_MSR_TSC_REFERENCE_ENABLE)
+			kvm_make_request(KVM_REQ_MASTERCLOCK_UPDATE, vcpu);
 		break;
-	}
 	case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4:
 		return kvm_hv_msr_set_crash_data(vcpu,
 						 msr - HV_X64_MSR_CRASH_P0,
diff --git a/arch/x86/kvm/hyperv.h b/arch/x86/kvm/hyperv.h
index 60eccd4bd1d3..2e300d9cfdc0 100644
--- a/arch/x86/kvm/hyperv.h
+++ b/arch/x86/kvm/hyperv.h
@@ -84,4 +84,6 @@ static inline bool kvm_hv_has_stimer_pending(struct kvm_vcpu *vcpu)
 
 void kvm_hv_process_stimers(struct kvm_vcpu *vcpu);
 
+int kvm_hv_setup_tsc_page(struct kvm *kvm, bool has_master_clock);
+
 #endif
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 5b937fdebc66..2507cbb34537 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -1231,9 +1231,7 @@ static void kvm_get_time_scale(uint32_t scaled_khz, uint32_t base_khz,
 		 __func__, base_khz, scaled_khz, shift, *pmultiplier);
 }
 
-#ifdef CONFIG_X86_64
 static atomic_t kvm_guest_has_master_clock = ATOMIC_INIT(0);
-#endif
 
 static DEFINE_PER_CPU(unsigned long, cpu_tsc_khz);
 static unsigned long max_tsc_khz;
@@ -1706,6 +1704,10 @@ static void kvm_gen_update_masterclock(struct kvm *kvm)
 
 	spin_unlock(&ka->pvclock_gtod_sync_lock);
 #endif
+
+	if (kvm->arch.hyperv.hv_tsc_page & HV_X64_MSR_TSC_REFERENCE_ENABLE)
+		kvm_hv_setup_tsc_page(kvm,
+				      atomic_read(&kvm_guest_has_master_clock));
 }
 
 static int kvm_guest_time_update(struct kvm_vcpu *v)
-- 
1.8.3.1

[toc] | [prev] | [next] | [standalone]


#1320711 — [PATCH 1/2] KVM: x86: introduce do_shl32_div32

FromPaolo Bonzini <pbonzini@redhat.com>
Date2016-01-28 15:10 +0100
Subject[PATCH 1/2] KVM: x86: introduce do_shl32_div32
Message-ID<qVVqP-4wq-19@gated-at.bofh.it>
In reply to#1320708
This is similar to the existing div_frac function, but it returns the
remainder too.  Unlike div_frac, it can be used to implement long
division, e.g. (a << 64) / b for 32-bit a and b.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 arch/x86/kvm/x86.c | 10 ++--------
 arch/x86/kvm/x86.h | 15 +++++++++++++++
 2 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 4244c2baf57d..5b937fdebc66 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -1196,14 +1196,8 @@ static void kvm_write_wall_clock(struct kvm *kvm, gpa_t wall_clock)
 
 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;
+	do_shl32_div32(dividend, divisor);
+	return dividend;
 }
 
 static void kvm_get_time_scale(uint32_t scaled_khz, uint32_t base_khz,
diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h
index f2afa5fe48a6..34f416427143 100644
--- a/arch/x86/kvm/x86.h
+++ b/arch/x86/kvm/x86.h
@@ -192,4 +192,19 @@ extern unsigned int min_timer_period_us;
 extern unsigned int lapic_timer_advance_ns;
 
 extern struct static_key kvm_no_apic_vcpu;
+
+/* Same "calling convention" as do_div:
+ * - divide (n << 32) by base
+ * - put result in n
+ * - return remainder
+ */
+#define do_shl32_div32(n, base)					\
+	({							\
+	    u32 __quot, __rem;					\
+	    asm("divl %2" : "=a" (__quot), "=d" (__rem)		\
+			: "rm" (base), "0" (0), "1" ((u32) n));	\
+	    n = __quot;						\
+	    __rem;						\
+	 })
+
 #endif
-- 
1.8.3.1

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web