Skip to content

Commit c353fde

Browse files
vdonnefortMarc Zyngier
authored andcommitted
KVM: arm64: np-guest CMOs with PMD_SIZE fixmap
With the introduction of stage-2 huge mappings in the pKVM hypervisor, guest pages CMO is needed for PMD_SIZE size. 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 when stage-2 huge mappings are installed. On a Pixel6, the iterative solution resulted in a latency of ~700us, while the PMD_SIZE fixmap reduces it to ~100us. Because of the horrendous private range allocation that would be necessary, this is disabled for 64KiB pages systems. Suggested-by: Quentin Perret <qperret@google.com> Signed-off-by: Vincent Donnefort <vdonnefort@google.com> Signed-off-by: Quentin Perret <qperret@google.com> Link: https://lore.kernel.org/r/20250521124834.1070650-11-vdonnefort@google.com Signed-off-by: Marc Zyngier <maz@kernel.org>
1 parent db14091 commit c353fde

File tree

6 files changed

+123
-33
lines changed

6 files changed

+123
-33
lines changed

arch/arm64/include/asm/kvm_pgtable.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ typedef u64 kvm_pte_t;
5959

6060
#define KVM_PHYS_INVALID (-1ULL)
6161

62+
#define KVM_PTE_TYPE BIT(1)
63+
#define KVM_PTE_TYPE_BLOCK 0
64+
#define KVM_PTE_TYPE_PAGE 1
65+
#define KVM_PTE_TYPE_TABLE 1
66+
6267
#define KVM_PTE_LEAF_ATTR_LO GENMASK(11, 2)
6368

6469
#define KVM_PTE_LEAF_ATTR_LO_S1_ATTRIDX GENMASK(4, 2)

arch/arm64/kvm/hyp/include/nvhe/mm.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
extern struct kvm_pgtable pkvm_pgtable;
1414
extern hyp_spinlock_t pkvm_pgd_lock;
1515

16-
int hyp_create_pcpu_fixmap(void);
16+
int hyp_create_fixmap(void);
1717
void *hyp_fixmap_map(phys_addr_t phys);
1818
void hyp_fixmap_unmap(void);
19+
void *hyp_fixblock_map(phys_addr_t phys, size_t *size);
20+
void hyp_fixblock_unmap(void);
1921

2022
int hyp_create_idmap(u32 hyp_va_bits);
2123
int hyp_map_vectors(void);

arch/arm64/kvm/hyp/nvhe/mem_protect.c

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -216,34 +216,42 @@ static void guest_s2_put_page(void *addr)
216216
hyp_put_page(&current_vm->pool, addr);
217217
}
218218

219-
static void clean_dcache_guest_page(void *va, size_t size)
219+
static void __apply_guest_page(void *va, size_t size,
220+
void (*func)(void *addr, size_t size))
220221
{
221222
size += va - PTR_ALIGN_DOWN(va, PAGE_SIZE);
222223
va = PTR_ALIGN_DOWN(va, PAGE_SIZE);
223224
size = PAGE_ALIGN(size);
224225

225226
while (size) {
226-
__clean_dcache_guest_page(hyp_fixmap_map(__hyp_pa(va)),
227-
PAGE_SIZE);
228-
hyp_fixmap_unmap();
229-
va += PAGE_SIZE;
230-
size -= PAGE_SIZE;
227+
size_t map_size = PAGE_SIZE;
228+
void *map;
229+
230+
if (IS_ALIGNED((unsigned long)va, PMD_SIZE) && size >= PMD_SIZE)
231+
map = hyp_fixblock_map(__hyp_pa(va), &map_size);
232+
else
233+
map = hyp_fixmap_map(__hyp_pa(va));
234+
235+
func(map, map_size);
236+
237+
if (map_size == PMD_SIZE)
238+
hyp_fixblock_unmap();
239+
else
240+
hyp_fixmap_unmap();
241+
242+
size -= map_size;
243+
va += map_size;
231244
}
232245
}
233246

234-
static void invalidate_icache_guest_page(void *va, size_t size)
247+
static void clean_dcache_guest_page(void *va, size_t size)
235248
{
236-
size += va - PTR_ALIGN_DOWN(va, PAGE_SIZE);
237-
va = PTR_ALIGN_DOWN(va, PAGE_SIZE);
238-
size = PAGE_ALIGN(size);
249+
__apply_guest_page(va, size, __clean_dcache_guest_page);
250+
}
239251

