| From: Marcelo Tosatti <mtosatti@redhat.com> |
| Date: Wed, 8 Apr 2015 20:33:25 -0300 |
| Subject: KVM: lapic: mark LAPIC timer handler as irqsafe |
| |
| Since lapic timer handler only wakes up a simple waitqueue, |
| it can be executed from hardirq context. |
| |
| Also handle the case where hrtimer_start_expires fails due to -ETIME, |
| by injecting the interrupt to the guest immediately. |
| |
| Reduces average cyclictest latency by 3us. |
| |
| Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com> |
| Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> |
| --- |
| arch/x86/kvm/lapic.c | 40 +++++++++++++++++++++++++++++++++++++--- |
| 1 file changed, 37 insertions(+), 3 deletions(-) |
| |
| --- a/arch/x86/kvm/lapic.c |
| +++ b/arch/x86/kvm/lapic.c |
| @@ -1167,8 +1167,36 @@ void wait_lapic_expire(struct kvm_vcpu * |
| __delay(tsc_deadline - guest_tsc); |
| } |
| |
| +static enum hrtimer_restart apic_timer_fn(struct hrtimer *data); |
| + |
| +static void __apic_timer_expired(struct hrtimer *data) |
| +{ |
| + int ret, i = 0; |
| + enum hrtimer_restart r; |
| + struct kvm_timer *ktimer = container_of(data, struct kvm_timer, timer); |
| + |
| + r = apic_timer_fn(data); |
| + |
| + if (r == HRTIMER_RESTART) { |
| + do { |
| + ret = hrtimer_start_expires(data, HRTIMER_MODE_ABS); |
| + if (ret == -ETIME) |
| + hrtimer_add_expires_ns(&ktimer->timer, |
| + ktimer->period); |
| + i++; |
| + } while (ret == -ETIME && i < 10); |
| + |
| + if (ret == -ETIME) { |
| + printk_once(KERN_ERR "%s: failed to reprogram timer\n", |
| + __func__); |
| + WARN_ON_ONCE(1); |
| + } |
| + } |
| +} |
| + |
| static void start_apic_timer(struct kvm_lapic *apic) |
| { |
| + int ret; |
| ktime_t now; |
| |
| atomic_set(&apic->lapic_timer.pending, 0); |
| @@ -1199,9 +1227,11 @@ static void start_apic_timer(struct kvm_ |
| } |
| } |
| |
| - hrtimer_start(&apic->lapic_timer.timer, |
| + ret = hrtimer_start(&apic->lapic_timer.timer, |
| ktime_add_ns(now, apic->lapic_timer.period), |
| HRTIMER_MODE_ABS); |
| + if (ret == -ETIME) |
| + __apic_timer_expired(&apic->lapic_timer.timer); |
| |
| apic_debug("%s: bus cycle is %" PRId64 "ns, now 0x%016" |
| PRIx64 ", " |
| @@ -1233,8 +1263,10 @@ static void start_apic_timer(struct kvm_ |
| do_div(ns, this_tsc_khz); |
| expire = ktime_add_ns(now, ns); |
| expire = ktime_sub_ns(expire, lapic_timer_advance_ns); |
| - hrtimer_start(&apic->lapic_timer.timer, |
| + ret = hrtimer_start(&apic->lapic_timer.timer, |
| expire, HRTIMER_MODE_ABS); |
| + if (ret == -ETIME) |
| + __apic_timer_expired(&apic->lapic_timer.timer); |
| } else |
| apic_timer_expired(apic); |
| |
| @@ -1707,6 +1739,7 @@ int kvm_create_lapic(struct kvm_vcpu *vc |
| hrtimer_init(&apic->lapic_timer.timer, CLOCK_MONOTONIC, |
| HRTIMER_MODE_ABS); |
| apic->lapic_timer.timer.function = apic_timer_fn; |
| + apic->lapic_timer.timer.irqsafe = 1; |
| |
| /* |
| * APIC is created enabled. This will prevent kvm_lapic_set_base from |
| @@ -1834,7 +1867,8 @@ void __kvm_migrate_apic_timer(struct kvm |
| |
| timer = &vcpu->arch.apic->lapic_timer.timer; |
| if (hrtimer_cancel(timer)) |
| - hrtimer_start_expires(timer, HRTIMER_MODE_ABS); |
| + if (hrtimer_start_expires(timer, HRTIMER_MODE_ABS) == -ETIME) |
| + __apic_timer_expired(timer); |
| } |
| |
| /* |