Re: [PATCH v5 1/7] KVM: arm64: Skip cache maintenance for non-cacheable pKVM mappings
From: Vincent Donnefort
Date: Tue Jul 28 2026 - 12:04:09 EST
On Fri, Jul 17, 2026 at 02:03:11PM +0100, Fuad Tabba wrote:
> From: Bradley Morgan <include@xxxxxxxxx>
>
> The pKVM flush path walks its own pkvm_mappings list and cleans the
> data cache for every mapping, unlike the generic stage-2 walker it
> shadows, which skips non-cacheable leaves. Cleaning the cacheable
> alias of a non-cacheable mapping is pointless and can corrupt a
> device endpoint. Record whether a mapping is non-cacheable in spare
> bits of nr_pages and skip cache maintenance for it.
>
> Fixes: e912efed485a ("KVM: arm64: Introduce the EL1 pKVM MMU")
> Suggested-by: Marc Zyngier <maz@xxxxxxxxxx>
> Signed-off-by: Bradley Morgan <include@xxxxxxxxx>
> [tabba: use Marc's anonymous bitfield in place of the open-coded mask and helpers]
> Signed-off-by: Fuad Tabba <fuad.tabba@xxxxxxxxx>
Reviewed-by: Vincent Donnefort <vdonnefort@xxxxxxxxxx>
> ---
> arch/arm64/include/asm/kvm_pkvm.h | 5 ++++-
> arch/arm64/kvm/pkvm.c | 15 +++++++++------
> 2 files changed, 13 insertions(+), 7 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h
> index 74fedd9c5ff0..57afb07d6b13 100644
> --- a/arch/arm64/include/asm/kvm_pkvm.h
> +++ b/arch/arm64/include/asm/kvm_pkvm.h
> @@ -195,7 +195,10 @@ struct pkvm_mapping {
> struct rb_node node;
> u64 gfn;
> u64 pfn;
> - u64 nr_pages;
> + struct {
> + u64 nr_pages:48;
> + u64 nc:1;
> + };
> u64 __subtree_last; /* Internal member for interval tree */
> };
>
> diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
> index 053e4f733e4b..f70c601dcf4c 100644
> --- a/arch/arm64/kvm/pkvm.c
> +++ b/arch/arm64/kvm/pkvm.c
> @@ -369,7 +369,7 @@ static int __pkvm_pgtable_stage2_unshare(struct kvm_pgtable *pgt, u64 start, u64
>
> for_each_mapping_in_range_safe(pgt, start, end, mapping) {
> ret = kvm_call_hyp_nvhe(__pkvm_host_unshare_guest, handle, mapping->gfn,
> - mapping->nr_pages);
> + (u64)mapping->nr_pages);
> if (WARN_ON(ret))
> return ret;
> pkvm_mapping_remove(mapping, &pgt->pkvm_mappings);
> @@ -473,6 +473,7 @@ int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size,
> mapping->gfn = gfn;
> mapping->pfn = pfn;
> mapping->nr_pages = size / PAGE_SIZE;
> + mapping->nc = !!(prot & (KVM_PGTABLE_PROT_DEVICE | KVM_PGTABLE_PROT_NORMAL_NC));
> pkvm_mapping_insert(mapping, &pgt->pkvm_mappings);
>
> return ret;
> @@ -503,7 +504,7 @@ int pkvm_pgtable_stage2_wrprotect(struct kvm_pgtable *pgt, u64 addr, u64 size)
> lockdep_assert_held(&kvm->mmu_lock);
> for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping) {
> ret = kvm_call_hyp_nvhe(__pkvm_host_wrprotect_guest, handle, mapping->gfn,
> - mapping->nr_pages);
> + (u64)mapping->nr_pages);
> if (WARN_ON(ret))
> break;
> }
> @@ -517,9 +518,11 @@ int pkvm_pgtable_stage2_flush(struct kvm_pgtable *pgt, u64 addr, u64 size)
> struct pkvm_mapping *mapping;
>
> lockdep_assert_held(&kvm->mmu_lock);
> - for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping)
> - __clean_dcache_guest_page(pfn_to_kaddr(mapping->pfn),
> - PAGE_SIZE * mapping->nr_pages);
> + for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping) {
> + if (!mapping->nc)
> + __clean_dcache_guest_page(pfn_to_kaddr(mapping->pfn),
> + PAGE_SIZE * mapping->nr_pages);
> + }
>
> return 0;
> }
> @@ -537,7 +540,7 @@ bool pkvm_pgtable_stage2_test_clear_young(struct kvm_pgtable *pgt, u64 addr, u64
> lockdep_assert_held(&kvm->mmu_lock);
> for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping)
> young |= kvm_call_hyp_nvhe(__pkvm_host_test_clear_young_guest, handle, mapping->gfn,
> - mapping->nr_pages, mkold);
> + (u64)mapping->nr_pages, mkold);
>
> return young;
> }
> --
> 2.39.5
>
--
--
Vincent