From: Paul Durrant Date: Mon, 4 Dec 2023 10:06:08 +0000 (+0000) Subject: KVM: x86/xen: avoid blocking in hardirq context in kvm_xen_set_evtchn_fast() X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=7394ecf583782b093c99177ae4624b5be0f0acfd;p=users%2Fdwmw2%2Flinux.git KVM: x86/xen: avoid blocking in hardirq context in kvm_xen_set_evtchn_fast() 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 Reviewed-by: David Woodhouse --- Cc: Sean Christopherson Cc: Paolo Bonzini Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Borislav Petkov Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Dave Hansen Cc: "H. Peter Anvin" Cc: David Woodhouse Cc: x86@kernel.org v2: • Use read_trylock only in interrupt context, to avoid concerns about unfairness in the slow path. --- diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c index c2eb5784019bb..4028e824aae2a 100644 --- a/arch/x86/kvm/xen.c +++ b/arch/x86/kvm/xen.c @@ -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; }