Skip to content

Commit c9f721e

Browse files
author
Claudio Imbrenda
committed
KVM: s390: move some gmap shadowing functions away from mm/gmap.c
Move some gmap shadowing functions from mm/gmap.c to kvm/kvm-s390.c and the newly created kvm/gmap-vsie.c This is a step toward removing gmap from mm. Reviewed-by: Janosch Frank <frankja@linux.ibm.com> Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com> Link: https://lore.kernel.org/r/20250123144627.312456-10-imbrenda@linux.ibm.com Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com> Message-ID: <20250123144627.312456-10-imbrenda@linux.ibm.com>
1 parent d41993f commit c9f721e

File tree

8 files changed

+271
-218
lines changed

8 files changed

+271
-218
lines changed

arch/s390/include/asm/gmap.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ struct gmap *gmap_create(struct mm_struct *mm, unsigned long limit);
106106
void gmap_remove(struct gmap *gmap);
107107
struct gmap *gmap_get(struct gmap *gmap);
108108
void gmap_put(struct gmap *gmap);
109+
void gmap_free(struct gmap *gmap);
110+
struct gmap *gmap_alloc(unsigned long limit);
109111

110112
int gmap_map_segment(struct gmap *gmap, unsigned long from,
111113
unsigned long to, unsigned long len);
@@ -118,9 +120,7 @@ void gmap_unlink(struct mm_struct *, unsigned long *table, unsigned long vmaddr)
118120

119121
int gmap_read_table(struct gmap *gmap, unsigned long gaddr, unsigned long *val);
120122

