Re: [PATCH 03/12] KVM: x86: Add a common TSC scaling function

2015-09-28 Thread Haozhong Zhang
On Mon, Sep 28, 2015 at 10:12:37PM +0200, Paolo Bonzini wrote:
> 
> 
> On 28/09/2015 07:38, Haozhong Zhang wrote:
> >  
> > -static u64 __scale_tsc(u64 ratio, u64 tsc)
> > -{
> > -   u64 mult, frac, _tsc;
> > -
> > -   mult  = ratio >> 32;
> > -   frac  = ratio & ((1ULL << 32) - 1);
> > -
> > -   _tsc  = tsc;
> > -   _tsc *= mult;
> > -   _tsc += (tsc >> 32) * frac;
> > -   _tsc += ((tsc & ((1ULL << 32) - 1)) * frac) >> 32;
> > -
> > -   return _tsc;
> > -}
> 
> This is basically
> 
>   return mul_u64_u64_shr(ratio, tsc,
>  kvm_tsc_scaling_ratio_frac_bits);
> 
> except that Linux has no mul_u64_u64_shr function, only mul_u64_u32_shr.
> 
> We should implement that function in include/linux/math64.h instead.
> For the x86_64 case (or any other CONFIG_ARCH_SUPPORTS_INT128
> architecture) we can just write it directly, as is done already for
> mul_u64_u32_shr.
> 
> For the 32-bit case, here is an implementation of both the
> multiplication and the shift, lifted from QEMU:
> 
> static inline void mul64(uint64_t *lo, uint64_t *hi,
>  uint64_t a, uint64_t b)
> {
> typedef union {
> uint64_t ll;
> struct {
> #ifdef __BIG_ENDIAN
> uint32_t high, low;
> #else
> uint32_t low, high;
> #endif
> } l;
> } LL;
> LL rl, rm, rn, rh, a0, b0;
> uint64_t c;
> 
> a0.ll = a;
> b0.ll = b;
> 
> rl.ll = (uint64_t)a0.l.low * b0.l.low;
> rm.ll = (uint64_t)a0.l.low * b0.l.high;
> rn.ll = (uint64_t)a0.l.high * b0.l.low;
> rh.ll = (uint64_t)a0.l.high * b0.l.high;
> 
> c = (uint64_t)rl.l.high + rm.l.low + rn.l.low;
> rl.l.high = c;
> c >>= 32;
> c = c + rm.l.high + rn.l.high + rh.l.low;
> rh.l.low = c;
> rh.l.high += (uint32_t)(c >> 32);
> 
> *lo = rl.ll;
> *hi = rh.ll;
> }
> 
> static inline void rshift128(uint64_t *lo, uint64_t *hi, int n)
> {
> uint64_t h;
> if (!n) {
> return;
> }
> h = *hi >> (n & 63);
> if (n >= 64) {
> *hi = 0;
> *lo = h;
> } else {
> *lo = (*lo >> n) | (*hi << (64 - n));
> *hi = h;
> }
> }
> 
> and you can easily reuse this code in Linux with just uintNN_t types
> changed to uNN + some extra cleanups when it's placed in a single functions.
> 
> Paolo

Thanks! I'll add mul_u64_u64_shr() and replace __scale_tsc().

- Haozhong
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 03/12] KVM: x86: Add a common TSC scaling function

2015-09-28 Thread Paolo Bonzini


On 28/09/2015 07:38, Haozhong Zhang wrote:
>  
> -static u64 __scale_tsc(u64 ratio, u64 tsc)
> -{
> - u64 mult, frac, _tsc;
> -
> - mult  = ratio >> 32;
> - frac  = ratio & ((1ULL << 32) - 1);
> -
> - _tsc  = tsc;
> - _tsc *= mult;
> - _tsc += (tsc >> 32) * frac;
> - _tsc += ((tsc & ((1ULL << 32) - 1)) * frac) >> 32;
> -
> - return _tsc;
> -}

This is basically

