KVM: s390: KVM page table management functions: allocation

Add page table management functions to be used for KVM guest (gmap)
page tables.

This patch adds the boilerplate and functions for the allocation and
deallocation of DAT tables.

Acked-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
This commit is contained in:
Claudio Imbrenda
2026-02-04 16:02:42 +01:00
parent 5a74e3d934
commit 12f2f61a9e
4 changed files with 182 additions and 0 deletions

103
arch/s390/kvm/dat.c Normal file
View File

@@ -0,0 +1,103 @@
// SPDX-License-Identifier: GPL-2.0
/*
* KVM guest address space mapping code
*
* Copyright IBM Corp. 2007, 2020, 2024
* Author(s): Claudio Imbrenda <imbrenda@linux.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* David Hildenbrand <david@redhat.com>
* Janosch Frank <frankja@linux.ibm.com>
*/
#include <linux/kernel.h>
#include <linux/pagewalk.h>
#include <linux/swap.h>
#include <linux/smp.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/swapops.h>
#include <linux/ksm.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/pgtable.h>
#include <linux/kvm_types.h>
#include <linux/kvm_host.h>
#include <linux/pgalloc.h>
#include <asm/page-states.h>
#include <asm/tlb.h>
#include "dat.h"
int kvm_s390_mmu_cache_topup(struct kvm_s390_mmu_cache *mc)
{
void *o;
for ( ; mc->n_crsts < KVM_S390_MMU_CACHE_N_CRSTS; mc->n_crsts++) {
o = (void *)__get_free_pages(GFP_KERNEL_ACCOUNT | __GFP_COMP, CRST_ALLOC_ORDER);
if (!o)
return -ENOMEM;
mc->crsts[mc->n_crsts] = o;
}
for ( ; mc->n_pts < KVM_S390_MMU_CACHE_N_PTS; mc->n_pts++) {
o = (void *)__get_free_page(GFP_KERNEL_ACCOUNT);
if (!o)
return -ENOMEM;
mc->pts[mc->n_pts] = o;
}
for ( ; mc->n_rmaps < KVM_S390_MMU_CACHE_N_RMAPS; mc->n_rmaps++) {
o = kzalloc(sizeof(*mc->rmaps[0]), GFP_KERNEL_ACCOUNT);
if (!o)
return -ENOMEM;
mc->rmaps[mc->n_rmaps] = o;
}
return 0;
}
static inline struct page_table *dat_alloc_pt_noinit(struct kvm_s390_mmu_cache *mc)
{
struct page_table *res;
res = kvm_s390_mmu_cache_alloc_pt(mc);
if (res)
__arch_set_page_dat(res, 1);
return res;
}
static inline struct crst_table *dat_alloc_crst_noinit(struct kvm_s390_mmu_cache *mc)
{
struct crst_table *res;
res = kvm_s390_mmu_cache_alloc_crst(mc);
if (res)
__arch_set_page_dat(res, 1UL << CRST_ALLOC_ORDER);
return res;
}
struct crst_table *dat_alloc_crst_sleepable(unsigned long init)
{
struct page *page;
void *virt;
page = alloc_pages(GFP_KERNEL_ACCOUNT | __GFP_COMP, CRST_ALLOC_ORDER);
if (!page)
return NULL;
virt = page_to_virt(page);
__arch_set_page_dat(virt, 1UL << CRST_ALLOC_ORDER);
crst_table_init(virt, init);
return virt;
}
void dat_free_level(struct crst_table *table, bool owns_ptes)
{
unsigned int i;
for (i = 0; i < _CRST_ENTRIES; i++) {
if (table->crstes[i].h.fc || table->crstes[i].h.i)
continue;
if (!is_pmd(table->crstes[i]))
dat_free_level(dereference_crste(table->crstes[i]), owns_ptes);
else if (owns_ptes)
dat_free_pt(dereference_pmd(table->crstes[i].pmd));
}
dat_free_crst(table);
}