121-
struct gmap *gmap_shadow(struct gmap *parent, unsigned long asce,
122-
int edat_level);
123-
int gmap_shadow_valid(struct gmap *sg, unsigned long asce, int edat_level);
123+
void gmap_unshadow(struct gmap *sg);
124124
int gmap_shadow_r2t(struct gmap *sg, unsigned long saddr, unsigned long r2t,
125125
int fake);
126126
int gmap_shadow_r3t(struct gmap *sg, unsigned long saddr, unsigned long r3t,
@@ -136,8 +136,7 @@ int gmap_shadow_page(struct gmap *sg, unsigned long saddr, pte_t pte);
136136
void gmap_register_pte_notifier(struct gmap_notifier *);
137137
void gmap_unregister_pte_notifier(struct gmap_notifier *);
138138

139-
int gmap_mprotect_notify(struct gmap *, unsigned long start,
140-
unsigned long len, int prot);
139+
int gmap_protect_one(struct gmap *gmap, unsigned long gaddr, int prot, unsigned long bits);
141140

142141
void gmap_sync_dirty_log_pmd(struct gmap *gmap, unsigned long dirty_bitmap[4],
143142
unsigned long gaddr, unsigned long vmaddr);

arch/s390/kvm/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ include $(srctree)/virt/kvm/Makefile.kvm
88
ccflags-y := -Ivirt/kvm -Iarch/s390/kvm
99

1010
kvm-y += kvm-s390.o intercept.o interrupt.o priv.o sigp.o
11-
kvm-y += diag.o gaccess.o guestdbg.o vsie.o pv.o gmap.o
11+
kvm-y += diag.o gaccess.o guestdbg.o vsie.o pv.o gmap.o gmap-vsie.o
1212

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

arch/s390/kvm/gmap-vsie.c

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Guest memory management for KVM/s390 nested VMs.
4+
*
5+
* Copyright IBM Corp. 2008, 2020, 2024
6+
*
7+
* Author(s): Claudio Imbrenda <imbrenda@linux.ibm.com>
8+
* Martin Schwidefsky <schwidefsky@de.ibm.com>
9+
* David Hildenbrand <david@redhat.com>
10+
* Janosch Frank <frankja@linux.vnet.ibm.com>
11+
*/
12+
13+
#include <linux/compiler.h>
14+
#include <linux/kvm.h>
15+
#include <linux/kvm_host.h>
16+
#include <linux/pgtable.h>
17+
#include <linux/pagemap.h>
18+
#include <linux/mman.h>
19+
20+
#include <asm/lowcore.h>
21+
#include <asm/gmap.h>
22+
#include <asm/uv.h>
23+
24+
#include "kvm-s390.h"
25+
#include "gmap.h"
26+
27+
/**
28+
* gmap_find_shadow - find a specific asce in the list of shadow tables
29+
* @parent: pointer to the parent gmap
30+
* @asce: ASCE for which the shadow table is created
31+
* @edat_level: edat level to be used for the shadow translation
32+
*
33+
* Returns the pointer to a gmap if a shadow table with the given asce is
34+
* already available, ERR_PTR(-EAGAIN) if another one is just being created,
35+
* otherwise NULL
36+
*
37+
* Context: Called with parent->shadow_lock held
38+
*/
39+
static struct gmap *gmap_find_shadow(struct gmap *parent, unsigned long asce, int edat_level)
40+
{
41+
struct gmap *sg;
42+
43+
lockdep_assert_held(&parent->shadow_lock);
44+
list_for_each_entry(sg, &parent->children, list) {
45+
if (!gmap_shadow_valid(sg, asce, edat_level))
46+
continue;
47+
if (!sg->initialized)
48+
return ERR_PTR(-EAGAIN);
49+
refcount_inc(&sg->ref_count);
50+
return sg;
51+
}
52+
return NULL;
53+
}
54+
55+
/**
56+
* gmap_shadow - create/find a shadow guest address space
57+
* @parent: pointer to the parent gmap
58+
* @asce: ASCE for which the shadow table is created
59+
* @edat_level: edat level to be used for the shadow translation
60+
*
61+
* The pages of the top level page table referred by the asce parameter
62+
* will be set to read-only and marked in the PGSTEs of the kvm process.
63+
* The shadow table will be removed automatically on any change to the
64+
* PTE mapping for the source table.
65+
*
66+
* Returns a guest address space structure, ERR_PTR(-ENOMEM) if out of memory,
67+
* ERR_PTR(-EAGAIN) if the caller has to retry and ERR_PTR(-EFAULT) if the
68+
* parent gmap table could not be protected.
69+
*/
70+
struct gmap *gmap_shadow(struct gmap *parent, unsigned long asce, int edat_level)
71+
{
72+
struct gmap *sg, *new;
73+
unsigned long limit;
74+
int rc;
75+
76+
if (KVM_BUG_ON(parent->mm->context.allow_gmap_hpage_1m, (struct kvm *)parent->private) ||
77+
KVM_BUG_ON(gmap_is_shadow(parent), (struct kvm *)parent->private))
78+
return ERR_PTR(-EFAULT);
79+
spin_lock(&parent->shadow_lock);
80+
sg = gmap_find_shadow(parent, asce, edat_level);
81+
spin_unlock(&parent->shadow_lock);
82+
if (sg)
83+
return sg;
84+
/* Create a new shadow gmap */
85+
limit = -1UL >> (33 - (((asce & _ASCE_TYPE_MASK) >> 2) * 11));
86+
if (asce & _ASCE_REAL_SPACE)
87+
limit = -1UL;
88+
new = gmap_alloc(limit);
89+
if (!new)
90+
return ERR_PTR(-ENOMEM);
91+
new->mm = parent->mm;
92+
new->parent = gmap_get(parent);
93+
new->private = parent->private;
94+
new->orig_asce = asce;
95+
new->edat_level = edat_level;
96+
new->initialized = false;
97+
spin_lock(&parent->shadow_lock);
98+
/* Recheck if another CPU created the same shadow */
99+
sg = gmap_find_shadow(parent, asce, edat_level);
100+
if (sg) {
101+
spin_unlock(&parent->shadow_lock);
102+
gmap_free(new);
103+
return sg;
104+
}
105+
if (asce & _ASCE_REAL_SPACE) {
106+
/* only allow one real-space gmap shadow */
107+
list_for_each_entry(sg, &parent->children, list) {
108+
if (sg->orig_asce & _ASCE_REAL_SPACE) {
109+
spin_lock(&sg->guest_table_lock);
110+
gmap_unshadow(sg);
111+
spin_unlock(&sg->guest_table_lock);
112+
list_del(&sg->list);
113+
gmap_put(sg);
114+
break;
115+
}
116+
}
117+
}
118+
refcount_set(&new->ref_count, 2);
119+
list_add(&new->list, &parent->children);
120+
if (asce & _ASCE_REAL_SPACE) {
121+
/* nothing to protect, return right away */
122+
new->initialized = true;
123+
spin_unlock(&parent->shadow_lock);
124+
return new;
125+
}
126+
spin_unlock(&parent->shadow_lock);
127+
/* protect after insertion, so it will get properly invalidated */
128+
mmap_read_lock(parent->mm);
129+
rc = __kvm_s390_mprotect_many(parent, asce & _ASCE_ORIGIN,
130+
((asce & _ASCE_TABLE_LENGTH) + 1),
131+
PROT_READ, GMAP_NOTIFY_SHADOW);
132+
mmap_read_unlock(parent->mm);
133+
spin_lock(&parent->shadow_lock);
134+
new->initialized = true;
135+
if (rc) {
136+
list_del(&new->list);
137+
gmap_free(new);
138+
new = ERR_PTR(rc);
139+
}
140+
spin_unlock(&parent->shadow_lock);
141+
return new;
142+
}

arch/s390/kvm/gmap.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,25 @@
1313
int gmap_make_secure(struct gmap *gmap, unsigned long gaddr, void *uvcb);
1414
int gmap_convert_to_secure(struct gmap *gmap, unsigned long gaddr);
1515
int gmap_destroy_page(struct gmap *gmap, unsigned long gaddr);
16+
struct gmap *gmap_shadow(struct gmap *parent, unsigned long asce, int edat_level);
17+
18+
/**
19+
* gmap_shadow_valid - check if a shadow guest address space matches the
20+
* given properties and is still valid
21+
* @sg: pointer to the shadow guest address space structure
22+
* @asce: ASCE for which the shadow table is requested
23+
* @edat_level: edat level to be used for the shadow translation
24+
*
25+
* Returns 1 if the gmap shadow is still valid and matches the given
26+
* properties, the caller can continue using it. Returns 0 otherwise, the
27+
* caller has to request a new shadow gmap in this case.
28+
*
29+
*/
30+
static inline int gmap_shadow_valid(struct gmap *sg, unsigned long asce, int edat_level)
31+
{
32+
if (sg->removed)
33+
return 0;
34+
return sg->orig_asce == asce && sg->edat_level == edat_level;
35+
}
1636

1737
#endif

arch/s390/kvm/kvm-s390.c

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4511,6 +4511,75 @@ static bool ibs_enabled(struct kvm_vcpu *vcpu)
45114511
return kvm_s390_test_cpuflags(vcpu, CPUSTAT_IBS);
45124512
}
45134513

