MIPS: KVM: Add count frequency KVM register
Expose the KVM guest CP0_Count frequency to userland via a new KVM_REG_MIPS_COUNT_HZ register accessible with the KVM_{GET,SET}_ONE_REG ioctls. When the frequency is altered the bias is adjusted such that the guest CP0_Count doesn't jump discontinuously or lose any timer interrupts. Signed-off-by: James Hogan <james.hogan@imgtec.com> Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: Gleb Natapov <gleb@kernel.org> Cc: kvm@vger.kernel.org Cc: Ralf Baechle <ralf@linux-mips.org> Cc: linux-mips@linux-mips.org Cc: David Daney <david.daney@cavium.com> Cc: Sanjay Lal <sanjayl@kymasys.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
f82393426a
commit
f74a8e224e
|
@ -720,6 +720,7 @@ void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare);
|
|||
void kvm_mips_init_count(struct kvm_vcpu *vcpu);
|
||||
int kvm_mips_set_count_ctl(struct kvm_vcpu *vcpu, s64 count_ctl);
|
||||
int kvm_mips_set_count_resume(struct kvm_vcpu *vcpu, s64 count_resume);
|
||||
int kvm_mips_set_count_hz(struct kvm_vcpu *vcpu, s64 count_hz);
|
||||
void kvm_mips_count_enable_cause(struct kvm_vcpu *vcpu);
|
||||
void kvm_mips_count_disable_cause(struct kvm_vcpu *vcpu);
|
||||
enum hrtimer_restart kvm_mips_count_timeout(struct kvm_vcpu *vcpu);
|
||||
|
|
|
@ -133,6 +133,13 @@ struct kvm_fpu {
|
|||
*/
|
||||
#define KVM_REG_MIPS_COUNT_RESUME (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \
|
||||
0x20000 | 1)
|
||||
/*
|
||||
* CP0_Count rate in Hz
|
||||
* Specifies the rate of the CP0_Count timer in Hz. Modifications occur without
|
||||
* discontinuities in CP0_Count.
|
||||
*/
|
||||
#define KVM_REG_MIPS_COUNT_HZ (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \
|
||||
0x20000 | 2)
|
||||
|
||||
/*
|
||||
* KVM MIPS specific structures and definitions
|
||||
|
|
|
@ -546,6 +546,7 @@ static u64 kvm_mips_get_one_regs[] = {
|
|||
|
||||
KVM_REG_MIPS_COUNT_CTL,
|
||||
KVM_REG_MIPS_COUNT_RESUME,
|
||||
KVM_REG_MIPS_COUNT_HZ,
|
||||
};
|
||||
|
||||
static int kvm_mips_get_reg(struct kvm_vcpu *vcpu,
|
||||
|
@ -627,6 +628,7 @@ static int kvm_mips_get_reg(struct kvm_vcpu *vcpu,
|
|||
case KVM_REG_MIPS_CP0_COUNT:
|
||||
case KVM_REG_MIPS_COUNT_CTL:
|
||||
case KVM_REG_MIPS_COUNT_RESUME:
|
||||
case KVM_REG_MIPS_COUNT_HZ:
|
||||
ret = kvm_mips_callbacks->get_one_reg(vcpu, reg, &v);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -724,6 +726,7 @@ static int kvm_mips_set_reg(struct kvm_vcpu *vcpu,
|
|||
case KVM_REG_MIPS_CP0_CAUSE:
|
||||
case KVM_REG_MIPS_COUNT_CTL:
|
||||
case KVM_REG_MIPS_COUNT_RESUME:
|
||||
case KVM_REG_MIPS_COUNT_HZ:
|
||||
return kvm_mips_callbacks->set_one_reg(vcpu, reg, v);
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
|
|
@ -497,6 +497,54 @@ void kvm_mips_init_count(struct kvm_vcpu *vcpu)
|
|||
kvm_mips_write_count(vcpu, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_mips_set_count_hz() - Update the frequency of the timer.
|
||||
* @vcpu: Virtual CPU.
|
||||
* @count_hz: Frequency of CP0_Count timer in Hz.
|
||||
*
|
||||
* Change the frequency of the CP0_Count timer. This is done atomically so that
|
||||
* CP0_Count is continuous and no timer interrupt is lost.
|
||||
*
|
||||
* Returns: -EINVAL if @count_hz is out of range.
|
||||
* 0 on success.
|
||||
*/
|
||||
int kvm_mips_set_count_hz(struct kvm_vcpu *vcpu, s64 count_hz)
|
||||
{
|
||||
struct mips_coproc *cop0 = vcpu->arch.cop0;
|
||||
int dc;
|
||||
ktime_t now;
|
||||
u32 count;
|
||||
|
||||
/* ensure the frequency is in a sensible range... */
|
||||
if (count_hz <= 0 || count_hz > NSEC_PER_SEC)
|
||||
return -EINVAL;
|
||||
/* ... and has actually changed */
|
||||
if (vcpu->arch.count_hz == count_hz)
|
||||
return 0;
|
||||
|
||||
/* Safely freeze timer so we can keep it continuous */
|
||||
dc = kvm_mips_count_disabled(vcpu);
|
||||
if (dc) {
|
||||
now = kvm_mips_count_time(vcpu);
|
||||
count = kvm_read_c0_guest_count(cop0);
|
||||
} else {
|
||||
now = kvm_mips_freeze_hrtimer(vcpu, &count);
|
||||
}
|
||||
|
||||
/* Update the frequency */
|
||||
vcpu->arch.count_hz = count_hz;
|
||||
vcpu->arch.count_period = div_u64((u64)NSEC_PER_SEC << 32, count_hz);
|
||||
vcpu->arch.count_dyn_bias = 0;
|
||||
|
||||
/* Calculate adjusted bias so dynamic count is unchanged */
|
||||
vcpu->arch.count_bias = count - kvm_mips_ktime_to_count(vcpu, now);
|
||||
|
||||
/* Update and resume hrtimer */
|
||||
if (!dc)
|
||||
kvm_mips_resume_hrtimer(vcpu, now, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_mips_write_compare() - Modify compare and update timer.
|
||||
* @vcpu: Virtual CPU.
|
||||
|
|
|
@ -415,6 +415,9 @@ static int kvm_trap_emul_get_one_reg(struct kvm_vcpu *vcpu,
|
|||
case KVM_REG_MIPS_COUNT_RESUME:
|
||||
*v = ktime_to_ns(vcpu->arch.count_resume);
|
||||
break;
|
||||
case KVM_REG_MIPS_COUNT_HZ:
|
||||
*v = vcpu->arch.count_hz;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -461,6 +464,9 @@ static int kvm_trap_emul_set_one_reg(struct kvm_vcpu *vcpu,
|
|||
case KVM_REG_MIPS_COUNT_RESUME:
|
||||
ret = kvm_mips_set_count_resume(vcpu, v);
|
||||
break;
|
||||
case KVM_REG_MIPS_COUNT_HZ:
|
||||
ret = kvm_mips_set_count_hz(vcpu, v);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue