* [PATCH 0/5] MIPS: KVM: A few misc fixes
@ 2016-04-22 9:38 James Hogan
2016-04-22 9:38 ` [PATCH 1/5] MIPS: KVM: Fix timer IRQ race when freezing timer James Hogan
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: James Hogan @ 2016-04-22 9:38 UTC (permalink / raw)
To: Paolo Bonzini, Radim Krčmář, Ralf Baechle
Cc: James Hogan, linux-mips, kvm, stable
Here are a few misc fixes for KVM on MIPS, including 2 guest timer race
fixes, 2 preemption fixes, and missing hazard barriers after disabling
FPU.
James Hogan (5):
MIPS: KVM: Fix timer IRQ race when freezing timer
MIPS: KVM: Fix timer IRQ race when writing CP0_Compare
MIPS: KVM: Fix preemptable kvm_mips_get_*_asid() calls
MIPS: KVM: Fix preemption warning reading FPU capability
MIPS: KVM: Add missing disable FPU hazard barriers
arch/mips/include/asm/kvm_host.h | 2 +-
arch/mips/kvm/emulate.c | 89 ++++++++++++++++++++++------------------
arch/mips/kvm/mips.c | 8 +++-
arch/mips/kvm/tlb.c | 32 ++++++++++-----
arch/mips/kvm/trap_emul.c | 2 +-
5 files changed, 79 insertions(+), 54 deletions(-)
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: "Radim Krčmář" <rkrcmar@redhat.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: linux-mips@linux-mips.org
Cc: kvm@vger.kernel.org
Cc: <stable@vger.kernel.org>
--
2.4.10
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/5] MIPS: KVM: Fix timer IRQ race when freezing timer
2016-04-22 9:38 [PATCH 0/5] MIPS: KVM: A few misc fixes James Hogan
@ 2016-04-22 9:38 ` James Hogan
2016-04-22 9:38 ` [PATCH 2/5] MIPS: KVM: Fix timer IRQ race when writing CP0_Compare James Hogan
2016-05-10 13:02 ` [PATCH 0/5] MIPS: KVM: A few misc fixes Paolo Bonzini
2 siblings, 0 replies; 5+ messages in thread
From: James Hogan @ 2016-04-22 9:38 UTC (permalink / raw)
To: Paolo Bonzini, Radim Krčmář, Ralf Baechle
Cc: James Hogan, linux-mips, kvm, stable
There's a particularly narrow and subtle race condition when the
software emulated guest timer is frozen which can allow a guest timer
interrupt to be missed.
This happens due to the hrtimer expiry being inexact, so very
occasionally the freeze time will be after the moment when the emulated
CP0_Count transitions to the same value as CP0_Compare (so an IRQ should
be generated), but before the moment when the hrtimer is due to expire
(so no IRQ is generated). The IRQ won't be generated when the timer is
resumed either, since the resume CP0_Count will already match CP0_Compare.
With VZ guests in particular this is far more likely to happen, since
the soft timer may be frozen frequently in order to restore the timer
state to the hardware guest timer. This happens after 5-10 hours of
guest soak testing, resulting in an overflow in guest kernel timekeeping
calculations, hanging the guest. A more focussed test case to
intentionally hit the race (with the help of a new hypcall to cause the
timer state to migrated between hardware & software) hits the condition
fairly reliably within around 30 seconds.
Instead of relying purely on the inexact hrtimer expiry to determine
whether an IRQ should be generated, read the guest CP0_Compare and
directly check whether the freeze time is before or after it. Only if
CP0_Count is on or after CP0_Compare do we check the hrtimer expiry to
determine whether the last IRQ has already been generated (which will
have pushed back the expiry by one timer period).
Fixes: e30492bbe95a ("MIPS: KVM: Rewrite count/compare timer emulation")
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: "Radim Krčmář" <rkrcmar@redhat.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: linux-mips@linux-mips.org
Cc: kvm@vger.kernel.org
Cc: <stable@vger.kernel.org> # 3.16.x-
---
arch/mips/kvm/emulate.c | 28 +++++++++++++++++++++++-----
1 file changed, 23 insertions(+), 5 deletions(-)
diff --git a/arch/mips/kvm/emulate.c b/arch/mips/kvm/emulate.c
index b37954cc880d..ab5681de6ece 100644
--- a/arch/mips/kvm/emulate.c
+++ b/arch/mips/kvm/emulate.c
@@ -302,12 +302,31 @@ static inline ktime_t kvm_mips_count_time(struct kvm_vcpu *vcpu)
*/
static uint32_t kvm_mips_read_count_running(struct kvm_vcpu *vcpu, ktime_t now)
{
- ktime_t expires;
+ struct mips_coproc *cop0 = vcpu->arch.cop0;
+ ktime_t expires, threshold;
+ uint32_t count, compare;
int running;
- /* Is the hrtimer pending? */
+ /* Calculate the biased and scaled guest CP0_Count */
+ count = vcpu->arch.count_bias + kvm_mips_ktime_to_count(vcpu, now);
+ compare = kvm_read_c0_guest_compare(cop0);
+
+ /*
+ * Find whether CP0_Count has reached the closest timer interrupt. If
+ * not, we shouldn't inject it.
+ */
+ if ((int32_t)(count - compare) < 0)
+ return count;
+
+ /*
+ * The CP0_Count we're going to return has already reached the closest
+ * timer interrupt. Quickly check if it really is a new interrupt by
+ * looking at whether the interval until the hrtimer expiry time is
+ * less than 1/4 of the timer period.
+ */
expires = hrtimer_get_expires(&vcpu->arch.comparecount_timer);
- if (ktime_compare(now, expires) >= 0) {
+ threshold = ktime_add_ns(now, vcpu->arch.count_period / 4);
+ if (ktime_before(expires, threshold)) {
/*
* Cancel it while we handle it so there's no chance of
* interference with the timeout handler.
@@ -329,8 +348,7 @@ static uint32_t kvm_mips_read_count_running(struct kvm_vcpu *vcpu, ktime_t now)
}
}
- /* Return the biased and scaled guest CP0_Count */
- return vcpu->arch.count_bias + kvm_mips_ktime_to_count(vcpu, now);
+ return count;
}
/**
--
2.4.10
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/5] MIPS: KVM: Fix timer IRQ race when writing CP0_Compare
2016-04-22 9:38 [PATCH 0/5] MIPS: KVM: A few misc fixes James Hogan
2016-04-22 9:38 ` [PATCH 1/5] MIPS: KVM: Fix timer IRQ race when freezing timer James Hogan
@ 2016-04-22 9:38 ` James Hogan
2016-05-10 13:02 ` [PATCH 0/5] MIPS: KVM: A few misc fixes Paolo Bonzini
2 siblings, 0 replies; 5+ messages in thread
From: James Hogan @ 2016-04-22 9:38 UTC (permalink / raw)
To: Paolo Bonzini, Radim Krčmář, Ralf Baechle
Cc: James Hogan, linux-mips, kvm, stable
Writing CP0_Compare clears the timer interrupt pending bit
(CP0_Cause.TI), but this wasn't being done atomically. If a timer
interrupt raced with the write of the guest CP0_Compare, the timer
interrupt could end up being pending even though the new CP0_Compare is
nowhere near CP0_Count.
We were already updating the hrtimer expiry with
kvm_mips_update_hrtimer(), which used both kvm_mips_freeze_hrtimer() and
kvm_mips_resume_hrtimer(). Close the race window by expanding out
kvm_mips_update_hrtimer(), and clearing CP0_Cause.TI and setting
CP0_Compare between the freeze and resume. Since the pending timer
interrupt should not be cleared when CP0_Compare is written via the KVM
user API, an ack argument is added to distinguish the source of the
write.
Fixes: e30492bbe95a ("MIPS: KVM: Rewrite count/compare timer emulation")
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: "Radim Krčmář" <rkrcmar@redhat.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: linux-mips@linux-mips.org
Cc: kvm@vger.kernel.org
Cc: <stable@vger.kernel.org> # 3.16.x-
---
arch/mips/include/asm/kvm_host.h | 2 +-
arch/mips/kvm/emulate.c | 61 ++++++++++++++++++----------------------
arch/mips/kvm/trap_emul.c | 2 +-
3 files changed, 29 insertions(+), 36 deletions(-)
diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h
index f6b12790716c..942b8f6bf35b 100644
--- a/arch/mips/include/asm/kvm_host.h
+++ b/arch/mips/include/asm/kvm_host.h
@@ -747,7 +747,7 @@ extern enum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu,
uint32_t kvm_mips_read_count(struct kvm_vcpu *vcpu);
void kvm_mips_write_count(struct kvm_vcpu *vcpu, uint32_t count);
-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);
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);
diff --git a/arch/mips/kvm/emulate.c b/arch/mips/kvm/emulate.c
index ab5681de6ece..b8b7860ec1a8 100644
--- a/arch/mips/kvm/emulate.c
+++ b/arch/mips/kvm/emulate.c
@@ -438,32 +438,6 @@ static void kvm_mips_resume_hrtimer(struct kvm_vcpu *vcpu,
}
/**
- * 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.
* @count: Guest CP0_Count value to set.
@@ -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;
diff --git a/arch/mips/kvm/trap_emul.c b/arch/mips/kvm/trap_emul.c
index c4038d2a724c..caa5ea1038a0 100644
--- a/arch/mips/kvm/trap_emul.c
+++ b/arch/mips/kvm/trap_emul.c
@@ -546,7 +546,7 @@ static int kvm_trap_emul_set_one_reg(struct kvm_vcpu *vcpu,
kvm_mips_write_count(vcpu, v);
break;
case KVM_REG_MIPS_CP0_COMPARE:
- kvm_mips_write_compare(vcpu, v);
+ kvm_mips_write_compare(vcpu, v, false);
break;
case KVM_REG_MIPS_CP0_CAUSE:
/*
--
2.4.10
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 0/5] MIPS: KVM: A few misc fixes
2016-04-22 9:38 [PATCH 0/5] MIPS: KVM: A few misc fixes James Hogan
2016-04-22 9:38 ` [PATCH 1/5] MIPS: KVM: Fix timer IRQ race when freezing timer James Hogan
2016-04-22 9:38 ` [PATCH 2/5] MIPS: KVM: Fix timer IRQ race when writing CP0_Compare James Hogan
@ 2016-05-10 13:02 ` Paolo Bonzini
2016-05-10 13:56 ` James Hogan
2 siblings, 1 reply; 5+ messages in thread
From: Paolo Bonzini @ 2016-05-10 13:02 UTC (permalink / raw)
To: James Hogan, Radim Krčmář, Ralf Baechle
Cc: linux-mips, kvm, stable
On 22/04/2016 11:38, James Hogan wrote:
> Here are a few misc fixes for KVM on MIPS, including 2 guest timer race
> fixes, 2 preemption fixes, and missing hazard barriers after disabling
> FPU.
>
> James Hogan (5):
> MIPS: KVM: Fix timer IRQ race when freezing timer
> MIPS: KVM: Fix timer IRQ race when writing CP0_Compare
> MIPS: KVM: Fix preemptable kvm_mips_get_*_asid() calls
> MIPS: KVM: Fix preemption warning reading FPU capability
> MIPS: KVM: Add missing disable FPU hazard barriers
>
> arch/mips/include/asm/kvm_host.h | 2 +-
> arch/mips/kvm/emulate.c | 89 ++++++++++++++++++++++------------------
> arch/mips/kvm/mips.c | 8 +++-
> arch/mips/kvm/tlb.c | 32 ++++++++++-----
> arch/mips/kvm/trap_emul.c | 2 +-
> 5 files changed, 79 insertions(+), 54 deletions(-)
>
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Cc: "Radim Krčmář" <rkrcmar@redhat.com>
> Cc: Ralf Baechle <ralf@linux-mips.org>
> Cc: linux-mips@linux-mips.org
> Cc: kvm@vger.kernel.org
> Cc: <stable@vger.kernel.org>
>
Queued to kvm/next, thanks.
Paolo
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 0/5] MIPS: KVM: A few misc fixes
2016-05-10 13:02 ` [PATCH 0/5] MIPS: KVM: A few misc fixes Paolo Bonzini
@ 2016-05-10 13:56 ` James Hogan
0 siblings, 0 replies; 5+ messages in thread
From: James Hogan @ 2016-05-10 13:56 UTC (permalink / raw)
To: Paolo Bonzini
Cc: Radim Krčmář, Ralf Baechle, linux-mips, kvm,
stable
[-- Attachment #1: Type: text/plain, Size: 1276 bytes --]
On Tue, May 10, 2016 at 03:02:11PM +0200, Paolo Bonzini wrote:
>
>
> On 22/04/2016 11:38, James Hogan wrote:
> > Here are a few misc fixes for KVM on MIPS, including 2 guest timer race
> > fixes, 2 preemption fixes, and missing hazard barriers after disabling
> > FPU.
> >
> > James Hogan (5):
> > MIPS: KVM: Fix timer IRQ race when freezing timer
> > MIPS: KVM: Fix timer IRQ race when writing CP0_Compare
> > MIPS: KVM: Fix preemptable kvm_mips_get_*_asid() calls
> > MIPS: KVM: Fix preemption warning reading FPU capability
> > MIPS: KVM: Add missing disable FPU hazard barriers
> >
> > arch/mips/include/asm/kvm_host.h | 2 +-
> > arch/mips/kvm/emulate.c | 89 ++++++++++++++++++++++------------------
> > arch/mips/kvm/mips.c | 8 +++-
> > arch/mips/kvm/tlb.c | 32 ++++++++++-----
> > arch/mips/kvm/trap_emul.c | 2 +-
> > 5 files changed, 79 insertions(+), 54 deletions(-)
> >
> > Cc: Paolo Bonzini <pbonzini@redhat.com>
> > Cc: "Radim Krčmář" <rkrcmar@redhat.com>
> > Cc: Ralf Baechle <ralf@linux-mips.org>
> > Cc: linux-mips@linux-mips.org
> > Cc: kvm@vger.kernel.org
> > Cc: <stable@vger.kernel.org>
> >
>
> Queued to kvm/next, thanks.
Thanks Paolo!
Cheers
James
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2016-05-10 13:56 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-04-22 9:38 [PATCH 0/5] MIPS: KVM: A few misc fixes James Hogan
2016-04-22 9:38 ` [PATCH 1/5] MIPS: KVM: Fix timer IRQ race when freezing timer James Hogan
2016-04-22 9:38 ` [PATCH 2/5] MIPS: KVM: Fix timer IRQ race when writing CP0_Compare James Hogan
2016-05-10 13:02 ` [PATCH 0/5] MIPS: KVM: A few misc fixes Paolo Bonzini
2016-05-10 13:56 ` James Hogan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox