diff options
author | James Hogan <james.hogan@imgtec.com> | 2014-05-29 10:16:37 +0100 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2014-05-30 13:02:45 +0200 |
commit | f82393426afb7c82f7618b3b4e440d8dd2b40c08 (patch) | |
tree | fdbaec36165ae0545c8561acf7d56cd09454faef /arch/mips/kvm/kvm_trap_emul.c | |
parent | eda3d33c685ff62524500681249e4c8e2e8dbb8c (diff) | |
download | lwn-f82393426afb7c82f7618b3b4e440d8dd2b40c08.tar.gz lwn-f82393426afb7c82f7618b3b4e440d8dd2b40c08.zip |
MIPS: KVM: Add master disable count interface
Expose two new virtual registers to userland via the
KVM_{GET,SET}_ONE_REG ioctls.
KVM_REG_MIPS_COUNT_CTL is for timer configuration fields and just
contains a master disable count bit. This can be used by userland to
freeze the timer in order to read a consistent state from the timer
count value and timer interrupt pending bit. This cannot be done with
the CP0_Cause.DC bit because the timer interrupt pending bit (TI) is
also in CP0_Cause so it would be impossible to stop the timer without
also risking a race with an hrtimer interrupt and having to explicitly
check whether an interrupt should have occurred.
When the timer is re-enabled it resumes without losing time, i.e. the
CP0_Count value jumps to what it would have been had the timer not been
disabled, which would also be impossible to do from userland with
CP0_Cause.DC. The timer interrupt also cannot be lost, i.e. if a timer
interrupt would have occurred had the timer not been disabled it is
queued when the timer is re-enabled.
This works by storing the nanosecond monotonic time when the master
disable is set, and using it for various operations instead of the
current monotonic time (e.g. when recalculating the bias when the
CP0_Count is set), until the master disable is cleared again, i.e. the
timer state is read/written as it would have been at that time. This
state is exposed to userland via the read-only KVM_REG_MIPS_COUNT_RESUME
virtual register so that userland can determine the exact time the
master disable took effect.
This should allow userland to atomically save the state of the timer,
and later restore it.
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>
Diffstat (limited to 'arch/mips/kvm/kvm_trap_emul.c')
-rw-r--r-- | arch/mips/kvm/kvm_trap_emul.c | 15 |
1 files changed, 14 insertions, 1 deletions
diff --git a/arch/mips/kvm/kvm_trap_emul.c b/arch/mips/kvm/kvm_trap_emul.c index 9908f2b0ff46..854502bcc749 100644 --- a/arch/mips/kvm/kvm_trap_emul.c +++ b/arch/mips/kvm/kvm_trap_emul.c @@ -409,6 +409,12 @@ static int kvm_trap_emul_get_one_reg(struct kvm_vcpu *vcpu, case KVM_REG_MIPS_CP0_COUNT: *v = kvm_mips_read_count(vcpu); break; + case KVM_REG_MIPS_COUNT_CTL: + *v = vcpu->arch.count_ctl; + break; + case KVM_REG_MIPS_COUNT_RESUME: + *v = ktime_to_ns(vcpu->arch.count_resume); + break; default: return -EINVAL; } @@ -420,6 +426,7 @@ static int kvm_trap_emul_set_one_reg(struct kvm_vcpu *vcpu, s64 v) { struct mips_coproc *cop0 = vcpu->arch.cop0; + int ret = 0; switch (reg->id) { case KVM_REG_MIPS_CP0_COUNT: @@ -448,10 +455,16 @@ static int kvm_trap_emul_set_one_reg(struct kvm_vcpu *vcpu, kvm_write_c0_guest_cause(cop0, v); } break; + case KVM_REG_MIPS_COUNT_CTL: + ret = kvm_mips_set_count_ctl(vcpu, v); + break; + case KVM_REG_MIPS_COUNT_RESUME: + ret = kvm_mips_set_count_resume(vcpu, v); + break; default: return -EINVAL; } - return 0; + return ret; } static struct kvm_mips_callbacks kvm_trap_emul_callbacks = { |