240-
while (size) {
241-
__invalidate_icache_guest_page(hyp_fixmap_map(__hyp_pa(va)),
242-
PAGE_SIZE);
243-
hyp_fixmap_unmap();
244-
va += PAGE_SIZE;
245-
size -= PAGE_SIZE;
246-
}
252+
static void invalidate_icache_guest_page(void *va, size_t size)
253+
{
254+
__apply_guest_page(va, size, __invalidate_icache_guest_page);
247255
}
248256

249257
int kvm_guest_prepare_stage2(struct pkvm_hyp_vm *vm, void *pgd)

arch/arm64/kvm/hyp/nvhe/mm.c

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,8 @@ int hyp_map_vectors(void)
229229
return 0;
230230
}
231231

232-
void *hyp_fixmap_map(phys_addr_t phys)
232+
static void *fixmap_map_slot(struct hyp_fixmap_slot *slot, phys_addr_t phys)
233233
{
234-
struct hyp_fixmap_slot *slot = this_cpu_ptr(&fixmap_slots);
235234
kvm_pte_t pte, *ptep = slot->ptep;
236235

237236
pte = *ptep;
@@ -243,10 +242,21 @@ void *hyp_fixmap_map(phys_addr_t phys)
243242
return (void *)slot->addr;
244243
}
245244

245+
void *hyp_fixmap_map(phys_addr_t phys)
246+
{
247+
return fixmap_map_slot(this_cpu_ptr(&fixmap_slots), phys);
248+
}
249+
246250
static void fixmap_clear_slot(struct hyp_fixmap_slot *slot)
247251
{
248252
kvm_pte_t *ptep = slot->ptep;
249253
u64 addr = slot->addr;
254+
u32 level;
255+
256+
if (FIELD_GET(KVM_PTE_TYPE, *ptep) == KVM_PTE_TYPE_PAGE)
257+
level = KVM_PGTABLE_LAST_LEVEL;
258+
else
259+
level = KVM_PGTABLE_LAST_LEVEL - 1; /* create_fixblock() guarantees PMD level */
250260

251261
WRITE_ONCE(*ptep, *ptep & ~KVM_PTE_VALID);
252262

@@ -260,7 +270,7 @@ static void fixmap_clear_slot(struct hyp_fixmap_slot *slot)
260270
* https://lore.kernel.org/kvm/20221017115209.2099-1-will@kernel.org/T/#mf10dfbaf1eaef9274c581b81c53758918c1d0f03
261271
*/
262272
dsb(ishst);
263-
__tlbi_level(vale2is, __TLBI_VADDR(addr, 0), KVM_PGTABLE_LAST_LEVEL);
273+
__tlbi_level(vale2is, __TLBI_VADDR(addr, 0), level);
264274
dsb(ish);
265275
isb();
266276
}
@@ -273,9 +283,9 @@ void hyp_fixmap_unmap(void)
273283
static int __create_fixmap_slot_cb(const struct kvm_pgtable_visit_ctx *ctx,
274284
enum kvm_pgtable_walk_flags visit)
275285
{
276-
struct hyp_fixmap_slot *slot = per_cpu_ptr(&fixmap_slots, (u64)ctx->arg);
286+
struct hyp_fixmap_slot *slot = (struct hyp_fixmap_slot *)ctx->arg;
277287

278-
if (!kvm_pte_valid(ctx->old) || ctx->level != KVM_PGTABLE_LAST_LEVEL)
288+
if (!kvm_pte_valid(ctx->old) || (ctx->end - ctx->start) != kvm_granule_size(ctx->level))
279289
return -EINVAL;
280290

281291
slot->addr = ctx->addr;
@@ -296,13 +306,84 @@ static int create_fixmap_slot(u64 addr, u64 cpu)
296306
struct kvm_pgtable_walker walker = {
297307
.cb = __create_fixmap_slot_cb,
298308
.flags = KVM_PGTABLE_WALK_LEAF,
299-
.arg = (void *)cpu,
309+
.arg = per_cpu_ptr(&fixmap_slots, cpu),
300310
};
301311

302312
return kvm_pgtable_walk(&pkvm_pgtable, addr, PAGE_SIZE, &walker);
303313
}
304314

