summaryrefslogtreecommitdiff
path: root/virt
diff options
context:
space:
mode:
authorMarc Zyngier <marc.zyngier@arm.com>2016-01-29 19:04:48 +0000
committerMarc Zyngier <marc.zyngier@arm.com>2016-02-29 18:34:22 +0000
commit9b4a3004439d5be680faf41f4267968ca11bb9f6 (patch)
tree7d0105f0da12af41b04a23dd4e4f65066d80d6f0 /virt
parentd06a5440a02cf8ff67b1cd4ee75a30b1b1c66cff (diff)
downloadlwn-9b4a3004439d5be680faf41f4267968ca11bb9f6.tar.gz
lwn-9b4a3004439d5be680faf41f4267968ca11bb9f6.zip
KVM: arm/arm64: timer: Add active state caching
Programming the active state in the (re)distributor can be an expensive operation so it makes some sense to try and reduce the number of accesses as much as possible. So far, we program the active state on each VM entry, but there is some opportunity to do less. An obvious solution is to cache the active state in memory, and only program it in the HW when conditions change. But because the HW can also change things under our feet (the active state can transition from 1 to 0 when the guest does an EOI), some precautions have to be taken, which amount to only caching an "inactive" state, and always programing it otherwise. With this in place, we observe a reduction of around 700 cycles on a 2GHz GICv2 platform for a NULL hypercall. Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Diffstat (limited to 'virt')
-rw-r--r--virt/kvm/arm/arch_timer.c31
1 files changed, 31 insertions, 0 deletions
diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
index ea6064696fe4..a9ad4fe3f68f 100644
--- a/virt/kvm/arm/arch_timer.c
+++ b/virt/kvm/arm/arch_timer.c
@@ -34,6 +34,11 @@ static struct timecounter *timecounter;
static struct workqueue_struct *wqueue;
static unsigned int host_vtimer_irq;
+void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu)
+{
+ vcpu->arch.timer_cpu.active_cleared_last = false;
+}
+
static cycle_t kvm_phys_timer_read(void)
{
return timecounter->cc->read(timecounter->cc);
@@ -130,6 +135,7 @@ static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level)
BUG_ON(!vgic_initialized(vcpu->kvm));
+ timer->active_cleared_last = false;
timer->irq.level = new_level;
trace_kvm_timer_update_irq(vcpu->vcpu_id, timer->map->virt_irq,
timer->irq.level);
@@ -245,10 +251,35 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
else
phys_active = false;
+ /*
+ * We want to avoid hitting the (re)distributor as much as
+ * possible, as this is a potentially expensive MMIO access
+ * (not to mention locks in the irq layer), and a solution for
+ * this is to cache the "active" state in memory.
+ *
+ * Things to consider: we cannot cache an "active set" state,
+ * because the HW can change this behind our back (it becomes
+ * "clear" in the HW). We must then restrict the caching to
+ * the "clear" state.
+ *
+ * The cache is invalidated on:
+ * - vcpu put, indicating that the HW cannot be trusted to be
+ * in a sane state on the next vcpu load,
+ * - any change in the interrupt state
+ *
+ * Usage conditions:
+ * - cached value is "active clear"
+ * - value to be programmed is "active clear"
+ */
+ if (timer->active_cleared_last && !phys_active)
+ return;
+
ret = irq_set_irqchip_state(timer->map->irq,
IRQCHIP_STATE_ACTIVE,
phys_active);
WARN_ON(ret);
+
+ timer->active_cleared_last = !phys_active;
}
/**