return mul_u64_u64_shr(ratio, tsc,
   kvm_tsc_scaling_ratio_frac_bits);

except that Linux has no mul_u64_u64_shr function, only mul_u64_u32_shr.

We should implement that function in include/linux/math64.h instead.
For the x86_64 case (or any other CONFIG_ARCH_SUPPORTS_INT128
architecture) we can just write it directly, as is done already for
mul_u64_u32_shr.

For the 32-bit case, here is an implementation of both the
multiplication and the shift, lifted from QEMU:

static inline void mul64(uint64_t *lo, uint64_t *hi,
 uint64_t a, uint64_t b)
{
typedef union {
uint64_t ll;
struct {
#ifdef __BIG_ENDIAN
uint32_t high, low;
#else
uint32_t low, high;
#endif
} l;
} LL;
LL rl, rm, rn, rh, a0, b0;
uint64_t c;

a0.ll = a;
b0.ll = b;

rl.ll = (uint64_t)a0.l.low * b0.l.low;
rm.ll = (uint64_t)a0.l.low * b0.l.high;
rn.ll = (uint64_t)a0.l.high * b0.l.low;
rh.ll = (uint64_t)a0.l.high * b0.l.high;

c = (uint64_t)rl.l.high + rm.l.low + rn.l.low;
rl.l.high = c;
c >>= 32;
c = c + rm.l.high + rn.l.high + rh.l.low;
rh.l.low = c;
rh.l.high += (uint32_t)(c >> 32);

*lo = rl.ll;
*hi = rh.ll;
}

static inline void rshift128(uint64_t *lo, uint64_t *hi, int n)
{
uint64_t h;
if (!n) {
return;
}
h = *hi >> (n & 63);
if (n >= 64) {
*hi = 0;
*lo = h;
} else {
*lo = (*lo >> n) | (*hi << (64 - n));
*hi = h;
}
}

and you can easily reuse this code in Linux with just uintNN_t types
changed to uNN + some extra cleanups when it's placed in a single functions.

Paolo
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 03/12] KVM: x86: Add a common TSC scaling function

2015-09-27 Thread Haozhong Zhang
VMX and SVM calculate the TSC scaling ratio in a similar logic, so this
patch generalizes it to a common TSC scaling function.

Signed-off-by: Haozhong Zhang 
---
 arch/x86/kvm/svm.c   | 48 +++--
 arch/x86/kvm/x86.c   | 70 
 include/linux/kvm_host.h |  4 ++-
 3 files changed, 77 insertions(+), 45 deletions(-)

diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index a3186e2..1a333bd 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -209,7 +209,6 @@ static int nested_svm_intercept(struct vcpu_svm *svm);
 static int nested_svm_vmexit(struct vcpu_svm *svm);
 static int nested_svm_check_exception(struct vcpu_svm *svm, unsigned nr,
  bool has_error_code, u32 error_code);
-static u64 __scale_tsc(u64 ratio, u64 tsc);
 
 enum {
VMCB_INTERCEPTS, /* Intercept vectors, TSC offset,
@@ -947,21 +946,7 @@ static __init int svm_hardware_setup(void)
kvm_enable_efer_bits(EFER_FFXSR);
 
if (boot_cpu_has(X86_FEATURE_TSCRATEMSR)) {
-   u64 max;
-
kvm_has_tsc_control = true;
-
-   /*
-* Make sure the user can only configure tsc_khz values that
-* fit into a signed integer.
-* A min value is not calculated needed because it will always
-* be 1 on all machines and a value of 0 is used to disable
-* tsc-scaling for the vcpu.
-*/
-   max = min(0x7fffULL, __scale_tsc(tsc_khz, TSC_RATIO_MAX));
-
-   kvm_max_guest_tsc_khz = max;
-
kvm_max_tsc_scaling_ratio = TSC_RATIO_MAX;
kvm_tsc_scaling_ratio_frac_bits = 32;
kvm_tsc_scaling_ratio_rsvd = TSC_RATIO_RSVD;
@@ -1030,31 +1015,6 @@ static void init_sys_seg(struct vmcb_seg *seg, uint32_t 
type)
seg->base = 0;
 }
 
