Skip to content

Commit e907ae5

Browse files
author
Claudio Imbrenda
committed
KVM: s390: Add helper functions for fault handling
Add some helper functions for handling multiple guest faults at the same time. This will be needed for VSIE, where a nested guest access also needs to access all the page tables that map it. Acked-by: Heiko Carstens <hca@linux.ibm.com> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
1 parent a2c17f9 commit e907ae5

6 files changed

Lines changed: 245 additions & 2 deletions

File tree

arch/s390/include/asm/kvm_host.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@ struct kvm_vcpu_arch {
442442
bool acrs_loaded;
443443
struct kvm_s390_pv_vcpu pv;
444444
union diag318_info diag318_info;
445+
void *mc; /* Placeholder */
445446
};
446447

447448
struct kvm_vm_stat {

arch/s390/kvm/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ ccflags-y := -Ivirt/kvm -Iarch/s390/kvm
99

1010
kvm-y += kvm-s390.o intercept.o interrupt.o priv.o sigp.o
1111
kvm-y += diag.o gaccess.o guestdbg.o vsie.o pv.o gmap-vsie.o
12-
kvm-y += dat.o gmap.o
12+
kvm-y += dat.o gmap.o faultin.o
1313

1414
kvm-$(CONFIG_VFIO_PCI_ZDEV_KVM) += pci.o
1515
obj-$(CONFIG_KVM) += kvm.o

arch/s390/kvm/faultin.c

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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+
}

arch/s390/kvm/faultin.h

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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+
9+
#ifndef __KVM_S390_FAULTIN_H
10+
#define __KVM_S390_FAULTIN_H
11+
12+
#include <linux/kvm_host.h>
13+
14+
#include "dat.h"
15+
16+
int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fault *f);
17+
int kvm_s390_get_guest_page(struct kvm *kvm, struct guest_fault *f, gfn_t gfn, bool w);
18+
19+
static inline int kvm_s390_faultin_gfn_simple(struct kvm_vcpu *vcpu, struct kvm *kvm,
20+
gfn_t gfn, bool wr)
21+
{
22+
struct guest_fault f = { .gfn = gfn, .write_attempt = wr, };
23+
24+
return kvm_s390_faultin_gfn(vcpu, kvm, &f);
25+
}
26+
27+
static inline int kvm_s390_get_guest_page_and_read_gpa(struct kvm *kvm, struct guest_fault *f,
28+
gpa_t gaddr, unsigned long *val)
29+
{
30+
int rc;
31+
32+
rc = kvm_s390_get_guest_page(kvm, f, gpa_to_gfn(gaddr), false);
33+
if (rc)
34+
return rc;
35+
36+
*val = *(unsigned long *)phys_to_virt(pfn_to_phys(f->pfn) | offset_in_page(gaddr));
37+
38+
return 0;
39+
}
40+
41+
static inline void kvm_s390_release_multiple(struct kvm *kvm, struct guest_fault *guest_faults,
42+
int n, bool ignore)
43+
{
44+
int i;
45+
46+
for (i = 0; i < n; i++) {
47+
kvm_release_faultin_page(kvm, guest_faults[i].page, ignore,
48+
guest_faults[i].write_attempt);
49+
guest_faults[i].page = NULL;
50+
}
51+
}
52+
53+
static inline bool kvm_s390_multiple_faults_need_retry(struct kvm *kvm, unsigned long seq,
54+
struct guest_fault *guest_faults, int n,
55+
bool unsafe)
56+
{
57+
int i;
58+
59+
for (i = 0; i < n; i++) {
60+
if (!guest_faults[i].valid)
61+
continue;
62+
if (unsafe && mmu_invalidate_retry_gfn_unsafe(kvm, seq, guest_faults[i].gfn))
63+
return true;
64+
if (!unsafe && mmu_invalidate_retry_gfn(kvm, seq, guest_faults[i].gfn))
65+
return true;
66+
}
67+
return false;
68+
}
69+
70+
static inline int kvm_s390_get_guest_pages(struct kvm *kvm, struct guest_fault *guest_faults,
71+
gfn_t start, int n_pages, bool write_attempt)
72+
{
73+
int i, rc;
74+
75+
for (i = 0; i < n_pages; i++) {
76+
rc = kvm_s390_get_guest_page(kvm, guest_faults + i, start + i, write_attempt);
77+
if (rc)
78+
break;
79+
}
80+
return rc;
81+
}
82+
83+
#define kvm_s390_release_faultin_array(kvm, array, ignore) \
84+
kvm_s390_release_multiple(kvm, array, ARRAY_SIZE(array), ignore)
85+
86+
#define kvm_s390_array_needs_retry_unsafe(kvm, seq, array) \
87+
kvm_s390_multiple_faults_need_retry(kvm, seq, array, ARRAY_SIZE(array), true)
88+
89+
#define kvm_s390_array_needs_retry_safe(kvm, seq, array) \
90+
kvm_s390_multiple_faults_need_retry(kvm, seq, array, ARRAY_SIZE(array), false)
91+
92+
#endif /* __KVM_S390_FAULTIN_H */

arch/s390/kvm/kvm-s390.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4637,7 +4637,7 @@ bool kvm_arch_can_dequeue_async_page_present(struct kvm_vcpu *vcpu)
46374637
return true;
46384638
}
46394639

4640-
static bool kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu)
4640+
bool kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu)
46414641
{
46424642
hva_t hva;
46434643
struct kvm_arch_async_pf arch;

arch/s390/kvm/kvm-s390.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,8 @@ static inline int kvm_s390_handle_dat_fault(struct kvm_vcpu *vcpu, gpa_t gaddr,
470470
return __kvm_s390_handle_dat_fault(vcpu, gpa_to_gfn(gaddr), gaddr, flags);
471471
}
472472

473+
bool kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu);
474+
473475
/* implemented in diag.c */
474476
int kvm_s390_handle_diag(struct kvm_vcpu *vcpu);
475477

0 commit comments

Comments
 (0)