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