[PATCH v4 06/14] KVM: arm64: Propagate and use kvm_s2_fault_result on S2 fault
Fuad Tabba
fuad.tabba at linux.dev
Wed Sep 23 11:25:00 PDT 2026
On Wed, 23 Sept 2026 at 16:17, Lorenzo Stoakes (ARM) <ljs at kernel.org> wrote:
>
> When stage 2 page tables fault the net result may either be that a page is
> mapped, an error occurred or the fault should be retried (-EAGAIN).
>
> When a fault succeeds it may be upgraded to a PMD size via
> transparent_hugepage_adjust().
>
> In order to support KVM pre-faulting the outcome of the fault and the
> mapping size must be recorded.
>
> Track the mapping size via new kvm_s2_fault_result struct, which is
> threaded through gmem_abort(), user_mem_abort() and kvm_s2_fault_map().
>
> PKVM and SEA aren't relevant to synthetic pre-faulting so neither
> kvm_inject_sea() nor pkvm_mem_abort() are altered.
>
> Actual hardware faulting doesn't require this information, so
> kvm_handle_guest_abort() simply passes NULL kvm_s2_fault_result to
> gmem_abort() and user_mem_abort().
>
> A non-NULL result also tells the abort handlers that the caller is
> pre-faulting rather than a vcpu, in which case -EAGAIN is propagated to the
> caller to allow the pre-fault to be retried.
>
> Likewise, only prepare a KVM_EXIT_MEMORY_FAULT in gmem_abort() when a real
> fault is being handled, as this updates vcpu state.
>
> No functional change intended.
>
> Suggested-by: Vincent Donnefort <vdonnefort at google.com>
> Reviewed-by: Oliver Upton <oupton at kernel.org>
> Signed-off-by: Lorenzo Stoakes (ARM) <ljs at kernel.org>
Reviewed-by: Fuad Tabba <fuad.tabba at linux.dev>
Cheers,
/fuad
> ---
> arch/arm64/kvm/mmu.c | 43 +++++++++++++++++++++++++++++++------------
> 1 file changed, 31 insertions(+), 12 deletions(-)
>
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 9d799f7c1e62..ee2a4f0faa14 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -1659,6 +1659,10 @@ struct kvm_s2_fault_desc {
> struct kvm_s2_mmu *mmu;
> };
>
> +struct kvm_s2_fault_result {
> + unsigned long mapping_size;
> +};
> +
> static bool kvm_s2_fault_is_perm(const struct kvm_s2_fault_desc *s2fd)
> {
> return esr_fsc_is_permission_fault(s2fd->esr);
> @@ -1684,7 +1688,8 @@ static u64 kvm_s2_perm_fault_granule(const struct kvm_s2_fault_desc *s2fd)
> return BIT(ARM64_HW_PGTABLE_LEVEL_SHIFT(level));
> }
>
> -static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
> +static int gmem_abort(const struct kvm_s2_fault_desc *s2fd,
> + struct kvm_s2_fault_result *result)
> {
> bool write_fault, exec_fault;
> bool perm_fault = kvm_s2_fault_is_perm(s2fd);
> @@ -1728,8 +1733,10 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
>
> ret = kvm_gmem_get_pfn(kvm, s2fd->memslot, gfn, &pfn, &page, NULL);
> if (ret) {
> - kvm_prepare_memory_fault_exit(s2fd->vcpu, s2fd->fault_ipa, PAGE_SIZE,
> - write_fault, exec_fault, false);
> + /* If result is non-NULL this is a synthetic fault. */
> + if (!result)
> + kvm_prepare_memory_fault_exit(s2fd->vcpu, s2fd->fault_ipa, PAGE_SIZE,
> + write_fault, exec_fault, false);
> kfree(mapping);
> return ret;
> }
> @@ -1784,7 +1791,13 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
> if ((prot & KVM_PGTABLE_PROT_W) && !ret)
> mark_page_dirty_in_slot(kvm, s2fd->memslot, gfn);
>
> - return ret != -EAGAIN ? ret : 0;
> + if (ret == -EAGAIN)
> + return result ? ret : 0;
> +
> + if (result && !ret)
> + result->mapping_size = PAGE_SIZE;
> +
> + return ret;
> }
>
> struct kvm_s2_fault_vma_info {
> @@ -2108,7 +2121,8 @@ static int kvm_s2_fault_compute_prot(const struct kvm_s2_fault_desc *s2fd,
> static int kvm_s2_fault_map(const struct kvm_s2_fault_desc *s2fd,
> const struct kvm_s2_fault_vma_info *s2vi,
> enum kvm_pgtable_prot prot,
> - void *memcache)
> + void *memcache,
> + struct kvm_s2_fault_result *result)
> {
> enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_SHARED;
> struct kvm_guest_s2_mapping *mapping = NULL;
> @@ -2209,12 +2223,17 @@ static int kvm_s2_fault_map(const struct kvm_s2_fault_desc *s2fd,
> mark_page_dirty_in_slot(kvm, s2fd->memslot,
> gpa_to_gfn(canonical_ipa));
>
> - if (ret != -EAGAIN)
> - return ret;
> - return 0;
> + if (ret == -EAGAIN)
> + return result ? ret : 0;
> +
> + if (result && !ret)
> + result->mapping_size = mapping_size;
> +
> + return ret;
> }
>
> -static int user_mem_abort(const struct kvm_s2_fault_desc *s2fd)
> +static int user_mem_abort(const struct kvm_s2_fault_desc *s2fd,
> + struct kvm_s2_fault_result *result)
> {
> bool perm_fault = kvm_s2_fault_is_perm(s2fd);
> struct kvm_s2_fault_vma_info s2vi = {};
> @@ -2253,7 +2272,7 @@ static int user_mem_abort(const struct kvm_s2_fault_desc *s2fd)
> return ret;
> }
>
> - return kvm_s2_fault_map(s2fd, &s2vi, prot, memcache);
> + return kvm_s2_fault_map(s2fd, &s2vi, prot, memcache, result);
> }
>
> /* Resolve the access fault by making the page young again. */
> @@ -2532,9 +2551,9 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
> !kvm_s2_fault_is_exec(&s2fd));
>
> if (kvm_slot_has_gmem(memslot))
> - ret = gmem_abort(&s2fd);
> + ret = gmem_abort(&s2fd, NULL);
> else
> - ret = user_mem_abort(&s2fd);
> + ret = user_mem_abort(&s2fd, NULL);
> }
>
> if (ret == 0)
>
> --
> 2.55.0
>
More information about the linux-arm-kernel
mailing list