Skip to content

Commit 7ab6fb5

Browse files
bibo-maochenhuacai
authored andcommitted
LoongArch: KVM: Optimization for memslot hugepage checking
During shadow mmu page fault, there is checking for huge page for specified memslot. Page fault is hot path, check logic can be done when memslot is created. Here two flags are added for huge page checking, KVM_MEM_HUGEPAGE_CAPABLE and KVM_MEM_HUGEPAGE_INCAPABLE. Indeed for an optimized qemu, memslot for DRAM is always huge page aligned. The flag is firstly checked during hot page fault path. Now only huge page flag is supported, there is a long way for super page support in LoongArch system. Since super page size is 64G for 16K pagesize and 1G for 4K pagesize, 64G physical address is rarely used and LoongArch kernel needs support super page for 4K. Also memory layout of LoongArch qemu VM should be 1G aligned. Signed-off-by: Bibo Mao <maobibo@loongson.cn> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
1 parent ceb6a6f commit 7ab6fb5

2 files changed

Lines changed: 86 additions & 41 deletions

File tree

arch/loongarch/include/asm/kvm_host.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ struct kvm_vcpu_stat {
4545
u64 signal_exits;
4646
};
4747

48+
#define KVM_MEM_HUGEPAGE_CAPABLE (1UL << 0)
49+
#define KVM_MEM_HUGEPAGE_INCAPABLE (1UL << 1)
4850
struct kvm_arch_memory_slot {
51+
unsigned long flags;
4952
};
5053