4514+
static int __kvm_s390_fixup_fault_sync(struct gmap *gmap, gpa_t gaddr, unsigned int flags)
4515+
{
4516+
struct kvm *kvm = gmap->private;
4517+
gfn_t gfn = gpa_to_gfn(gaddr);
4518+
bool unlocked;
4519+
hva_t vmaddr;
4520+
gpa_t tmp;
4521+
int rc;
4522+
4523+
if (kvm_is_ucontrol(kvm)) {
4524+
tmp = __gmap_translate(gmap, gaddr);
4525+
gfn = gpa_to_gfn(tmp);
4526+
}
4527+
4528+
vmaddr = gfn_to_hva(kvm, gfn);
4529+
rc = fixup_user_fault(gmap->mm, vmaddr, FAULT_FLAG_WRITE, &unlocked);
4530+
if (!rc)
4531+
rc = __gmap_link(gmap, gaddr, vmaddr);
4532+
return rc;
4533+
}
4534+
4535+
/**
4536+
* __kvm_s390_mprotect_many() - Apply specified protection to guest pages
4537+
* @gmap: the gmap of the guest
4538+
* @gpa: the starting guest address
4539+
* @npages: how many pages to protect
4540+
* @prot: indicates access rights: PROT_NONE, PROT_READ or PROT_WRITE
4541+
* @bits: pgste notification bits to set
4542+
*
4543+
* Returns: 0 in case of success, < 0 in case of error - see gmap_protect_one()
4544+
*
4545+
* Context: kvm->srcu and gmap->mm need to be held in read mode
4546+
*/
4547+
int __kvm_s390_mprotect_many(struct gmap *gmap, gpa_t gpa, u8 npages, unsigned int prot,
4548+
unsigned long bits)
4549+
{
4550+
unsigned int fault_flag = (prot & PROT_WRITE) ? FAULT_FLAG_WRITE : 0;
4551+
gpa_t end = gpa + npages * PAGE_SIZE;
4552+
int rc;
4553+
4554+
for (; gpa < end; gpa = ALIGN(gpa + 1, rc)) {
4555+
rc = gmap_protect_one(gmap, gpa, prot, bits);
4556+
if (rc == -EAGAIN) {
4557+
__kvm_s390_fixup_fault_sync(gmap, gpa, fault_flag);
4558+
rc = gmap_protect_one(gmap, gpa, prot, bits);
4559+
}
4560+
if (rc < 0)
4561+
return rc;
4562+
}
4563+
4564+
return 0;
4565+
}
4566+
4567+
static int kvm_s390_mprotect_notify_prefix(struct kvm_vcpu *vcpu)
4568+
{
4569+
gpa_t gaddr = kvm_s390_get_prefix(vcpu);
4570+
int idx, rc;
4571+
4572+
idx = srcu_read_lock(&vcpu->kvm->srcu);
4573+
mmap_read_lock(vcpu->arch.gmap->mm);
4574+
4575+
rc = __kvm_s390_mprotect_many(vcpu->arch.gmap, gaddr, 2, PROT_WRITE, GMAP_NOTIFY_MPROT);
4576+
4577+
mmap_read_unlock(vcpu->arch.gmap->mm);
4578+
srcu_read_unlock(&vcpu->kvm->srcu, idx);
4579+
4580+
return rc;
4581+
}
4582+
45144583
static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu)
45154584
{
45164585
retry:
@@ -4526,9 +4595,8 @@ static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu)
45264595
*/
45274596
if (kvm_check_request(KVM_REQ_REFRESH_GUEST_PREFIX, vcpu)) {
45284597
int rc;
4529-
rc = gmap_mprotect_notify(vcpu->arch.gmap,
4530-
kvm_s390_get_prefix(vcpu),
4531-
PAGE_SIZE * 2, PROT_WRITE);
4598+
4599+
rc = kvm_s390_mprotect_notify_prefix(vcpu);
45324600
if (rc) {
45334601
kvm_make_request(KVM_REQ_REFRESH_GUEST_PREFIX, vcpu);
45344602
return rc;

arch/s390/kvm/kvm-s390.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,8 @@ void kvm_s390_set_cpu_timer(struct kvm_vcpu *vcpu, __u64 cputm);
420420
__u64 kvm_s390_get_cpu_timer(struct kvm_vcpu *vcpu);
421421
int kvm_s390_cpus_from_pv(struct kvm *kvm, u16 *rc, u16 *rrc);
422422
int __kvm_s390_handle_dat_fault(struct kvm_vcpu *vcpu, gfn_t gfn, gpa_t gaddr, unsigned int flags);
423+
int __kvm_s390_mprotect_many(struct gmap *gmap, gpa_t gpa, u8 npages, unsigned int prot,
424+
unsigned long bits);
423425

424426
static inline int kvm_s390_handle_dat_fault(struct kvm_vcpu *vcpu, gpa_t gaddr, unsigned int flags)
425427
{

arch/s390/kvm/vsie.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/bitmap.h>
1414
#include <linux/sched/signal.h>
1515
#include <linux/io.h>
16+
#include <linux/mman.h>
1617

1718
#include <asm/gmap.h>
1819
#include <asm/mmu_context.h>
@@ -22,6 +23,7 @@
2223
#include <asm/facility.h>
2324
#include "kvm-s390.h"
2425
#include "gaccess.h"
26+
#include "gmap.h"
2527

2628
enum vsie_page_flags {
2729
VSIE_PAGE_IN_USE = 0,

0 commit comments

Comments
 (0)