Introduce gmap_enable_cmm_already_locked() and its locking wrapper gmap_enable_cmm() to attempt to allocate all page tables of a running guest. This means that all hugepages will be mapped with 4k pages regardless of the hpage parameter. Existing mappings with large pages will be split. If any memory allocation fails, ignore the error and give up. This is only a best effort. Signed-off-by: Claudio Imbrenda Cc: stable@vger.kernel.org # 7.0+: prerequisite for the next patch --- arch/s390/kvm/gmap/gmap.c | 37 +++++++++++++++++++++++++++++++++++++ arch/s390/kvm/gmap/gmap.h | 11 +++++++++++ 2 files changed, 48 insertions(+) diff --git a/arch/s390/kvm/gmap/gmap.c b/arch/s390/kvm/gmap/gmap.c index b9d92bef5459..e0c584ec6906 100644 --- a/arch/s390/kvm/gmap/gmap.c +++ b/arch/s390/kvm/gmap/gmap.c @@ -1524,3 +1524,40 @@ struct gmap *gmap_create_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *pare } return new; } + +/* Bail out for any allocation failure. This is only a best effort to allocate all page tables. */ +void gmap_enable_cmm_already_locked(struct kvm_s390_mmu_cache *mc, struct gmap *gmap) +{ + struct kvm_memory_slot *ms; + struct kvm_memslots *slots; + gfn_t gfn, start, end; + union crste *crstep; + union pte *ptep; + int flags, bkt; + + if (uses_cmm(gmap)) + return; + + set_bit(GMAP_FLAG_USES_CMM, &gmap->flags); + + slots = kvm_memslots(gmap->kvm); + if (!slots || kvm_memslots_empty(slots)) + return; + + flags = DAT_WALK_ALLOC | DAT_WALK_SPLIT | (uses_skeys(gmap) ? DAT_WALK_USES_SKEYS : 0); + + kvm_for_each_memslot(ms, bkt, slots) { + start = ALIGN(ms->base_gfn, _PAGE_ENTRIES); + end = ALIGN_DOWN(ms->base_gfn + ms->npages, _PAGE_ENTRIES); + + for (gfn = start; gfn < end; gfn += _PAGE_ENTRIES) { + if (kvm_s390_mmu_cache_topup(mc)) + return; + + guard(write_lock)(&gmap->kvm->mmu_lock); + + dat_entry_walk(mc, gfn, gmap->asce, flags, TABLE_TYPE_PAGE_TABLE, + &crstep, &ptep); + } + } +} diff --git a/arch/s390/kvm/gmap/gmap.h b/arch/s390/kvm/gmap/gmap.h index 8f47f29c89fd..226fb6595fe4 100644 --- a/arch/s390/kvm/gmap/gmap.h +++ b/arch/s390/kvm/gmap/gmap.h @@ -113,6 +113,7 @@ void _gmap_handle_vsie_unshadow_event(struct gmap *parent, gfn_t gfn); struct gmap *gmap_create_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *gmap, union asce asce, int edat_level); void gmap_split_huge_pages(struct gmap *gmap); +void gmap_enable_cmm_already_locked(struct kvm_s390_mmu_cache *mc, struct gmap *gmap); static inline bool uses_skeys(struct gmap *gmap) { @@ -345,4 +346,14 @@ static inline bool gmap_is_shadow_valid(struct gmap *sg, union asce asce, int ed return sg->guest_asce.val == asce.val && sg->edat_level == edat_level; } +static inline void gmap_enable_cmm(struct kvm_s390_mmu_cache *mc, struct gmap *gmap) +{ + /* CMMA already enabled, nothing to do */ + if (uses_cmm(gmap)) + return; + + guard(mutex)(&gmap->kvm->slots_arch_lock); + + gmap_enable_cmm_already_locked(mc, gmap); +} #endif /* ARCH_KVM_GMAP_GMAP_H */ -- 2.56.0