5154
struct kvm_context {

arch/loongarch/kvm/mmu.c

Lines changed: 83 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@
1313
#include <asm/tlb.h>
1414
#include <asm/kvm_mmu.h>
1515

16+
static inline bool kvm_hugepage_capable(struct kvm_memory_slot *slot)
17+
{
18+
return slot->arch.flags & KVM_MEM_HUGEPAGE_CAPABLE;
19+
}
20+
21+
static inline bool kvm_hugepage_incapable(struct kvm_memory_slot *slot)
22+
{
23+
return slot->arch.flags & KVM_MEM_HUGEPAGE_INCAPABLE;
24+
}
25+
1626
static inline void kvm_ptw_prepare(struct kvm *kvm, kvm_ptw_ctx *ctx)
1727
{
1828
ctx->level = kvm->arch.root_level;
@@ -365,6 +375,69 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
365375
kvm_ptw_top(kvm->arch.pgd, start << PAGE_SHIFT, end << PAGE_SHIFT, &ctx);
366376
}
367377

378+
int kvm_arch_prepare_memory_region(struct kvm *kvm, const struct kvm_memory_slot *old,
379+
struct kvm_memory_slot *new, enum kvm_mr_change change)
380+
{
381+
gpa_t gpa_start;
382+
hva_t hva_start;
383+
size_t size, gpa_offset, hva_offset;
384+
385+
if ((change != KVM_MR_MOVE) && (change != KVM_MR_CREATE))
386+
return 0;
387+
/*
388+
* Prevent userspace from creating a memory region outside of the
389+
* VM GPA address space
390+
*/
391+
if ((new->base_gfn + new->npages) > (kvm->arch.gpa_size >> PAGE_SHIFT))
392+
return -ENOMEM;
393+
394+
new->arch.flags = 0;
395+
size = new->npages * PAGE_SIZE;
396+
gpa_start = new->base_gfn << PAGE_SHIFT;
397+
hva_start = new->userspace_addr;
398+
if (IS_ALIGNED(size, PMD_SIZE) && IS_ALIGNED(gpa_start, PMD_SIZE)
399+
&& IS_ALIGNED(hva_start, PMD_SIZE))
400+
new->arch.flags |= KVM_MEM_HUGEPAGE_CAPABLE;
401+
else {
402+
/*
403+
* Pages belonging to memslots that don't have the same
404+
* alignment within a PMD for userspace and GPA cannot be
405+
* mapped with PMD entries, because we'll end up mapping
406+
* the wrong pages.
407+
*
408+
* Consider a layout like the following:
409+
*
410+
* memslot->userspace_addr:
411+
* +-----+--------------------+--------------------+---+
412+
* |abcde|fgh Stage-1 block | Stage-1 block tv|xyz|
413+
* +-----+--------------------+--------------------+---+
414+
*
415+
* memslot->base_gfn << PAGE_SIZE:
416+
* +---+--------------------+--------------------+-----+
417+
* |abc|def Stage-2 block | Stage-2 block |tvxyz|
418+
* +---+--------------------+--------------------+-----+
419+
*
420+
* If we create those stage-2 blocks, we'll end up with this
421+
* incorrect mapping:
422+
* d -> f
423+
* e -> g
424+
* f -> h
425+
*/
426+
gpa_offset = gpa_start & (PMD_SIZE - 1);
427+
hva_offset = hva_start & (PMD_SIZE - 1);
428+
if (gpa_offset != hva_offset) {
429+
new->arch.flags |= KVM_MEM_HUGEPAGE_INCAPABLE;
430+
} else {
431+
if (gpa_offset == 0)
432+
gpa_offset = PMD_SIZE;
433+
if ((size + gpa_offset) < (PMD_SIZE * 2))
434+
new->arch.flags |= KVM_MEM_HUGEPAGE_INCAPABLE;
435+
}
436+
}
437+
438+
return 0;
439+
}
440+
368441
void kvm_arch_commit_memory_region(struct kvm *kvm,
369442
struct kvm_memory_slot *old,
370443
const struct kvm_memory_slot *new,
@@ -562,47 +635,23 @@ static int kvm_map_page_fast(struct kvm_vcpu *vcpu, unsigned long gpa, bool writ
562635
}
563636

564637
static bool fault_supports_huge_mapping(struct kvm_memory_slot *memslot,
565-
unsigned long hva, unsigned long map_size, bool write)
638+
unsigned long hva, bool write)
566639
{
567-
size_t size;
568-
gpa_t gpa_start;
569-
hva_t uaddr_start, uaddr_end;
640+
hva_t start, end;
570641

571642
/* Disable dirty logging on HugePages */
572643
if (kvm_slot_dirty_track_enabled(memslot) && write)
573644
return false;
574645

575-
size = memslot->npages * PAGE_SIZE;
576-
gpa_start = memslot->base_gfn << PAGE_SHIFT;
577-
uaddr_start = memslot->userspace_addr;
578-
uaddr_end = uaddr_start + size;
646+
if (kvm_hugepage_capable(memslot))
647+
return true;
579648

580-
/*
581-
* Pages belonging to memslots that don't have the same alignment
582-
* within a PMD for userspace and GPA cannot be mapped with stage-2
583-
* PMD entries, because we'll end up mapping the wrong pages.
584-
*
585-
* Consider a layout like the following:
586-
*
587-
* memslot->userspace_addr:
588-
* +-----+--------------------+--------------------+---+
589-
* |abcde|fgh Stage-1 block | Stage-1 block tv|xyz|
590-
* +-----+--------------------+--------------------+---+
591-
*
592-
* memslot->base_gfn << PAGE_SIZE:
593-
* +---+--------------------+--------------------+-----+
594-
* |abc|def Stage-2 block | Stage-2 block |tvxyz|
595-
* +---+--------------------+--------------------+-----+
596-
*
597-
* If we create those stage-2 blocks, we'll end up with this incorrect
598-
* mapping:
599-
* d -> f
600-
* e -> g
601-
* f -> h
602-
*/
603-
if ((gpa_start & (map_size - 1)) != (uaddr_start & (map_size - 1)))
649+
if (kvm_hugepage_incapable(memslot))
604650
return false;
605651

652+
start = memslot->userspace_addr;
653+
end = start + memslot->npages * PAGE_SIZE;
654+
606655
/*
607656
* Next, let's make sure we're not trying to map anything not covered
608657
* by the memslot. This means we have to prohibit block size mappings
@@ -615,8 +664,7 @@ static bool fault_supports_huge_mapping(struct kvm_memory_slot *memslot,
615664
* userspace_addr or the base_gfn, as both are equally aligned (per
616665
* the check above) and equally sized.
617666
*/
618-
return (hva & ~(map_size - 1)) >= uaddr_start &&
619-
(hva & ~(map_size - 1)) + map_size <= uaddr_end;
667+
return (hva >= ALIGN(start, PMD_SIZE)) && (hva < ALIGN_DOWN(end, PMD_SIZE));
620668
}
621669

622670
/*
@@ -842,7 +890,7 @@ static int kvm_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, bool write)
842890

843891
/* Disable dirty logging on HugePages */
844892
level = 0;
845-
if (!fault_supports_huge_mapping(memslot, hva, PMD_SIZE, write)) {
893+
if (!fault_supports_huge_mapping(memslot, hva, write)) {
846894
level = 0;
847895
} else {
848896
level = host_pfn_mapping_level(kvm, gfn, memslot);
@@ -901,12 +949,6 @@ void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)
901949
{
902950
}
903951

904-
int kvm_arch_prepare_memory_region(struct kvm *kvm, const struct kvm_memory_slot *old,
905-
struct kvm_memory_slot *new, enum kvm_mr_change change)
906-
{
907-
return 0;
908-
}
909-
910952
void kvm_arch_flush_remote_tlbs_memslot(struct kvm *kvm,
911953
const struct kvm_memory_slot *memslot)
912954
{

0 commit comments

Comments
 (0)