static void kvm_vcpu_write_tsc_multiplier(struct kvm_vcpu *vcpu, u64 l1_multiplier);
-static int set_tsc_khz(struct kvm_vcpu *vcpu, u32 user_tsc_khz, bool scale)
+static int calc_tsc_scaling_ratio(u32 user_tsc_khz, u64 *r_ratio)
{
+ u32 thresh_lo, thresh_hi;
u64 ratio;
- /* Guest TSC same frequency as host TSC? */
- if (!scale) {
- kvm_vcpu_write_tsc_multiplier(vcpu, kvm_caps.default_tsc_scaling_ratio);
+ /*
+ * Compute the variation in TSC rate which is acceptable
+ * within the range of tolerance and decide if the
+ * rate being applied is within that bounds of the hardware
+ * rate. If so, no scaling or compensation need be done.
+ */
+ thresh_lo = adjust_tsc_khz(tsc_khz, -tsc_tolerance_ppm);
+ thresh_hi = adjust_tsc_khz(tsc_khz, tsc_tolerance_ppm);
+ if (user_tsc_khz < thresh_lo || user_tsc_khz > thresh_hi) {
+ pr_debug("requested TSC rate %u falls outside tolerance [%u,%u]\n",
+ user_tsc_khz, thresh_lo, thresh_hi);
+ *r_ratio = kvm_caps.default_tsc_scaling_ratio;
return 0;
}
/* TSC scaling supported? */
if (!kvm_caps.has_tsc_control) {
if (user_tsc_khz > tsc_khz) {
- vcpu->arch.tsc_catchup = 1;
- vcpu->arch.tsc_always_catchup = 1;
+ *r_ratio = 0;
return 0;
} else {
pr_warn_ratelimited("user requested TSC rate below hardware speed\n");
- return -1;
+ return -ENOTSUPP;
}
- }
+ } else if (user_tsc_khz >= kvm_caps.max_guest_tsc_khz)
+ return -EINVAL;
/* TSC scaling required - calculate ratio */
ratio = mul_u64_u32_div(1ULL << kvm_caps.tsc_scaling_ratio_frac_bits,
if (ratio == 0 || ratio >= kvm_caps.max_tsc_scaling_ratio) {
pr_warn_ratelimited("Invalid TSC scaling ratio - virtual-tsc-khz=%u\n",
user_tsc_khz);
- return -1;
+ return -EINVAL;
}
- kvm_vcpu_write_tsc_multiplier(vcpu, ratio);
+ *r_ratio = ratio;
return 0;
}
-static int kvm_set_tsc_khz(struct kvm_vcpu *vcpu, u32 user_tsc_khz)
+static int kvm_vcpu_set_tsc_khz(struct kvm_vcpu *vcpu, u32 user_tsc_khz)
{
- u32 thresh_lo, thresh_hi;
- int use_scaling = 0;
+ uint64_t ratio;
+ int ret;
/* tsc_khz can be zero if TSC calibration fails */
if (user_tsc_khz == 0) {
/* set tsc_scaling_ratio to a safe value */
kvm_vcpu_write_tsc_multiplier(vcpu, kvm_caps.default_tsc_scaling_ratio);
- return -1;
+ return -EINVAL;
}
+ ret = calc_tsc_scaling_ratio(user_tsc_khz, &ratio);
+ if (ret)
+ return ret;
+
/* Compute a scale to convert nanoseconds in TSC cycles */
kvm_get_time_scale(user_tsc_khz * 1000LL, NSEC_PER_SEC,
&vcpu->arch.virtual_tsc_shift,
&vcpu->arch.virtual_tsc_mult);
vcpu->arch.virtual_tsc_khz = user_tsc_khz;
- /*
- * Compute the variation in TSC rate which is acceptable
- * within the range of tolerance and decide if the
- * rate being applied is within that bounds of the hardware
- * rate. If so, no scaling or compensation need be done.
- */
- thresh_lo = adjust_tsc_khz(tsc_khz, -tsc_tolerance_ppm);
- thresh_hi = adjust_tsc_khz(tsc_khz, tsc_tolerance_ppm);
- if (user_tsc_khz < thresh_lo || user_tsc_khz > thresh_hi) {
- pr_debug("requested TSC rate %u falls outside tolerance [%u,%u]\n",
- user_tsc_khz, thresh_lo, thresh_hi);
- use_scaling = 1;
+
+ if (ratio) {
+ kvm_vcpu_write_tsc_multiplier(vcpu, ratio);
+ } else {
+ /* Zero means software catchup */
+ vcpu->arch.tsc_catchup = 1;
+ vcpu->arch.tsc_always_catchup = 1;
}
- return set_tsc_khz(vcpu, user_tsc_khz, use_scaling);
+
+ return 0;
+}
+
+static int kvm_set_default_tsc_khz(struct kvm *kvm, u32 user_tsc_khz)
+{
+ uint64_t ratio;
+
+ /* Verify that the requested frequency is achievable */
+ if (calc_tsc_scaling_ratio(user_tsc_khz, &ratio))
+ return -EINVAL;
+
+ WRITE_ONCE(kvm->arch.default_tsc_khz, user_tsc_khz);
+
+ return 0;
}
static u64 compute_guest_tsc(struct kvm_vcpu *vcpu, s64 kernel_ns)
case KVM_SET_TSC_KHZ: {
u32 user_tsc_khz;
- r = -EINVAL;
user_tsc_khz = (u32)arg;
- if (kvm_caps.has_tsc_control &&
- user_tsc_khz >= kvm_caps.max_guest_tsc_khz)
- goto out;
-
if (user_tsc_khz == 0)
user_tsc_khz = tsc_khz;
- if (!kvm_set_tsc_khz(vcpu, user_tsc_khz))
- r = 0;
-
+ r = kvm_vcpu_set_tsc_khz(vcpu, user_tsc_khz);
goto out;
}
case KVM_GET_TSC_KHZ: {
case KVM_SET_TSC_KHZ: {
u32 user_tsc_khz;
- r = -EINVAL;
user_tsc_khz = (u32)arg;
- if (kvm_caps.has_tsc_control &&
- user_tsc_khz >= kvm_caps.max_guest_tsc_khz)
- goto out;
-
if (user_tsc_khz == 0)
user_tsc_khz = tsc_khz;
- WRITE_ONCE(kvm->arch.default_tsc_khz, user_tsc_khz);
- r = 0;
-
- goto out;
+ r = kvm_set_default_tsc_khz(kvm, user_tsc_khz);
+ break;
}
case KVM_GET_TSC_KHZ: {
r = READ_ONCE(kvm->arch.default_tsc_khz);
- goto out;
+ break;
}
case KVM_MEMORY_ENCRYPT_OP: {
r = -ENOTTY;
vcpu->arch.msr_platform_info = MSR_PLATFORM_INFO_CPUID_FAULT;
kvm_xen_init_vcpu(vcpu);
vcpu_load(vcpu);
- kvm_set_tsc_khz(vcpu, vcpu->kvm->arch.default_tsc_khz);
+ kvm_vcpu_set_tsc_khz(vcpu, vcpu->kvm->arch.default_tsc_khz);
kvm_vcpu_reset(vcpu, false);
kvm_init_mmu(vcpu);
vcpu_put(vcpu);