-static u64 __scale_tsc(u64 ratio, u64 tsc)
-{
-   u64 mult, frac, _tsc;
-
-   mult  = ratio >> 32;
-   frac  = ratio & ((1ULL << 32) - 1);
-
-   _tsc  = tsc;
-   _tsc *= mult;
-   _tsc += (tsc >> 32) * frac;
-   _tsc += ((tsc & ((1ULL << 32) - 1)) * frac) >> 32;
-
-   return _tsc;
-}
-
-static u64 svm_scale_tsc(struct kvm_vcpu *vcpu, u64 tsc)
-{
-   u64 _tsc = tsc;
-
-   if (vcpu->arch.tsc_scaling_ratio != TSC_RATIO_DEFAULT)
-   _tsc = __scale_tsc(vcpu->arch.tsc_scaling_ratio, tsc);
-
-   return _tsc;
-}
-
 static void svm_set_tsc_khz(struct kvm_vcpu *vcpu, u32 user_tsc_khz, bool 
scale)
 {
u64 ratio;
@@ -1123,7 +1083,7 @@ static void svm_adjust_tsc_offset(struct kvm_vcpu *vcpu, 
s64 adjustment, bool ho
if (host) {
if (vcpu->arch.tsc_scaling_ratio != TSC_RATIO_DEFAULT)
WARN_ON(adjustment < 0);
-   adjustment = svm_scale_tsc(vcpu, (u64)adjustment);
+   adjustment = kvm_scale_tsc(vcpu, (u64)adjustment);
}
 
svm->vmcb->control.tsc_offset += adjustment;
@@ -1141,7 +1101,7 @@ static u64 svm_compute_tsc_offset(struct kvm_vcpu *vcpu, 
u64 target_tsc)
 {
u64 tsc;
 
-   tsc = svm_scale_tsc(vcpu, rdtsc());
+   tsc = kvm_scale_tsc(vcpu, rdtsc());
 
return target_tsc - tsc;
 }
@@ -3166,7 +3126,7 @@ static u64 svm_read_l1_tsc(struct kvm_vcpu *vcpu, u64 
host_tsc)
 {
struct vmcb *vmcb = get_host_vmcb(to_svm(vcpu));
return vmcb->control.tsc_offset +
-   svm_scale_tsc(vcpu, host_tsc);
+   kvm_scale_tsc(vcpu, host_tsc);
 }
 
 static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
@@ -3176,7 +3136,7 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct 
msr_data *msr_info)
switch (msr_info->index) {
case MSR_IA32_TSC: {
msr_info->data = svm->vmcb->control.tsc_offset +
-   svm_scale_tsc(vcpu, rdtsc());
+   kvm_scale_tsc(vcpu, rdtsc());
 
break;
}
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 4a521b4..920c302 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -1321,6 +1321,64 @@ static void update_ia32_tsc_adjust_msr(struct kvm_vcpu 
*vcpu, s64 offset)
vcpu->arch.ia32_tsc_adjust_msr += offset - curr_offset;
 }
 
+/*
+ * Multiply tsc by a fixed point number represented by ratio.
+ *
+ * The most significant 64-N bits (mult) of ratio represent the
+ * integral part of the fixed point number; the remaining N bits
+ * (frac) represent the fractional part, ie. ratio represents a fixed
+ * point number (mult + frac * 2^(-N)).
+ *
+ * N.B: we always assume not all 64 bits of ratio are used for the
+ * fractional part and the ratio has at least 1 bit for the fractional
+ * part, i.e. 0 < N < 64.
+ *
+ * N equals to kvm_tsc_scaling_ratio_frac_bits.
+ */
+static u64 __scale_tsc(u64 ratio, u64 tsc)
+{
+   u64 mult, frac, mask, _tsc;
+   int width, nr;
+
+   BUG_ON(kv