1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * KVM guest fault handling. 4 * 5 * Copyright IBM Corp. 2025 6 * Author(s): Claudio Imbrenda <imbrenda@linux.ibm.com> 7 */ 8 #include <linux/kvm_types.h> 9 #include <linux/kvm_host.h> 10 11 #include "gmap.h" 12 #include "trace.h" 13 #include "faultin.h" 14 15 bool kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu); 16 17 /* 18 * kvm_s390_faultin_gfn() - handle a dat fault. 19 * @vcpu: The vCPU whose gmap is to be fixed up, or NULL if operating on the VM. 20 * @kvm: The VM whose gmap is to be fixed up, or NULL if operating on a vCPU. 21 * @f: The guest fault that needs to be resolved. 22 * 23 * Return: 24 * * 0 on success 25 * * < 0 in case of error 26 * * > 0 in case of guest exceptions 27 * 28 * Context: 29 * * The mm lock must not be held before calling 30 * * kvm->srcu must be held 31 * * may sleep 32 */ 33 int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fault *f) 34 { 35 struct kvm_s390_mmu_cache *local_mc __free(kvm_s390_mmu_cache) = NULL; 36 struct kvm_s390_mmu_cache *mc = NULL; 37 struct kvm_memory_slot *slot; 38 unsigned long inv_seq; 39 int foll, rc = 0; 40 41 foll = f->write_attempt ? FOLL_WRITE : 0; 42 foll |= f->attempt_pfault ? FOLL_NOWAIT : 0; 43 44 if (vcpu) { 45 kvm = vcpu->kvm; 46 mc = vcpu->arch.mc; 47 } 48 49 lockdep_assert_held(&kvm->srcu); 50 51 scoped_guard(read_lock, &kvm->mmu_lock) { 52 if (gmap_try_fixup_minor(kvm->arch.gmap, f) == 0) 53 return 0; 54 } 55 56 while (1) { 57 f->valid = false; 58 inv_seq = kvm->mmu_invalidate_seq; 59 /* Pairs with the smp_wmb() in kvm_mmu_invalidate_end(). */ 60 smp_rmb(); 61 62 if (vcpu) 63 slot = kvm_vcpu_gfn_to_memslot(vcpu, f->gfn); 64 else 65 slot = gfn_to_memslot(kvm, f->gfn); 66 f->pfn = __kvm_faultin_pfn(slot, f->gfn, foll, &f->writable, &f->page); 67 68 /* Needs I/O, try to setup async pfault (only possible with FOLL_NOWAIT). */ 69 if (f->pfn == KVM_PFN_ERR_NEEDS_IO) { 70 if (unlikely(!f->attempt_pfault)) 71 return -EAGAIN; 72 if (unlikely(!vcpu)) 73 return -EINVAL; 74 trace_kvm_s390_major_guest_pfault(vcpu); 75 if (kvm_arch_setup_async_pf(vcpu)) 76 return 0; 77 vcpu->stat.pfault_sync++; 78 /* Could not setup async pfault, try again synchronously. */ 79 foll &= ~FOLL_NOWAIT; 80 f->pfn = __kvm_faultin_pfn(slot, f->gfn, foll, &f->writable, &f->page); 81 } 82 83 /* Access outside memory, addressing exception. */ 84 if (is_noslot_pfn(f->pfn)) 85 return PGM_ADDRESSING; 86 /* Signal pending: try again. */ 87 if (f->pfn == KVM_PFN_ERR_SIGPENDING) 88 return -EAGAIN; 89 /* Check if it's read-only memory; don't try to actually handle that case. */ 90 if (f->pfn == KVM_PFN_ERR_RO_FAULT) 91 return -EOPNOTSUPP; 92 /* Any other error. */ 93 if (is_error_pfn(f->pfn)) 94 return -EFAULT; 95 96 if (!mc) { 97 local_mc = kvm_s390_new_mmu_cache(); 98 if (!local_mc) 99 return -ENOMEM; 100 mc = local_mc; 101 } 102 103 /* Loop, will automatically release the faulted page. */ 104 if (mmu_invalidate_retry_gfn_unsafe(kvm, inv_seq, f->gfn)) { 105 kvm_release_faultin_page(kvm, f->page, true, false); 106 continue; 107 } 108 109 scoped_guard(read_lock, &kvm->mmu_lock) { 110 if (!mmu_invalidate_retry_gfn(kvm, inv_seq, f->gfn)) { 111 f->valid = true; 112 rc = gmap_link(mc, kvm->arch.gmap, f); 113 kvm_release_faultin_page(kvm, f->page, !!rc, f->write_attempt); 114 f->page = NULL; 115 } 116 } 117 kvm_release_faultin_page(kvm, f->page, true, false); 118 119 if (rc == -ENOMEM) { 120 rc = kvm_s390_mmu_cache_topup(mc); 121 if (rc) 122 return rc; 123 } else if (rc != -EAGAIN) { 124 return rc; 125 } 126 } 127 } 128 129 int kvm_s390_get_guest_page(struct kvm *kvm, struct guest_fault *f, gfn_t gfn, bool w) 130 { 131 struct kvm_memory_slot *slot = gfn_to_memslot(kvm, gfn); 132 int foll = w ? FOLL_WRITE : 0; 133 134 f->write_attempt = w; 135 f->gfn = gfn; 136 f->pfn = __kvm_faultin_pfn(slot, gfn, foll, &f->writable, &f->page); 137 if (is_noslot_pfn(f->pfn)) 138 return PGM_ADDRESSING; 139 if (is_sigpending_pfn(f->pfn)) 140 return -EINTR; 141 if (f->pfn == KVM_PFN_ERR_NEEDS_IO) 142 return -EAGAIN; 143 if (is_error_pfn(f->pfn)) 144 return -EFAULT; 145 146 f->valid = true; 147 return 0; 148 } 149