]> git.karo-electronics.de Git - linux-beck.git/commitdiff
KVM: arm/arm64: timer: Add active state caching
authorMarc Zyngier <marc.zyngier@arm.com>
Fri, 29 Jan 2016 19:04:48 +0000 (19:04 +0000)
committerMarc Zyngier <marc.zyngier@arm.com>
Mon, 29 Feb 2016 18:34:22 +0000 (18:34 +0000)
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>
arch/arm/kvm/arm.c
include/kvm/arm_arch_timer.h
virt/kvm/arm/arch_timer.c

index 75c7fed5d14c98d93ffa0cf27c7d93ceddd9bc9f..9ca653e34d8ca7a85af3aeb56b556d6b4cc358dc 100644 (file)
@@ -322,6 +322,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
        vcpu->cpu = -1;
 
        kvm_arm_set_running_vcpu(NULL);
+       kvm_timer_vcpu_put(vcpu);
 }
 
 int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
index 1800227af9d61fa31fb525a0d3f7408f2c9ab43e..b651aed9dc6b9084ed2dcae8832a033217d4dca4 100644 (file)
@@ -55,6 +55,9 @@ struct arch_timer_cpu {
 
        /* VGIC mapping */
        struct irq_phys_map             *map;
+
+       /* Active IRQ state caching */
+       bool                            active_cleared_last;
 };
 
 int kvm_timer_hyp_init(void);
@@ -74,4 +77,6 @@ bool kvm_timer_should_fire(struct kvm_vcpu *vcpu);
 void kvm_timer_schedule(struct kvm_vcpu *vcpu);
 void kvm_timer_unschedule(struct kvm_vcpu *vcpu);
 
+void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu);
+
 #endif
index ea6064696fe43867d1d1baee35059b276d84817b..a9ad4fe3f68f07848876403985e19e4c544ef1d0 100644 (file)
@@ -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;
 }
 
 /**