From: David Woodhouse <[email protected]> A caller which does not hold kvm->srcu may now pass wait=true to kvm_gpc_refresh() or kvm_gpc_activate() to wait out a concurrent memslot update instead of receiving -EAGAIN, looping with cond_resched() until the lookup resolves or a fatal signal is pending. (An HVA-based cache never consults the memslots, so kvm_gpc_activate_hva() keeps its signature.)
The refresh now takes its own kvm->srcu read lock for each attempt, so callers no longer need to hold it. Holding it with wait=true would deadlock — the wait would block the memslot update from completing — and is rejected with a lockdep assertion. Signed-off-by: David Woodhouse <[email protected]> Assisted-by: LLM --- arch/x86/kvm/msrs.c | 2 +- arch/x86/kvm/x86.c | 2 +- arch/x86/kvm/xen.c | 28 +++++----- include/linux/kvm_host.h | 17 ++++-- virt/kvm/pfncache.c | 114 ++++++++++++++++++++++++++++----------- 5 files changed, 112 insertions(+), 51 deletions(-) diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c index dd3bb04878ca..4c0fbba6e7ae 100644 --- a/arch/x86/kvm/msrs.c +++ b/arch/x86/kvm/msrs.c @@ -1280,7 +1280,7 @@ static void kvm_write_system_time(struct kvm_vcpu *vcpu, gpa_t system_time, /* we verify if the enable bit is set... */ if (system_time & 1) kvm_gpc_activate(&vcpu->arch.pv_time, system_time & ~1ULL, - sizeof(struct pvclock_vcpu_time_info)); + sizeof(struct pvclock_vcpu_time_info), false); else kvm_gpc_deactivate(&vcpu->arch.pv_time); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 79468ddfe473..eadcaeee651a 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1727,7 +1727,7 @@ static void kvm_setup_guest_pvclock(struct pvclock_vcpu_time_info *ref_hv_clock, while (!kvm_gpc_check(gpc, offset + sizeof(*guest_hv_clock))) { read_unlock_irqrestore(&gpc->lock, flags); - if (kvm_gpc_refresh(gpc, offset + sizeof(*guest_hv_clock))) + if (kvm_gpc_refresh(gpc, offset + sizeof(*guest_hv_clock), false)) return; read_lock_irqsave(&gpc->lock, flags); diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c index 6aa00f746cba..fae893cd5762 100644 --- a/arch/x86/kvm/xen.c +++ b/arch/x86/kvm/xen.c @@ -50,7 +50,7 @@ static int kvm_xen_shared_info_init(struct kvm *kvm) while (!kvm_gpc_check(gpc, PAGE_SIZE)) { read_unlock_irq(&gpc->lock); - ret = kvm_gpc_refresh(gpc, PAGE_SIZE); + ret = kvm_gpc_refresh(gpc, PAGE_SIZE, false); if (ret) goto out; @@ -161,7 +161,7 @@ static int xen_get_guest_pvclock(struct kvm_vcpu *vcpu, while (!kvm_gpc_check(gpc, offset + sizeof(*hv_clock))) { read_unlock_irqrestore(&gpc->lock, flags); - r = kvm_gpc_refresh(gpc, offset + sizeof(*hv_clock)); + r = kvm_gpc_refresh(gpc, offset + sizeof(*hv_clock), false); if (r) return r; @@ -435,7 +435,7 @@ static void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, bool atomic) if (atomic) return; - if (kvm_gpc_refresh(gpc1, user_len1)) + if (kvm_gpc_refresh(gpc1, user_len1, false)) return; read_lock_irqsave(&gpc1->lock, flags); @@ -488,7 +488,7 @@ static void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, bool atomic) * 64-bit mode, the second GPC won't have been set up. */ if (kvm_gpc_activate(gpc2, gpc1->gpa + user_len1, - user_len2)) + user_len2, false)) return; /* @@ -669,7 +669,7 @@ void kvm_xen_inject_pending_events(struct kvm_vcpu *v) while (!kvm_gpc_check(gpc, sizeof(struct vcpu_info))) { read_unlock_irqrestore(&gpc->lock, flags); - if (kvm_gpc_refresh(gpc, sizeof(struct vcpu_info))) + if (kvm_gpc_refresh(gpc, sizeof(struct vcpu_info), false)) return; read_lock_irqsave(&gpc->lock, flags); @@ -757,7 +757,7 @@ int __kvm_xen_has_interrupt(struct kvm_vcpu *v) if (in_atomic() || !task_is_running(current)) return 1; - if (kvm_gpc_refresh(gpc, sizeof(struct vcpu_info))) { + if (kvm_gpc_refresh(gpc, sizeof(struct vcpu_info), false)) { /* * If this failed, userspace has screwed up the * vcpu_info mapping. No interrupts for you. @@ -813,7 +813,7 @@ int kvm_xen_hvm_set_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data) r = 0; } else { r = kvm_gpc_activate(&kvm->arch.xen.shinfo_cache, - gfn_to_gpa(gfn), PAGE_SIZE); + gfn_to_gpa(gfn), PAGE_SIZE, false); } } else { void __user * hva = u64_to_user_ptr(data->u.shared_info.hva); @@ -825,7 +825,7 @@ int kvm_xen_hvm_set_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data) r = 0; } else { r = kvm_gpc_activate_hva(&kvm->arch.xen.shinfo_cache, - (unsigned long)hva, PAGE_SIZE); + (unsigned long)hva, PAGE_SIZE, false); } } @@ -960,7 +960,7 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data) break; r = kvm_gpc_activate(&vcpu->arch.xen.vcpu_info_cache, - data->u.gpa, sizeof(struct vcpu_info)); + data->u.gpa, sizeof(struct vcpu_info), false); } else { if (data->u.hva == 0) { kvm_gpc_deactivate(&vcpu->arch.xen.vcpu_info_cache); @@ -973,7 +973,7 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data) break; r = kvm_gpc_activate_hva(&vcpu->arch.xen.vcpu_info_cache, - data->u.hva, sizeof(struct vcpu_info)); + data->u.hva, sizeof(struct vcpu_info), false); } if (!r) @@ -990,7 +990,7 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data) r = kvm_gpc_activate(&vcpu->arch.xen.vcpu_time_info_cache, data->u.gpa, - sizeof(struct pvclock_vcpu_time_info)); + sizeof(struct pvclock_vcpu_time_info), false); if (!r) kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu); break; @@ -1023,7 +1023,7 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data) /* How much fits in the (first) page? */ sz1 = PAGE_SIZE - (data->u.gpa & ~PAGE_MASK); r = kvm_gpc_activate(&vcpu->arch.xen.runstate_cache, - data->u.gpa, sz1); + data->u.gpa, sz1, false); if (r) goto deactivate_out; @@ -1034,7 +1034,7 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data) sz2 = sz - sz1; BUG_ON((data->u.gpa + sz1) & ~PAGE_MASK); r = kvm_gpc_activate(&vcpu->arch.xen.runstate2_cache, - data->u.gpa + sz1, sz2); + data->u.gpa + sz1, sz2, false); if (r) goto deactivate_out; } @@ -2011,7 +2011,7 @@ static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm) break; idx = srcu_read_lock(&kvm->srcu); - rc = kvm_gpc_refresh(gpc, PAGE_SIZE); + rc = kvm_gpc_refresh(gpc, PAGE_SIZE, false); srcu_read_unlock(&kvm->srcu, idx); } while(!rc); diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 3dd04605f2e5..88c1ee37550c 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -1474,16 +1474,23 @@ void kvm_gpc_init(struct gfn_to_pfn_cache *gpc, struct kvm *kvm); * @gpc: struct gfn_to_pfn_cache object. * @gpa: guest physical address to map. * @len: sanity check; the range being access must fit a single page. + * @wait: wait out a concurrent memslot update instead of returning + * -EAGAIN. May not be used while this task holds any kvm->srcu + * read section (lockdep-asserted): the update's + * synchronize_srcu() would deadlock against the wait. * * @return: 0 for success. * -EINVAL for a mapping which would cross a page boundary. * -EFAULT for an untranslatable guest physical address. + * -EAGAIN if the memslot is being updated (only with + * wait == false, or on a fatal signal while waiting). * * This primes a gfn_to_pfn_cache and links it into the @gpc->kvm's list for * invalidations to be processed. Callers are required to use kvm_gpc_check() * to ensure that the cache is valid before accessing the target page. */ -int kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long len); +int kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long len, + bool wait); /** * kvm_gpc_activate_hva - prepare a cached kernel mapping and HPA for a given HVA. @@ -1499,7 +1506,8 @@ int kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long len) * The semantics of this function are the same as those of kvm_gpc_activate(). It * merely bypasses a layer of address translation. */ -int kvm_gpc_activate_hva(struct gfn_to_pfn_cache *gpc, unsigned long hva, unsigned long len); +int kvm_gpc_activate_hva(struct gfn_to_pfn_cache *gpc, unsigned long hva, + unsigned long len); /** * kvm_gpc_check - check validity of a gfn_to_pfn_cache. @@ -1524,10 +1532,13 @@ bool kvm_gpc_check(struct gfn_to_pfn_cache *gpc, unsigned long len); * * @gpc: struct gfn_to_pfn_cache object. * @len: sanity check; the range being access must fit a single page. + * @wait: as kvm_gpc_activate(). * * @return: 0 for success. * -EINVAL for a mapping which would cross a page boundary. * -EFAULT for an untranslatable guest physical address. + * -EAGAIN if the memslot is being updated (only with + * wait == false, or on a fatal signal while waiting). * * This will attempt to refresh a gfn_to_pfn_cache. Note that a successful * return from this function does not mean the page can be immediately @@ -1535,7 +1546,7 @@ bool kvm_gpc_check(struct gfn_to_pfn_cache *gpc, unsigned long len); * still lock and check the cache status, as this function does not return * with the lock still held to permit access. */ -int kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, unsigned long len); +int kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, unsigned long len, bool wait); /** * kvm_gpc_deactivate - deactivate and unlink a gfn_to_pfn_cache. diff --git a/virt/kvm/pfncache.c b/virt/kvm/pfncache.c index 17aaebd76db8..8504e26fa111 100644 --- a/virt/kvm/pfncache.c +++ b/virt/kvm/pfncache.c @@ -378,23 +378,57 @@ static int __kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned l return ret; } -int kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, unsigned long len) +/* + * One refresh attempt, under the caller's refresh_lock. Takes a kvm->srcu + * read section for the attempt, as the memslot lookups consult the active + * memslots. + */ +static int gpc_refresh_locked(struct gfn_to_pfn_cache *gpc, gpa_t gpa, + unsigned long uhva) +{ + int idx, ret; + + idx = srcu_read_lock(&gpc->kvm->srcu); + ret = __kvm_gpc_refresh(gpc, gpa, uhva); + srcu_read_unlock(&gpc->kvm->srcu, idx); + + return ret; +} + +int kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, unsigned long len, bool wait) { unsigned long uhva; + int ret; - guard(mutex)(&gpc->refresh_lock); + if (wait) + lockdep_assert(!srcu_read_lock_held(&gpc->kvm->srcu)); - if (!kvm_gpc_is_valid_len(gpc->gpa, gpc->uhva, len)) - return -EINVAL; + for (;;) { + scoped_guard(mutex, &gpc->refresh_lock) { + if (!kvm_gpc_is_valid_len(gpc->gpa, gpc->uhva, len)) + return -EINVAL; - /* - * If the GPA is valid then ignore the HVA, as a cache can be GPA-based - * or HVA-based, not both. For GPA-based caches, the HVA will be - * recomputed during refresh if necessary. - */ - uhva = kvm_is_error_gpa(gpc->gpa) ? gpc->uhva : KVM_HVA_ERR_BAD; + /* + * If the GPA is valid then ignore the HVA, as a cache + * can be GPA-based or HVA-based, not both. For + * GPA-based caches, the HVA will be recomputed during + * refresh if necessary. + */ + uhva = kvm_is_error_gpa(gpc->gpa) ? gpc->uhva : KVM_HVA_ERR_BAD; + + ret = gpc_refresh_locked(gpc, gpc->gpa, uhva); + } + + if (ret != -EAGAIN || !wait || fatal_signal_pending(current)) + return ret; - return __kvm_gpc_refresh(gpc, gpc->gpa, uhva); + /* + * Wait with refresh_lock dropped: a kvm->srcu reader blocked + * on it must be able to proceed and quiesce, or the memslot + * update being waited for could never complete. + */ + cond_resched(); + } } void kvm_gpc_init(struct gfn_to_pfn_cache *gpc, struct kvm *kvm) @@ -410,36 +444,50 @@ void kvm_gpc_init(struct gfn_to_pfn_cache *gpc, struct kvm *kvm) } static int __kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long uhva, - unsigned long len) + unsigned long len, bool wait) { struct kvm *kvm = gpc->kvm; if (!kvm_gpc_is_valid_len(gpa, uhva, len)) return -EINVAL; - guard(mutex)(&gpc->refresh_lock); + if (wait) + lockdep_assert(!srcu_read_lock_held(&kvm->srcu)); - if (!gpc->active) { - if (KVM_BUG_ON(gpc->valid, kvm)) - return -EIO; + for (;;) { + int ret; - spin_lock(&kvm->gpc_lock); - list_add(&gpc->list, &kvm->gpc_list); - spin_unlock(&kvm->gpc_lock); + scoped_guard(mutex, &gpc->refresh_lock) { + if (!gpc->active) { + if (KVM_BUG_ON(gpc->valid, kvm)) + return -EIO; - /* - * Activate the cache after adding it to the list, a concurrent - * refresh must not establish a mapping until the cache is - * reachable by mmu_notifier events. - */ - write_lock_irq(&gpc->lock); - gpc->active = true; - write_unlock_irq(&gpc->lock); + spin_lock(&kvm->gpc_lock); + list_add(&gpc->list, &kvm->gpc_list); + spin_unlock(&kvm->gpc_lock); + + /* + * Activate the cache after adding it to the + * list, a concurrent refresh must not + * establish a mapping until the cache is + * reachable by mmu_notifier events. + */ + write_lock_irq(&gpc->lock); + gpc->active = true; + write_unlock_irq(&gpc->lock); + } + ret = gpc_refresh_locked(gpc, gpa, uhva); + } + + if (ret != -EAGAIN || !wait || fatal_signal_pending(current)) + return ret; + + cond_resched(); } - return __kvm_gpc_refresh(gpc, gpa, uhva); } -int kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long len) +int kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long len, + bool wait) { /* * Explicitly disallow INVALID_GPA so that the magic value can be used @@ -448,15 +496,17 @@ int kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long len) if (WARN_ON_ONCE(kvm_is_error_gpa(gpa))) return -EINVAL; - return __kvm_gpc_activate(gpc, gpa, KVM_HVA_ERR_BAD, len); + return __kvm_gpc_activate(gpc, gpa, KVM_HVA_ERR_BAD, len, wait); } -int kvm_gpc_activate_hva(struct gfn_to_pfn_cache *gpc, unsigned long uhva, unsigned long len) +int kvm_gpc_activate_hva(struct gfn_to_pfn_cache *gpc, unsigned long uhva, + unsigned long len) { if (!access_ok((void __user *)uhva, len)) return -EINVAL; - return __kvm_gpc_activate(gpc, INVALID_GPA, uhva, len); + /* An HVA-based cache never consults the memslots, so never waits. */ + return __kvm_gpc_activate(gpc, INVALID_GPA, uhva, len, false); } void kvm_gpc_deactivate(struct gfn_to_pfn_cache *gpc) -- 2.43.0

