mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-07-12 20:35:23 +02:00
ANDROID: KVM: arm64: Guest page CMOs with PMD_SIZE fixmap at EL2
With the introduction of THP support in the pKVM hypervisor, guest pages COM is needed for PMD_SIZE mapping. Fixmap only supports PAGE_SIZE and iterating over the huge-page is time consuming (mostly due to TLBI on hyp_fixmap_unmap) which is a problem for EL2 latency. Introduce a shared PMD_SIZE fixmap (hyp_fixblock_map/hyp_fixblock_unmap) to improve guest page CMOs with THP support. The iterative solution resulted in a latency of ~700us on Pixel6, while the shared PMD_SIZE fixmap reduces it to ~100us. Bug: 278749606 Bug: 278011447 Change-Id: I2ee5034ce6c6e63c4a385c884fc3b6fa378e8155 Signed-off-by: Vincent Donnefort <vdonnefort@google.com> [ qperret: reworked the private range allocation to avoid transiently releasing the page-table lock ] Signed-off-by: Quentin Perret <qperret@google.com>
This commit is contained in:
parent
b6a4560a75
commit
358f4bcd33
|
@ -14,9 +14,11 @@ extern struct kvm_pgtable pkvm_pgtable;
|
||||||
extern hyp_spinlock_t pkvm_pgd_lock;
|
extern hyp_spinlock_t pkvm_pgd_lock;
|
||||||
extern const struct pkvm_module_ops module_ops;
|
extern const struct pkvm_module_ops module_ops;
|
||||||
|
|
||||||
int hyp_create_pcpu_fixmap(void);
|
int hyp_create_fixmap(void);
|
||||||
void *hyp_fixmap_map(phys_addr_t phys);
|
void *hyp_fixmap_map(phys_addr_t phys);
|
||||||
void hyp_fixmap_unmap(void);
|
void hyp_fixmap_unmap(void);
|
||||||
|
void *hyp_fixblock_map(phys_addr_t phys);
|
||||||
|
void hyp_fixblock_unmap(void);
|
||||||
void hyp_poison_page(phys_addr_t phys);
|
void hyp_poison_page(phys_addr_t phys);
|
||||||
|
|
||||||
int hyp_create_idmap(u32 hyp_va_bits);
|
int hyp_create_idmap(u32 hyp_va_bits);
|
||||||
|
|
|
@ -273,25 +273,68 @@ static void guest_s2_put_page(void *addr)
|
||||||
hyp_put_page(¤t_vm->pool, addr);
|
hyp_put_page(¤t_vm->pool, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *__fixmap_guest_page(void *va, size_t *size)
|
||||||
|
{
|
||||||
|
void *addr;
|
||||||
|
|
||||||
|
if (WARN_ON(!IS_ALIGNED(*size, *size)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (IS_ALIGNED(*size, PMD_SIZE)) {
|
||||||
|
addr = hyp_fixblock_map(__hyp_pa(va));
|
||||||
|
if (addr)
|
||||||
|
return addr;
|
||||||
|
|
||||||
|
*size = PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_ALIGNED(*size, PAGE_SIZE))
|
||||||
|
return hyp_fixmap_map(__hyp_pa(va));
|
||||||
|
|
||||||
|
WARN_ON(1);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __fixunmap_guest_page(size_t size)
|
||||||
|
{
|
||||||
|
switch (size) {
|
||||||
|
case PAGE_SIZE:
|
||||||
|
hyp_fixmap_unmap();
|
||||||
|
break;
|
||||||
|
case PMD_SIZE:
|
||||||
|
hyp_fixblock_unmap();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BUG();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void clean_dcache_guest_page(void *va, size_t size)
|
static void clean_dcache_guest_page(void *va, size_t size)
|
||||||
{
|
{
|
||||||
while (size) {
|
while (size) {
|
||||||
__clean_dcache_guest_page(hyp_fixmap_map(__hyp_pa(va)),
|
size_t __size = size == PMD_SIZE ? size : PAGE_SIZE;
|
||||||
PAGE_SIZE);
|
void *addr = __fixmap_guest_page(va, &__size);
|
||||||
hyp_fixmap_unmap();
|
|
||||||
va += PAGE_SIZE;
|
__clean_dcache_guest_page(addr, __size);
|
||||||
size -= PAGE_SIZE;
|
__fixunmap_guest_page(__size);
|
||||||
|
|
||||||
|
size -= __size;
|
||||||
|
va += __size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void invalidate_icache_guest_page(void *va, size_t size)
|
static void invalidate_icache_guest_page(void *va, size_t size)
|
||||||
{
|
{
|
||||||
while (size) {
|
while (size) {
|
||||||
__invalidate_icache_guest_page(hyp_fixmap_map(__hyp_pa(va)),
|
size_t __size = size == PMD_SIZE ? size : PAGE_SIZE;
|
||||||
PAGE_SIZE);
|
void *addr = __fixmap_guest_page(va, &__size);
|
||||||
hyp_fixmap_unmap();
|
|
||||||
va += PAGE_SIZE;
|
__invalidate_icache_guest_page(addr, __size);
|
||||||
size -= PAGE_SIZE;
|
__fixunmap_guest_page(__size);
|
||||||
|
|
||||||
|
size -= __size;
|
||||||
|
va += __size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ static u64 __io_map_base;
|
||||||
struct hyp_fixmap_slot {
|
struct hyp_fixmap_slot {
|
||||||
u64 addr;
|
u64 addr;
|
||||||
kvm_pte_t *ptep;
|
kvm_pte_t *ptep;
|
||||||
|
u8 level;
|
||||||
};
|
};
|
||||||
static DEFINE_PER_CPU(struct hyp_fixmap_slot, fixmap_slots);
|
static DEFINE_PER_CPU(struct hyp_fixmap_slot, fixmap_slots);
|
||||||
|
|
||||||
|
@ -309,9 +310,8 @@ int hyp_map_vectors(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *hyp_fixmap_map(phys_addr_t phys)
|
static void *fixmap_map_slot(struct hyp_fixmap_slot *slot, phys_addr_t phys)
|
||||||
{
|
{
|
||||||
struct hyp_fixmap_slot *slot = this_cpu_ptr(&fixmap_slots);
|
|
||||||
kvm_pte_t pte, *ptep = slot->ptep;
|
kvm_pte_t pte, *ptep = slot->ptep;
|
||||||
|
|
||||||
pte = *ptep;
|
pte = *ptep;
|
||||||
|
@ -323,6 +323,11 @@ void *hyp_fixmap_map(phys_addr_t phys)
|
||||||
return (void *)slot->addr + offset_in_page(phys);
|
return (void *)slot->addr + offset_in_page(phys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *hyp_fixmap_map(phys_addr_t phys)
|
||||||
|
{
|
||||||
|
return fixmap_map_slot(this_cpu_ptr(&fixmap_slots), phys);
|
||||||
|
}
|
||||||
|
|
||||||
static void fixmap_clear_slot(struct hyp_fixmap_slot *slot)
|
static void fixmap_clear_slot(struct hyp_fixmap_slot *slot)
|
||||||
{
|
{
|
||||||
kvm_pte_t *ptep = slot->ptep;
|
kvm_pte_t *ptep = slot->ptep;
|
||||||
|
@ -340,7 +345,7 @@ static void fixmap_clear_slot(struct hyp_fixmap_slot *slot)
|
||||||
* https://lore.kernel.org/kvm/20221017115209.2099-1-will@kernel.org/T/#mf10dfbaf1eaef9274c581b81c53758918c1d0f03
|
* https://lore.kernel.org/kvm/20221017115209.2099-1-will@kernel.org/T/#mf10dfbaf1eaef9274c581b81c53758918c1d0f03
|
||||||
*/
|
*/
|
||||||
dsb(ishst);
|
dsb(ishst);
|
||||||
__tlbi_level(vale2is, __TLBI_VADDR(addr, 0), (KVM_PGTABLE_MAX_LEVELS - 1));
|
__tlbi_level(vale2is, __TLBI_VADDR(addr, 0), slot->level);
|
||||||
dsb(ish);
|
dsb(ish);
|
||||||
isb();
|
isb();
|
||||||
}
|
}
|
||||||
|
@ -353,9 +358,9 @@ void hyp_fixmap_unmap(void)
|
||||||
static int __create_fixmap_slot_cb(const struct kvm_pgtable_visit_ctx *ctx,
|
static int __create_fixmap_slot_cb(const struct kvm_pgtable_visit_ctx *ctx,
|
||||||
enum kvm_pgtable_walk_flags visit)
|
enum kvm_pgtable_walk_flags visit)
|
||||||
{
|
{
|
||||||
struct hyp_fixmap_slot *slot = per_cpu_ptr(&fixmap_slots, (u64)ctx->arg);
|
struct hyp_fixmap_slot *slot = (struct hyp_fixmap_slot *)ctx->arg;
|
||||||
|
|
||||||
if (!kvm_pte_valid(ctx->old) || ctx->level != KVM_PGTABLE_MAX_LEVELS - 1)
|
if (!kvm_pte_valid(ctx->old) || ctx->level != slot->level)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
slot->addr = ctx->addr;
|
slot->addr = ctx->addr;
|
||||||
|
@ -376,13 +381,78 @@ static int create_fixmap_slot(u64 addr, u64 cpu)
|
||||||
struct kvm_pgtable_walker walker = {
|
struct kvm_pgtable_walker walker = {
|
||||||
.cb = __create_fixmap_slot_cb,
|
.cb = __create_fixmap_slot_cb,
|
||||||
.flags = KVM_PGTABLE_WALK_LEAF,
|
.flags = KVM_PGTABLE_WALK_LEAF,
|
||||||
.arg = (void *)cpu,
|
.arg = (void *)per_cpu_ptr(&fixmap_slots, cpu),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
per_cpu_ptr(&fixmap_slots, cpu)->level = KVM_PGTABLE_MAX_LEVELS - 1;
|
||||||
|
|
||||||
return kvm_pgtable_walk(&pkvm_pgtable, addr, PAGE_SIZE, &walker);
|
return kvm_pgtable_walk(&pkvm_pgtable, addr, PAGE_SIZE, &walker);
|
||||||
}
|
}
|
||||||
|
|
||||||
int hyp_create_pcpu_fixmap(void)
|
#ifndef CONFIG_ARM64_64K_PAGES
|
||||||
|
static struct hyp_fixmap_slot hyp_fixblock_slot;
|
||||||
|
static DEFINE_HYP_SPINLOCK(hyp_fixblock_lock);
|
||||||
|
|
||||||
|
void *hyp_fixblock_map(phys_addr_t phys)
|
||||||
|
{
|
||||||
|
WARN_ON(!IS_ALIGNED(phys, PMD_SIZE));
|
||||||
|
|
||||||
|
hyp_spin_lock(&hyp_fixblock_lock);
|
||||||
|
return fixmap_map_slot(&hyp_fixblock_slot, phys);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hyp_fixblock_unmap(void)
|
||||||
|
{
|
||||||
|
fixmap_clear_slot(&hyp_fixblock_slot);
|
||||||
|
hyp_spin_unlock(&hyp_fixblock_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int create_fixblock(void)
|
||||||
|
{
|
||||||
|
struct kvm_pgtable_walker walker = {
|
||||||
|
.cb = __create_fixmap_slot_cb,
|
||||||
|
.flags = KVM_PGTABLE_WALK_LEAF,
|
||||||
|
.arg = (void *)&hyp_fixblock_slot,
|
||||||
|
};
|
||||||
|
unsigned long addr;
|
||||||
|
phys_addr_t phys;
|
||||||
|
int ret, i;
|
||||||
|
|
||||||
|
/* Find a RAM phys address, PMD aligned */
|
||||||
|
for (i = 0; i < hyp_memblock_nr; i++) {
|
||||||
|
phys = ALIGN(hyp_memory[i].base, PMD_SIZE);
|
||||||
|
if (phys + PMD_SIZE < (hyp_memory[i].base + hyp_memory[i].size))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Really? Your RAM isn't larger than a couple of times PMD_SIZE? */
|
||||||
|
if (i >= hyp_memblock_nr)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
hyp_spin_lock(&pkvm_pgd_lock);
|
||||||
|
addr = ALIGN(__io_map_base, PMD_SIZE);
|
||||||
|
ret = __pkvm_alloc_private_va_range(addr, PMD_SIZE);
|
||||||
|
if (ret)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
ret = kvm_pgtable_hyp_map(&pkvm_pgtable, addr, PMD_SIZE, phys, PAGE_HYP);
|
||||||
|
if (ret)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
hyp_fixblock_slot.level = KVM_PGTABLE_MAX_LEVELS - 2;
|
||||||
|
ret = kvm_pgtable_walk(&pkvm_pgtable, addr, PMD_SIZE, &walker);
|
||||||
|
unlock:
|
||||||
|
hyp_spin_unlock(&pkvm_pgd_lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void hyp_fixblock_unmap(void) { WARN_ON(1); }
|
||||||
|
void *hyp_fixblock_map(phys_addr_t phys) { return NULL; }
|
||||||
|
static int create_fixblock(void) { return 0; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int hyp_create_fixmap(void)
|
||||||
{
|
{
|
||||||
unsigned long addr, i;
|
unsigned long addr, i;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -402,7 +472,7 @@ int hyp_create_pcpu_fixmap(void)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return create_fixblock();
|
||||||
}
|
}
|
||||||
|
|
||||||
int hyp_create_idmap(u32 hyp_va_bits)
|
int hyp_create_idmap(u32 hyp_va_bits)
|
||||||
|
|
|
@ -362,7 +362,7 @@ void __noreturn __pkvm_init_finalise(void)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
ret = hyp_create_pcpu_fixmap();
|
ret = hyp_create_fixmap();
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user