Commit ed9eb2855071faa632c8e04c6cbe3a11c086aa5b

Authored by David Hildenbrand
Committed by Greg Kroah-Hartman
1 parent d204feb294

KVM: s390: forward hrtimer if guest ckc not pending yet

commit 2d00f759427bb3ed963b60f570830e9eca7e1c69 upstream.

Patch 0759d0681cae ("KVM: s390: cleanup handle_wait by reusing
kvm_vcpu_block") changed the way pending guest clock comparator
interrupts are detected. It was assumed that as soon as the hrtimer
wakes up, the condition for the guest ckc is satisfied.

This is however only true as long as adjclock() doesn't speed
up the monotonic clock. Reason is that the hrtimer is based on
CLOCK_MONOTONIC, the guest clock comparator detection is based
on the raw TOD clock. If CLOCK_MONOTONIC runs faster than the
TOD clock, the hrtimer wakes the target VCPU up too early and
the target VCPU will not detect any pending interrupts, therefore
going back to sleep. It will never be woken up again because the
hrtimer has finished. The VCPU is stuck.

As a quick fix, we have to forward the hrtimer until the guest
clock comparator is really due, to guarantee properly timed wake
ups.

As the hrtimer callback might be triggered on another cpu, we
have to make sure that the timer is really stopped and not currently
executing the callback on another cpu. This can happen if the vcpu
thread is scheduled onto another physical cpu, but the timer base
is not migrated. So lets use hrtimer_cancel instead of try_to_cancel.

A proper fix might be to introduce a RAW based hrtimer.

Reported-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: David Hildenbrand <dahi@linux.vnet.ibm.com>
Acked-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Showing 1 changed file with 12 additions and 2 deletions Side-by-side Diff

arch/s390/kvm/interrupt.c
... ... @@ -613,7 +613,7 @@
613 613 __unset_cpu_idle(vcpu);
614 614 vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
615 615  
616   - hrtimer_try_to_cancel(&vcpu->arch.ckc_timer);
  616 + hrtimer_cancel(&vcpu->arch.ckc_timer);
617 617 return 0;
618 618 }
619 619  
620 620  
621 621  
... ... @@ -633,10 +633,20 @@
633 633 enum hrtimer_restart kvm_s390_idle_wakeup(struct hrtimer *timer)
634 634 {
635 635 struct kvm_vcpu *vcpu;
  636 + u64 now, sltime;
636 637  
637 638 vcpu = container_of(timer, struct kvm_vcpu, arch.ckc_timer);
638   - kvm_s390_vcpu_wakeup(vcpu);
  639 + now = get_tod_clock_fast() + vcpu->arch.sie_block->epoch;
  640 + sltime = tod_to_ns(vcpu->arch.sie_block->ckc - now);
639 641  
  642 + /*
  643 + * If the monotonic clock runs faster than the tod clock we might be
  644 + * woken up too early and have to go back to sleep to avoid deadlocks.
  645 + */
  646 + if (vcpu->arch.sie_block->ckc > now &&
  647 + hrtimer_forward_now(timer, ns_to_ktime(sltime)))
  648 + return HRTIMER_RESTART;
  649 + kvm_s390_vcpu_wakeup(vcpu);
640 650 return HRTIMER_NORESTART;
641 651 }
642 652