305-
int hyp_create_pcpu_fixmap(void)
315+
#if PAGE_SHIFT < 16
316+
#define HAS_FIXBLOCK
317+
static struct hyp_fixmap_slot hyp_fixblock_slot;
318+
static DEFINE_HYP_SPINLOCK(hyp_fixblock_lock);
319+
#endif
320+
321+
static int create_fixblock(void)
322+
{
323+
#ifdef HAS_FIXBLOCK
324+
struct kvm_pgtable_walker walker = {
325+
.cb = __create_fixmap_slot_cb,
326+
.flags = KVM_PGTABLE_WALK_LEAF,
327+
.arg = &hyp_fixblock_slot,
328+
};
329+
unsigned long addr;
330+
phys_addr_t phys;
331+
int ret, i;
332+
333+
/* Find a RAM phys address, PMD aligned */
334+
for (i = 0; i < hyp_memblock_nr; i++) {
335+
phys = ALIGN(hyp_memory[i].base, PMD_SIZE);
336+
if (phys + PMD_SIZE < (hyp_memory[i].base + hyp_memory[i].size))
337+
break;
338+
}
339+
340+
if (i >= hyp_memblock_nr)
341+
return -EINVAL;
342+
343+
hyp_spin_lock(&pkvm_pgd_lock);
344+
addr = ALIGN(__io_map_base, PMD_SIZE);
345+
ret = __pkvm_alloc_private_va_range(addr, PMD_SIZE);
346+
if (ret)
347+
goto unlock;
348+
349+
ret = kvm_pgtable_hyp_map(&pkvm_pgtable, addr, PMD_SIZE, phys, PAGE_HYP);
350+
if (ret)
351+
goto unlock;
352+
353+
ret = kvm_pgtable_walk(&pkvm_pgtable, addr, PMD_SIZE, &walker);
354+
355+
unlock:
356+
hyp_spin_unlock(&pkvm_pgd_lock);
357+
358+
return ret;
359+
#else
360+
return 0;
361+
#endif
362+
}
363+
364+
void *hyp_fixblock_map(phys_addr_t phys, size_t *size)
365+
{
366+
#ifdef HAS_FIXBLOCK
367+
*size = PMD_SIZE;
368+
hyp_spin_lock(&hyp_fixblock_lock);
369+
return fixmap_map_slot(&hyp_fixblock_slot, phys);
370+
#else
371+
*size = PAGE_SIZE;
372+
return hyp_fixmap_map(phys);
373+
#endif
374+
}
375+
376+
void hyp_fixblock_unmap(void)
377+
{
378+
#ifdef HAS_FIXBLOCK
379+
fixmap_clear_slot(&hyp_fixblock_slot);
380+
hyp_spin_unlock(&hyp_fixblock_lock);
381+
#else
382+
hyp_fixmap_unmap();
383+
#endif
384+
}
385+
386+
int hyp_create_fixmap(void)
306387
{
307388
unsigned long addr, i;
308389
int ret;
@@ -322,7 +403,7 @@ int hyp_create_pcpu_fixmap(void)
322403
return ret;
323404
}
324405

325-
return 0;
406+
return create_fixblock();
326407
}
327408

328409
int hyp_create_idmap(u32 hyp_va_bits)

arch/arm64/kvm/hyp/nvhe/setup.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ void __noreturn __pkvm_init_finalise(void)
312312
if (ret)
313313
goto out;
314314

315-
ret = hyp_create_pcpu_fixmap();
315+
ret = hyp_create_fixmap();
316316
if (ret)
317317
goto out;
318318

arch/arm64/kvm/hyp/pgtable.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,6 @@
1111
#include <asm/kvm_pgtable.h>
1212
#include <asm/stage2_pgtable.h>
1313

14-
15-
#define KVM_PTE_TYPE BIT(1)
16-
#define KVM_PTE_TYPE_BLOCK 0
17-
#define KVM_PTE_TYPE_PAGE 1
18-
#define KVM_PTE_TYPE_TABLE 1
19-
2014
struct kvm_pgtable_walk_data {
2115
struct kvm_pgtable_walker *walker;
2216

0 commit comments

Comments
 (0)