[PATCH v2 03/30] KVM: arm64: Extract PFN resolution in user_mem_abort()
Anshuman Khandual
anshuman.khandual at arm.com
Sun Mar 29 21:33:11 PDT 2026
On 27/03/26 5:05 PM, Marc Zyngier wrote:
> From: Fuad Tabba <tabba at google.com>
>
> Extract the section of code responsible for pinning the physical page
> frame number (PFN) backing the faulting IPA into a new helper,
> kvm_s2_fault_pin_pfn().
>
> This helper encapsulates the critical section where the mmap_read_lock
> is held, the VMA is looked up, the mmu invalidate sequence is sampled,
> and the PFN is ultimately resolved via __kvm_faultin_pfn(). It also
> handles the early exits for hardware poisoned pages and noslot PFNs.
>
> By isolating this region, we can begin to organize the state variables
> required for PFN resolution into the kvm_s2_fault struct, clearing out
> a significant amount of local variable clutter from user_mem_abort().
>
> Signed-off-by: Fuad Tabba <tabba at google.com>
> Signed-off-by: Marc Zyngier <maz at kernel.org>
Reviewed-by: Anshuman Khandual <anshuman.khandual at arm.com>
> ---
> arch/arm64/kvm/mmu.c | 105 ++++++++++++++++++++++++-------------------
> 1 file changed, 59 insertions(+), 46 deletions(-)
>
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index b366bde15a429..5079a58b65b14 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -1740,55 +1740,11 @@ struct kvm_s2_fault {
> vm_flags_t vm_flags;
> };
>
> -static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
> - struct kvm_s2_trans *nested,
> - struct kvm_memory_slot *memslot, unsigned long hva,
> - bool fault_is_perm)
> +static int kvm_s2_fault_pin_pfn(struct kvm_s2_fault *fault)
> {
> - int ret = 0;
> - struct kvm_s2_fault fault_data = {
> - .vcpu = vcpu,
> - .fault_ipa = fault_ipa,
> - .nested = nested,
> - .memslot = memslot,
> - .hva = hva,
> - .fault_is_perm = fault_is_perm,
> - .ipa = fault_ipa,
> - .logging_active = memslot_is_logging(memslot),
> - .force_pte = memslot_is_logging(memslot),
> - .s2_force_noncacheable = false,
> - .vfio_allow_any_uc = false,
> - .prot = KVM_PGTABLE_PROT_R,
> - };
> - struct kvm_s2_fault *fault = &fault_data;
> - struct kvm *kvm = vcpu->kvm;
> struct vm_area_struct *vma;
> - void *memcache;
> - struct kvm_pgtable *pgt;
> - enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_SHARED;
> -
> - if (fault->fault_is_perm)
> - fault->fault_granule = kvm_vcpu_trap_get_perm_fault_granule(fault->vcpu);
> - fault->write_fault = kvm_is_write_fault(fault->vcpu);
> - fault->exec_fault = kvm_vcpu_trap_is_exec_fault(fault->vcpu);
> - VM_WARN_ON_ONCE(fault->write_fault && fault->exec_fault);
> + struct kvm *kvm = fault->vcpu->kvm;
>
> - /*
> - * Permission faults just need to update the existing leaf entry,
> - * and so normally don't require allocations from the memcache. The
> - * only exception to this is when dirty logging is enabled at runtime
> - * and a write fault needs to collapse a block entry into a table.
> - */
> - fault->topup_memcache = !fault->fault_is_perm ||
> - (fault->logging_active && fault->write_fault);
> - ret = prepare_mmu_memcache(fault->vcpu, fault->topup_memcache, &memcache);
> - if (ret)
> - return ret;
> -
> - /*
> - * Let's check if we will get back a huge page backed by hugetlbfs, or
> - * get block mapping for device MMIO region.
> - */
> mmap_read_lock(current->mm);
> vma = vma_lookup(current->mm, fault->hva);
> if (unlikely(!vma)) {
> @@ -1842,6 +1798,63 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
> if (is_error_noslot_pfn(fault->pfn))
> return -EFAULT;
>
> + return 1;
> +}
> +
> +static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
> + struct kvm_s2_trans *nested,
> + struct kvm_memory_slot *memslot, unsigned long hva,
> + bool fault_is_perm)
> +{
> + int ret = 0;
> + struct kvm_s2_fault fault_data = {
> + .vcpu = vcpu,
> + .fault_ipa = fault_ipa,
> + .nested = nested,
> + .memslot = memslot,
> + .hva = hva,
> + .fault_is_perm = fault_is_perm,
> + .ipa = fault_ipa,
> + .logging_active = memslot_is_logging(memslot),
> + .force_pte = memslot_is_logging(memslot),
> + .s2_force_noncacheable = false,
> + .vfio_allow_any_uc = false,
> + .prot = KVM_PGTABLE_PROT_R,
> + };
> + struct kvm_s2_fault *fault = &fault_data;
> + struct kvm *kvm = vcpu->kvm;
> + void *memcache;
> + struct kvm_pgtable *pgt;
> + enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_SHARED;
> +
> + if (fault->fault_is_perm)
> + fault->fault_granule = kvm_vcpu_trap_get_perm_fault_granule(fault->vcpu);
> + fault->write_fault = kvm_is_write_fault(fault->vcpu);
> + fault->exec_fault = kvm_vcpu_trap_is_exec_fault(fault->vcpu);
> + VM_WARN_ON_ONCE(fault->write_fault && fault->exec_fault);
> +
> + /*
> + * Permission faults just need to update the existing leaf entry,
> + * and so normally don't require allocations from the memcache. The
> + * only exception to this is when dirty logging is enabled at runtime
> + * and a write fault needs to collapse a block entry into a table.
> + */
> + fault->topup_memcache = !fault->fault_is_perm ||
> + (fault->logging_active && fault->write_fault);
> + ret = prepare_mmu_memcache(fault->vcpu, fault->topup_memcache, &memcache);
> + if (ret)
> + return ret;
> +
> + /*
> + * Let's check if we will get back a huge page backed by hugetlbfs, or
> + * get block mapping for device MMIO region.
> + */
> + ret = kvm_s2_fault_pin_pfn(fault);
> + if (ret != 1)
> + return ret;
> +
> + ret = 0;
> +
> /*
> * Check if this is non-struct page memory PFN, and cannot support
> * CMOs. It could potentially be unsafe to access as cacheable.
More information about the linux-arm-kernel
mailing list