diff options
author | James Hogan <james.hogan@imgtec.com> | 2014-05-29 05:16:38 -0400 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2014-05-30 07:02:54 -0400 |
commit | f74a8e224e32edccade63a23bc260b141b067b1d (patch) | |
tree | b79412e24fa3b7a059232871982f71bd1f8d2ff3 | |
parent | f82393426afb7c82f7618b3b4e440d8dd2b40c08 (diff) |
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>
-rw-r--r-- | arch/mips/include/asm/kvm_host.h | 1 | ||||
-rw-r--r-- | arch/mips/include/uapi/asm/kvm.h | 7 | ||||
-rw-r--r-- | arch/mips/kvm/kvm_mips.c | 3 | ||||
-rw-r--r-- | arch/mips/kvm/kvm_mips_emul.c | 48 | ||||
-rw-r--r-- | arch/mips/kvm/kvm_trap_emul.c | 6 |
5 files changed, 65 insertions, 0 deletions
diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h index 1deeaecbe73e..f9c672f729ea 100644 --- a/arch/mips/include/asm/kvm_host.h +++ b/arch/mips/include/asm/kvm_host.h | |||
@@ -720,6 +720,7 @@ void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare); | |||
720 | void kvm_mips_init_count(struct kvm_vcpu *vcpu); | 720 | void kvm_mips_init_count(struct kvm_vcpu *vcpu); |
721 | int kvm_mips_set_count_ctl(struct kvm_vcpu *vcpu, s64 count_ctl); | 721 | int kvm_mips_set_count_ctl(struct kvm_vcpu *vcpu, s64 count_ctl); |
722 | int kvm_mips_set_count_resume(struct kvm_vcpu *vcpu, s64 count_resume); | 722 | int kvm_mips_set_count_resume(struct kvm_vcpu *vcpu, s64 count_resume); |
723 | int kvm_mips_set_count_hz(struct kvm_vcpu *vcpu, s64 count_hz); | ||
723 | void kvm_mips_count_enable_cause(struct kvm_vcpu *vcpu); | 724 | void kvm_mips_count_enable_cause(struct kvm_vcpu *vcpu); |
724 | void kvm_mips_count_disable_cause(struct kvm_vcpu *vcpu); | 725 | void kvm_mips_count_disable_cause(struct kvm_vcpu *vcpu); |
725 | enum hrtimer_restart kvm_mips_count_timeout(struct kvm_vcpu *vcpu); | 726 | enum hrtimer_restart kvm_mips_count_timeout(struct kvm_vcpu *vcpu); |
diff --git a/arch/mips/include/uapi/asm/kvm.h b/arch/mips/include/uapi/asm/kvm.h index f859fbada1f7..2c04b6d9ff85 100644 --- a/arch/mips/include/uapi/asm/kvm.h +++ b/arch/mips/include/uapi/asm/kvm.h | |||
@@ -133,6 +133,13 @@ struct kvm_fpu { | |||
133 | */ | 133 | */ |
134 | #define KVM_REG_MIPS_COUNT_RESUME (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \ | 134 | #define KVM_REG_MIPS_COUNT_RESUME (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \ |
135 | 0x20000 | 1) | 135 | 0x20000 | 1) |
136 | /* | ||
137 | * CP0_Count rate in Hz | ||
138 | * Specifies the rate of the CP0_Count timer in Hz. Modifications occur without | ||
139 | * discontinuities in CP0_Count. | ||
140 | */ | ||
141 | #define KVM_REG_MIPS_COUNT_HZ (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \ | ||
142 | 0x20000 | 2) | ||
136 | 143 | ||
137 | /* | 144 | /* |
138 | * KVM MIPS specific structures and definitions | 145 | * KVM MIPS specific structures and definitions |
diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index a2d5d4243f51..7ccb7a532c55 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c | |||
@@ -546,6 +546,7 @@ static u64 kvm_mips_get_one_regs[] = { | |||
546 | 546 | ||
547 | KVM_REG_MIPS_COUNT_CTL, | 547 | KVM_REG_MIPS_COUNT_CTL, |
548 | KVM_REG_MIPS_COUNT_RESUME, | 548 | KVM_REG_MIPS_COUNT_RESUME, |
549 | KVM_REG_MIPS_COUNT_HZ, | ||
549 | }; | 550 | }; |
550 | 551 | ||
551 | static int kvm_mips_get_reg(struct kvm_vcpu *vcpu, | 552 | static int kvm_mips_get_reg(struct kvm_vcpu *vcpu, |
@@ -627,6 +628,7 @@ static int kvm_mips_get_reg(struct kvm_vcpu *vcpu, | |||
627 | case KVM_REG_MIPS_CP0_COUNT: | 628 | case KVM_REG_MIPS_CP0_COUNT: |
628 | case KVM_REG_MIPS_COUNT_CTL: | 629 | case KVM_REG_MIPS_COUNT_CTL: |
629 | case KVM_REG_MIPS_COUNT_RESUME: | 630 | case KVM_REG_MIPS_COUNT_RESUME: |
631 | case KVM_REG_MIPS_COUNT_HZ: | ||
630 | ret = kvm_mips_callbacks->get_one_reg(vcpu, reg, &v); | 632 | ret = kvm_mips_callbacks->get_one_reg(vcpu, reg, &v); |
631 | if (ret) | 633 | if (ret) |
632 | return ret; | 634 | return ret; |
@@ -724,6 +726,7 @@ static int kvm_mips_set_reg(struct kvm_vcpu *vcpu, | |||
724 | case KVM_REG_MIPS_CP0_CAUSE: | 726 | case KVM_REG_MIPS_CP0_CAUSE: |
725 | case KVM_REG_MIPS_COUNT_CTL: | 727 | case KVM_REG_MIPS_COUNT_CTL: |
726 | case KVM_REG_MIPS_COUNT_RESUME: | 728 | case KVM_REG_MIPS_COUNT_RESUME: |
729 | case KVM_REG_MIPS_COUNT_HZ: | ||
727 | return kvm_mips_callbacks->set_one_reg(vcpu, reg, v); | 730 | return kvm_mips_callbacks->set_one_reg(vcpu, reg, v); |
728 | default: | 731 | default: |
729 | return -EINVAL; | 732 | return -EINVAL; |
diff --git a/arch/mips/kvm/kvm_mips_emul.c b/arch/mips/kvm/kvm_mips_emul.c index 65c8dea6d1f5..6d257384c9b4 100644 --- a/arch/mips/kvm/kvm_mips_emul.c +++ b/arch/mips/kvm/kvm_mips_emul.c | |||
@@ -498,6 +498,54 @@ void kvm_mips_init_count(struct kvm_vcpu *vcpu) | |||
498 | } | 498 | } |
499 | 499 | ||
500 | /** | 500 | /** |
501 | * kvm_mips_set_count_hz() - Update the frequency of the timer. | ||
502 | * @vcpu: Virtual CPU. | ||
503 | * @count_hz: Frequency of CP0_Count timer in Hz. | ||
504 | * | ||
505 | * Change the frequency of the CP0_Count timer. This is done atomically so that | ||
506 | * CP0_Count is continuous and no timer interrupt is lost. | ||
507 | * | ||
508 | * Returns: -EINVAL if @count_hz is out of range. | ||
509 | * 0 on success. | ||
510 | */ | ||
511 | int kvm_mips_set_count_hz(struct kvm_vcpu *vcpu, s64 count_hz) | ||
512 | { | ||
513 | struct mips_coproc *cop0 = vcpu->arch.cop0; | ||
514 | int dc; | ||
515 | ktime_t now; | ||
516 | u32 count; | ||
517 | |||
518 | /* ensure the frequency is in a sensible range... */ | ||
519 | if (count_hz <= 0 || count_hz > NSEC_PER_SEC) | ||
520 | return -EINVAL; | ||
521 | /* ... and has actually changed */ | ||
522 | if (vcpu->arch.count_hz == count_hz) | ||
523 | return 0; | ||
524 | |||
525 | /* Safely freeze timer so we can keep it continuous */ | ||
526 | dc = kvm_mips_count_disabled(vcpu); | ||
527 | if (dc) { | ||
528 | now = kvm_mips_count_time(vcpu); | ||
529 | count = kvm_read_c0_guest_count(cop0); | ||
530 | } else { | ||
531 | now = kvm_mips_freeze_hrtimer(vcpu, &count); | ||
532 | } | ||
533 | |||
534 | /* Update the frequency */ | ||
535 | vcpu->arch.count_hz = count_hz; | ||
536 | vcpu->arch.count_period = div_u64((u64)NSEC_PER_SEC << 32, count_hz); | ||
537 | vcpu->arch.count_dyn_bias = 0; | ||
538 | |||
539 | /* Calculate adjusted bias so dynamic count is unchanged */ | ||
540 | vcpu->arch.count_bias = count - kvm_mips_ktime_to_count(vcpu, now); | ||
541 | |||
542 | /* Update and resume hrtimer */ | ||
543 | if (!dc) | ||
544 | kvm_mips_resume_hrtimer(vcpu, now, count); | ||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | /** | ||
501 | * kvm_mips_write_compare() - Modify compare and update timer. | 549 | * kvm_mips_write_compare() - Modify compare and update timer. |
502 | * @vcpu: Virtual CPU. | 550 | * @vcpu: Virtual CPU. |
503 | * @compare: New CP0_Compare value. | 551 | * @compare: New CP0_Compare value. |
diff --git a/arch/mips/kvm/kvm_trap_emul.c b/arch/mips/kvm/kvm_trap_emul.c index 854502bcc749..b171db324cf0 100644 --- a/arch/mips/kvm/kvm_trap_emul.c +++ b/arch/mips/kvm/kvm_trap_emul.c | |||
@@ -415,6 +415,9 @@ static int kvm_trap_emul_get_one_reg(struct kvm_vcpu *vcpu, | |||
415 | case KVM_REG_MIPS_COUNT_RESUME: | 415 | case KVM_REG_MIPS_COUNT_RESUME: |
416 | *v = ktime_to_ns(vcpu->arch.count_resume); | 416 | *v = ktime_to_ns(vcpu->arch.count_resume); |
417 | break; | 417 | break; |
418 | case KVM_REG_MIPS_COUNT_HZ: | ||
419 | *v = vcpu->arch.count_hz; | ||
420 | break; | ||
418 | default: | 421 | default: |
419 | return -EINVAL; | 422 | return -EINVAL; |
420 | } | 423 | } |
@@ -461,6 +464,9 @@ static int kvm_trap_emul_set_one_reg(struct kvm_vcpu *vcpu, | |||
461 | case KVM_REG_MIPS_COUNT_RESUME: | 464 | case KVM_REG_MIPS_COUNT_RESUME: |
462 | ret = kvm_mips_set_count_resume(vcpu, v); | 465 | ret = kvm_mips_set_count_resume(vcpu, v); |
463 | break; | 466 | break; |
467 | case KVM_REG_MIPS_COUNT_HZ: | ||
468 | ret = kvm_mips_set_count_hz(vcpu, v); | ||
469 | break; | ||
464 | default: | 470 | default: |
465 | return -EINVAL; | 471 | return -EINVAL; |
466 | } | 472 | } |