| From f8d6a3dbcd12ca4ce308d7075e3ac33ef96dc205 Mon Sep 17 00:00:00 2001 |
| From: David Stevens <stevensd@chromium.org> |
| Date: Thu, 29 Feb 2024 11:57:57 +0900 |
| Subject: [PATCH] BACKPORT: FROMLIST: KVM: x86: Migrate to kvm_follow_pfn() |
| |
| Migrate functions which need to be able to map non-refcounted struct |
| pages to kvm_follow_pfn(). These functions are kvm_faultin_pfn() and |
| reexecute_instruction(). The former requires replacing the async in/out |
| parameter with FOLL_NOWAIT parameter and the KVM_PFN_ERR_NEEDS_IO return |
| value (actually handling non-refcounted pages is complicated, so it will |
| be done in a followup). The latter is a straightforward refactor. |
| |
| APIC related callers do not need to migrate because KVM controls the |
| memslot, so it will always be regular memory. Prefetch related callers |
| do not need to be migrated because atomic gfn_to_pfn() calls can never |
| make it to hva_to_pfn_remapped(). |
| |
| Signed-off-by: David Stevens <stevensd@chromium.org> |
| Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com> |
| (am from https://patchwork.kernel.org/patch/13576448/) |
| (also found at https://lore.kernel.org/r/20240229025759.1187910-7-stevensd@google.com) |
| |
| BUG=b:328351865 |
| UPSTREAM-TASK=b:265081912 |
| TEST=tast run zork arc.Boot.vm |
| TEST=No panic on lazor after logging in 5 times with kernel |
| TEST=build with USE="debug lockdebug kcov" |
| |
| Change-Id: I1c58f1765aaf2923f92877c085261bb35b6cc8ce |
| Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/5359865 |
| Reviewed-by: Douglas Anderson <dianders@chromium.org> |
| Tested-by: David Stevens <stevensd@chromium.org> |
| Tested-by: Stephen Boyd <swboyd@chromium.org> |
| Commit-Queue: Douglas Anderson <dianders@chromium.org> |
| Reviewed-by: Stephen Boyd <swboyd@chromium.org> |
| --- |
| arch/x86/kvm/mmu/mmu.c | 44 ++++++++++++++++++++++++++++++++---------- |
| arch/x86/kvm/x86.c | 11 +++++++++-- |
| virt/kvm/kvm_main.c | 11 ++++------- |
| 3 files changed, 47 insertions(+), 19 deletions(-) |
| |
| diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c |
| index 662f62dfb2aa9f0d205ee02a88da0b687c2291d6..d2a611523cfddad77031e7079962c05a04c886a1 100644 |
| --- a/arch/x86/kvm/mmu/mmu.c |
| +++ b/arch/x86/kvm/mmu/mmu.c |
| @@ -4306,17 +4306,33 @@ static int kvm_faultin_pfn_private(struct kvm_vcpu *vcpu, |
| |
| static int __kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) |
| { |
| - bool async; |
| + struct kvm_memory_slot *slot = fault->slot; |
| + struct kvm_follow_pfn kfp = { |
| + .slot = slot, |
| + .gfn = fault->gfn, |
| + .flags = FOLL_GET | (fault->write ? FOLL_WRITE : 0), |
| + .try_map_writable = true, |
| + .guarded_by_mmu_notifier = true, |
| + .allow_non_refcounted_struct_page = false, |
| + }; |
| |
| if (fault->is_private) |
| return kvm_faultin_pfn_private(vcpu, fault); |
| |
| - async = false; |
| - fault->pfn = __gfn_to_pfn_memslot(fault->slot, fault->gfn, false, false, |
| - &async, fault->write, |
| - &fault->map_writable, &fault->hva); |
| - if (!async) |
| - return RET_PF_CONTINUE; /* *pfn has correct page already */ |
| + kfp.flags |= FOLL_NOWAIT; |
| + fault->pfn = kvm_follow_pfn(&kfp); |
| + |
| + if (!is_error_noslot_pfn(fault->pfn)) |
| + goto success; |
| + |
| + /* |
| + * If kvm_follow_pfn() failed because I/O is needed to fault in the |
| + * page, then either set up an asynchronous #PF to do the I/O, or if |
| + * doing an async #PF isn't possible, retry kvm_follow_pfn() with |
| + * I/O allowed. All other failures are fatal, i.e. retrying won't help. |
| + */ |
| + if (fault->pfn != KVM_PFN_ERR_NEEDS_IO) |
| + return RET_PF_CONTINUE; |
| |
| if (!fault->prefetch && kvm_can_do_async_pf(vcpu)) { |
| trace_kvm_try_async_get_page(fault->addr, fault->gfn); |
| @@ -4334,9 +4350,17 @@ static int __kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault |
| * to wait for IO. Note, gup always bails if it is unable to quickly |
| * get a page and a fatal signal, i.e. SIGKILL, is pending. |
| */ |
| - fault->pfn = __gfn_to_pfn_memslot(fault->slot, fault->gfn, false, true, |
| - NULL, fault->write, |
| - &fault->map_writable, &fault->hva); |
| + kfp.flags |= FOLL_INTERRUPTIBLE; |
| + kfp.flags &= ~FOLL_NOWAIT; |
| + fault->pfn = kvm_follow_pfn(&kfp); |
| + |
| + if (!is_error_noslot_pfn(fault->pfn)) |
| + goto success; |
| + |
| + return RET_PF_CONTINUE; |
| +success: |
| + fault->hva = kfp.hva; |
| + fault->map_writable = kfp.writable; |
| return RET_PF_CONTINUE; |
| } |
| |
| diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c |
| index 4197c9c3ef6553545071353a6e84109514ad1d92..6912a9503340e192c0068dd263a2eedf9e046257 100644 |
| --- a/arch/x86/kvm/x86.c |
| +++ b/arch/x86/kvm/x86.c |
| @@ -8847,6 +8847,7 @@ static bool reexecute_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, |
| { |
| gpa_t gpa = cr2_or_gpa; |
| kvm_pfn_t pfn; |
| + struct kvm_follow_pfn kfp; |
| |
| if (!(emulation_type & EMULTYPE_ALLOW_RETRY_PF)) |
| return false; |
| @@ -8876,7 +8877,13 @@ static bool reexecute_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, |
| * retry instruction -> write #PF -> emulation fail -> retry |
| * instruction -> ... |
| */ |
| - pfn = gfn_to_pfn(vcpu->kvm, gpa_to_gfn(gpa)); |
| + kfp = (struct kvm_follow_pfn) { |
| + .slot = gfn_to_memslot(vcpu->kvm, gpa_to_gfn(gpa)), |
| + .gfn = gpa_to_gfn(gpa), |
| + .flags = FOLL_GET | FOLL_WRITE, |
| + .allow_non_refcounted_struct_page = true, |
| + }; |
| + pfn = kvm_follow_pfn(&kfp); |
| |
| /* |
| * If the instruction failed on the error pfn, it can not be fixed, |
| @@ -8885,7 +8892,7 @@ static bool reexecute_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, |
| if (is_error_noslot_pfn(pfn)) |
| return false; |
| |
| - kvm_release_pfn_clean(pfn); |
| + kvm_release_page_clean(kfp.refcounted_page); |
| |
| /* |
| * If emulation may have been triggered by a write to a shadowed page |
| diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c |
| index 4855d169128552c6887f484cce6e28d882956c89..aff9ba1982a323c0789bbaf1b1ff5e8bd0346b21 100644 |
| --- a/virt/kvm/kvm_main.c |
| +++ b/virt/kvm/kvm_main.c |
| @@ -3401,6 +3401,9 @@ void kvm_release_page_clean(struct page *page) |
| { |
| WARN_ON(is_error_page(page)); |
| |
| + if (!page) |
| + return; |
| + |
| kvm_set_page_accessed(page); |
| put_page(page); |
| } |
| @@ -3408,16 +3411,10 @@ EXPORT_SYMBOL_GPL(kvm_release_page_clean); |
| |
| void kvm_release_pfn_clean(kvm_pfn_t pfn) |
| { |
| - struct page *page; |
| - |
| if (is_error_noslot_pfn(pfn)) |
| return; |
| |
| - page = kvm_pfn_to_refcounted_page(pfn); |
| - if (!page) |
| - return; |
| - |
| - kvm_release_page_clean(page); |
| + kvm_release_page_clean(kvm_pfn_to_refcounted_page(pfn)); |
| } |
| EXPORT_SYMBOL_GPL(kvm_release_pfn_clean); |
| |
| -- |
| 2.45.1.288.g0e0cd299f1-goog |
| |