xref: /linux/arch/s390/kvm/gmap/faultin.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
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