|
@@ -437,32 +437,6 @@ static void kvm_mips_resume_hrtimer(struct kvm_vcpu *vcpu,
|
|
|
hrtimer_start(&vcpu->arch.comparecount_timer, expire, HRTIMER_MODE_ABS);
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * kvm_mips_update_hrtimer() - Update next expiry time of hrtimer.
|
|
|
- * @vcpu: Virtual CPU.
|
|
|
- *
|
|
|
- * Recalculates and updates the expiry time of the hrtimer. This can be used
|
|
|
- * after timer parameters have been altered which do not depend on the time that
|
|
|
- * the change occurs (in those cases kvm_mips_freeze_hrtimer() and
|
|
|
- * kvm_mips_resume_hrtimer() are used directly).
|
|
|
- *
|
|
|
- * It is guaranteed that no timer interrupts will be lost in the process.
|
|
|
- *
|
|
|
- * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running).
|
|
|
- */
|
|
|
-static void kvm_mips_update_hrtimer(struct kvm_vcpu *vcpu)
|
|
|
-{
|
|
|
- ktime_t now;
|
|
|
- uint32_t count;
|
|
|
-
|
|
|
- /*
|
|
|
- * freeze_hrtimer takes care of a timer interrupts <= count, and
|
|
|
- * resume_hrtimer the hrtimer takes care of a timer interrupts > count.
|
|
|
- */
|
|
|
- now = kvm_mips_freeze_hrtimer(vcpu, &count);
|
|
|
- kvm_mips_resume_hrtimer(vcpu, now, count);
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* kvm_mips_write_count() - Modify the count and update timer.
|
|
|
* @vcpu: Virtual CPU.
|
|
@@ -558,23 +532,42 @@ int kvm_mips_set_count_hz(struct kvm_vcpu *vcpu, s64 count_hz)
|
|
|
* kvm_mips_write_compare() - Modify compare and update timer.
|
|
|
* @vcpu: Virtual CPU.
|
|
|
* @compare: New CP0_Compare value.
|
|
|
+ * @ack: Whether to acknowledge timer interrupt.
|
|
|
*
|
|
|
* Update CP0_Compare to a new value and update the timeout.
|
|
|
+ * If @ack, atomically acknowledge any pending timer interrupt, otherwise ensure
|
|
|
+ * any pending timer interrupt is preserved.
|
|
|
*/
|
|
|
-void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare)
|
|
|
+void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare, bool ack)
|
|
|
{
|
|
|
struct mips_coproc *cop0 = vcpu->arch.cop0;
|
|
|
+ int dc;
|
|
|
+ u32 old_compare = kvm_read_c0_guest_compare(cop0);
|
|
|
+ ktime_t now;
|
|
|
+ uint32_t count;
|
|
|
|
|
|
/* if unchanged, must just be an ack */
|
|
|
- if (kvm_read_c0_guest_compare(cop0) == compare)
|
|
|
+ if (old_compare == compare) {
|
|
|
+ if (!ack)
|
|
|
+ return;
|
|
|
+ kvm_mips_callbacks->dequeue_timer_int(vcpu);
|
|
|
+ kvm_write_c0_guest_compare(cop0, compare);
|
|
|
return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* freeze_hrtimer() takes care of timer interrupts <= count */
|
|
|
+ dc = kvm_mips_count_disabled(vcpu);
|
|
|
+ if (!dc)
|
|
|
+ now = kvm_mips_freeze_hrtimer(vcpu, &count);
|
|
|
+
|
|
|
+ if (ack)
|
|
|
+ kvm_mips_callbacks->dequeue_timer_int(vcpu);
|
|
|
|
|
|
- /* Update compare */
|
|
|
kvm_write_c0_guest_compare(cop0, compare);
|
|
|
|
|
|
- /* Update timeout if count enabled */
|
|
|
- if (!kvm_mips_count_disabled(vcpu))
|
|
|
- kvm_mips_update_hrtimer(vcpu);
|
|
|
+ /* resume_hrtimer() takes care of timer interrupts > count */
|
|
|
+ if (!dc)
|
|
|
+ kvm_mips_resume_hrtimer(vcpu, now, count);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1113,9 +1106,9 @@ enum emulation_result kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc,
|
|
|
|
|
|
/* If we are writing to COMPARE */
|
|
|
/* Clear pending timer interrupt, if any */
|
|
|
- kvm_mips_callbacks->dequeue_timer_int(vcpu);
|
|
|
kvm_mips_write_compare(vcpu,
|
|
|
- vcpu->arch.gprs[rt]);
|
|
|
+ vcpu->arch.gprs[rt],
|
|
|
+ true);
|
|
|
} else if ((rd == MIPS_CP0_STATUS) && (sel == 0)) {
|
|
|
unsigned int old_val, val, change;
|
|
|
|