]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
KVM: x86/xen: avoid blocking in hardirq context in kvm_xen_set_evtchn_fast()
authorPaul Durrant <pdurrant@amazon.com>
Mon, 4 Dec 2023 10:06:08 +0000 (10:06 +0000)
committerDavid Woodhouse <dwmw@amazon.co.uk>
Fri, 8 Mar 2024 14:58:52 +0000 (14:58 +0000)
As described in [1] compiling with CONFIG_PROVE_RAW_LOCK_NESTING shows that
kvm_xen_set_evtchn_fast() is blocking on pfncache locks in IRQ context.
There is only actually blocking with PREEMPT_RT because the locks will
turned into mutexes. There is no 'raw' version of rwlock_t that can be used
to avoid that, so use read_trylock() and treat failure to lock the same as
an invalid cache.

[1] https://lore.kernel.org/lkml/99771ef3a4966a01fefd3adbb2ba9c3a75f97cf2.camel@infradead.org/T/#mbd06e5a04534ce9c0ee94bd8f1e8d942b2d45bd6
Fixes: 77c9b9dea4fb ("KVM: x86/xen: Use fast path for Xen timer delivery")
Signed-off-by: Paul Durrant <pdurrant@amazon.com>
Reviewed-by: David Woodhouse <dwmw@amazon.co.uk>
---
Cc: Sean Christopherson <seanjc@google.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: x86@kernel.org
v2:
 • Use read_trylock only in interrupt context, to avoid concerns about
   unfairness in the slow path.

arch/x86/kvm/xen.c

index c2eb5784019bbc4037fe54e18f90826f66e3fdab..4028e824aae2ac794f82a3fd93a2cef962081e5a 100644 (file)
@@ -1750,9 +1750,15 @@ static int set_shinfo_evtchn_pending(struct kvm_vcpu *vcpu, u32 port)
        unsigned long flags;
        int rc = -EWOULDBLOCK;
 
-       read_lock_irqsave(&gpc->lock, flags);
+       if (in_interrupt()) {
+               if (!read_trylock_irqsave(&gpc->lock, &flags))
+                       goto out;
+       } else {
+               read_lock_irqsave(&gpc->lock, flags);
+       }
+
        if (!kvm_gpc_check(gpc, PAGE_SIZE))
-               goto out;
+               goto out_unlock;
 
        if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
                struct shared_info *shinfo = gpc->khva;
@@ -1775,8 +1781,9 @@ static int set_shinfo_evtchn_pending(struct kvm_vcpu *vcpu, u32 port)
                rc = 1; /* It is newly raised */
        }
 
- out:
+ out_unlock:
        read_unlock_irqrestore(&gpc->lock, flags);
+ out:
        return rc;
 }
 
@@ -1786,21 +1793,22 @@ static bool set_vcpu_info_evtchn_pending(struct kvm_vcpu *vcpu, u32 port)
        struct gfn_to_pfn_cache *gpc = &vcpu->arch.xen.vcpu_info_cache;
        unsigned long flags;
        bool kick_vcpu = false;
+       bool locked;
 
-       read_lock_irqsave(&gpc->lock, flags);
+       locked = read_trylock_irqsave(&gpc->lock, &flags);
 
        /*
         * Try to deliver the event directly to the vcpu_info. If successful and
         * the guest is using upcall_vector delivery, send the MSI.
-        * If the pfncache is invalid, set the shadow. In this case, or if the
-        * guest is using another form of event delivery, the vCPU must be
-        * kicked to complete the delivery.
+        * If the pfncache lock is contended or the cache is invalid, set the
+        * shadow. In this case, or if the guest is using another form of event
+        * delivery, the vCPU must be kicked to complete the delivery.
         */
        if (IS_ENABLED(CONFIG_64BIT) && kvm->arch.xen.long_mode) {
                struct vcpu_info *vcpu_info = gpc->khva;
                int port_word_bit = port / 64;
 
-               if (!kvm_gpc_check(gpc, sizeof(*vcpu_info))) {
+               if ((!locked || !kvm_gpc_check(gpc, sizeof(*vcpu_info)))) {
                        if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
                                kick_vcpu = true;
                        goto out;
@@ -1814,7 +1822,7 @@ static bool set_vcpu_info_evtchn_pending(struct kvm_vcpu *vcpu, u32 port)
                struct compat_vcpu_info *vcpu_info = gpc->khva;
                int port_word_bit = port / 32;
 
-               if (!kvm_gpc_check(gpc, sizeof(*vcpu_info))) {
+               if ((!locked || !kvm_gpc_check(gpc, sizeof(*vcpu_info)))) {
                        if (!test_and_set_bit(port_word_bit, &vcpu->arch.xen.evtchn_pending_sel))
                                kick_vcpu = true;
                        goto out;
@@ -1833,7 +1841,9 @@ static bool set_vcpu_info_evtchn_pending(struct kvm_vcpu *vcpu, u32 port)
        }
 
  out:
-       read_unlock_irqrestore(&gpc->lock, flags);
+       if (locked)
+               read_unlock_irqrestore(&gpc->lock, flags);
+
        return kick_vcpu;
 }