From: Sean Christopherson <seanjc@google.com>
To: James Houghton <jthoughton@google.com>
Cc: Marco Marangoni <mamarang@amazon.com>,
Paolo Bonzini <pbonzini@redhat.com>,
Thomas Gleixner <tglx@kernel.org>,
Ingo Molnar <mingo@redhat.com>, Borislav Petkov <bp@alien8.de>,
Dave Hansen <dave.hansen@linux.intel.com>,
"x86@kernel.org" <x86@kernel.org>,
"H. Peter Anvin" <hpa@zytor.com>,
"kvm@vger.kernel.org" <kvm@vger.kernel.org>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
"kernel-patches@amazon.com" <kernel-patches@amazon.com>,
Riccardo Mancini <mancio@amazon.co.uk>,
Michael Zoumboulakis <zoumboul@amazon.com>,
Marco Marangoni <marco.marangoni@proton.me>
Subject: Re: [RFC] KVM: x86/mmu: Prefetch forward run of pages on TDP page faults
Date: Tue, 25 Aug 2026 14:04:42 -0700 [thread overview]
Message-ID: <ao4Daj0hnwjP9Lpf@google.com> (raw)
In-Reply-To: <ao4CufEI_pRCjbMF@google.com>
On Tue, Aug 25, 2026, Sean Christopherson wrote:
> Somewhat off the cuff and *very* lightly tested, but this seems to do what I want.
> If it provides comparable performance, I'll write a changelog (or two? e.g. to
> have direct MMUs switch in a separate patch), and let Sashiko and other bots rip
> apart my idea.
>
> Note! This has a hard dependency on in-flight prefaulting fixes[*]. Without
> those, prefaulting will hang the vCPU if the root is invalidated.
> [*] https://lore.kernel.org/all/20260806214050.78058-1-seanjc@google.com
>
> Note #2! The below deliberately ignores A/D-disabled MMUs. I can't think of
> any reason why it matters whether or not KVM can precisely detect accessed SPTEs,
> all of the aging stuff is already extremely fuzzy.
And of course I posted an untested version (I ripped out the direct MMU prefetching
as an afterthough, and dropped a printk). This version should actually compile.
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index 79c450d677b4..88b6aa1f840f 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -118,6 +118,9 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(tdp_mmu_enabled);
bool __read_mostly eager_page_split = true;
module_param(eager_page_split, bool, 0644);
+unsigned int __read_mostly auto_prefault_nr_pages = KVM_PAGES_PER_HPAGE(PG_LEVEL_2M);
+module_param(auto_prefault_nr_pages, uint, 0644);
+
static int max_huge_page_level __read_mostly;
static int tdp_root_level __read_mostly;
static int max_tdp_level __read_mostly;
@@ -3205,69 +3208,6 @@ static bool kvm_mmu_prefetch_sptes(struct kvm_vcpu *vcpu, gfn_t gfn, u64 *sptep,
return true;
}
-static bool direct_pte_prefetch_many(struct kvm_vcpu *vcpu,
- struct kvm_mmu_page *sp,
- u64 *start, u64 *end)
-{
- gfn_t gfn = kvm_mmu_page_get_gfn(sp, spte_index(start));
- unsigned int access = sp->role.access;
-
- return kvm_mmu_prefetch_sptes(vcpu, gfn, start, end - start, access);
-}
-
-static void __direct_pte_prefetch(struct kvm_vcpu *vcpu,
- struct kvm_mmu_page *sp, u64 *sptep)
-{
- u64 *spte, *start = NULL;
- int i;
-
- WARN_ON_ONCE(!sp->role.direct);
-
- i = spte_index(sptep) & ~(PTE_PREFETCH_NUM - 1);
- spte = sp->spt + i;
-
- for (i = 0; i < PTE_PREFETCH_NUM; i++, spte++) {
- if (is_shadow_present_pte(*spte) || spte == sptep) {
- if (!start)
- continue;
- if (!direct_pte_prefetch_many(vcpu, sp, start, spte))
- return;
-
- start = NULL;
- } else if (!start)
- start = spte;
- }
- if (start)
- direct_pte_prefetch_many(vcpu, sp, start, spte);
-}
-
-static void direct_pte_prefetch(struct kvm_vcpu *vcpu, u64 *sptep)
-{
- struct kvm_mmu_page *sp;
-
- sp = sptep_to_sp(sptep);
-
- /*
- * Without accessed bits, there's no way to distinguish between
- * actually accessed translations and prefetched, so disable pte
- * prefetch if accessed bits aren't available.
- */
- if (sp_ad_disabled(sp))
- return;
-
- if (sp->role.level > PG_LEVEL_4K)
- return;
-
- /*
- * If addresses are being invalidated, skip prefetching to avoid
- * accidentally prefetching those addresses.
- */
- if (unlikely(vcpu->kvm->mmu_invalidate_in_progress))
- return;
-
- __direct_pte_prefetch(vcpu, sp, sptep);
-}
-
/*
* Lookup the mapping level for @gfn in the current mm.
*
@@ -3502,8 +3442,8 @@ static int direct_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
{
struct kvm_shadow_walk_iterator it;
struct kvm_mmu_page *sp;
- int ret, access;
gfn_t base_gfn = fault->gfn;
+ int access;
kvm_mmu_hugepage_adjust(vcpu, fault);
@@ -3534,13 +3474,8 @@ static int direct_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
if (WARN_ON_ONCE(it.level != fault->goal_level))
return -EFAULT;
- ret = mmu_set_spte(vcpu, fault->slot, it.sptep, access,
- base_gfn, fault->pfn, fault);
- if (ret == RET_PF_SPURIOUS)
- return ret;
-
- direct_pte_prefetch(vcpu, it.sptep);
- return ret;
+ return mmu_set_spte(vcpu, fault->slot, it.sptep, access, base_gfn,
+ fault->pfn, fault);
}
static void kvm_send_hwpoison_signal(struct kvm_memory_slot *slot, gfn_t gfn)
@@ -6580,11 +6515,38 @@ static int kvm_mmu_write_protect_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
return RET_PF_EMULATE;
}
+static void kvm_mmu_auto_prefault(struct kvm_vcpu *vcpu, gpa_t start,
+ u64 error_code, u8 level)
+{
+ gfn_t nr_pages = READ_ONCE(auto_prefault_nr_pages);
+ int nr_pages_msb;
+ gfn_t i;
+
+ if (unlikely(error_code & PFERR_RSVD_MASK))
+ return;
+
+ nr_pages = min(nr_pages, KVM_PAGES_PER_HPAGE(PG_LEVEL_1G));
+ nr_pages_msb = find_last_bit((unsigned long *)&nr_pages, sizeof(nr_pages));
+
+ start = ALIGN_DOWN(start, gfn_to_gpa(BIT_ULL(nr_pages_msb)));
+
+ for (i = KVM_PAGES_PER_HPAGE(level); i < nr_pages; i += KVM_PAGES_PER_HPAGE(level)) {
+ gpa_t gpa = start + gfn_to_gpa(i);
+
+ if (gpa < start || gpa_to_gfn(gpa) > kvm_mmu_max_gfn())
+ return;
+
+ if (kvm_tdp_page_prefault(vcpu, gpa, error_code, &level))
+ return;
+ }
+}
+
int noinline kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 error_code,
void *insn, int insn_len)
{
int r, emulation_type = EMULTYPE_PF;
bool direct = vcpu->arch.mmu->root_role.direct;
+ u8 level;
if (WARN_ON_ONCE(!VALID_PAGE(vcpu->arch.mmu->root.hpa)))
return RET_PF_RETRY;
@@ -6617,7 +6579,7 @@ int noinline kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 err
vcpu->stat.pf_taken++;
r = kvm_mmu_do_page_fault(vcpu, cr2_or_gpa, error_code, false,
- &emulation_type, NULL);
+ &emulation_type, &level);
if (KVM_BUG_ON(r == RET_PF_INVALID, vcpu->kvm))
return -EIO;
}
@@ -6628,6 +6590,8 @@ int noinline kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 err
if (r == RET_PF_WRITE_PROTECTED)
r = kvm_mmu_write_protect_fault(vcpu, cr2_or_gpa, error_code,
&emulation_type);
+ else if (r == RET_PF_FIXED)
+ kvm_mmu_auto_prefault(vcpu, cr2_or_gpa, error_code, level);
if (r == RET_PF_FIXED)
vcpu->stat.pf_fixed++;
diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h
index 27427e7f22fa..b41b4b78cc81 100644
--- a/arch/x86/kvm/mmu/paging_tmpl.h
+++ b/arch/x86/kvm/mmu/paging_tmpl.h
@@ -617,7 +617,7 @@ static void FNAME(pte_prefetch)(struct kvm_vcpu *vcpu, struct guest_walker *gw,
sp = sptep_to_sp(sptep);
- if (sp->role.level > PG_LEVEL_4K)
+ if (sp->role.level > PG_LEVEL_4K || sp->role.direct)
return;
/*
@@ -627,9 +627,6 @@ static void FNAME(pte_prefetch)(struct kvm_vcpu *vcpu, struct guest_walker *gw,
if (unlikely(vcpu->kvm->mmu_invalidate_in_progress))
return;
- if (sp->role.direct)
- return __direct_pte_prefetch(vcpu, sp, sptep);
-
i = spte_index(sptep) & ~(PTE_PREFETCH_NUM - 1);
spte = sp->spt + i;
next prev parent reply other threads:[~2026-08-25 21:04 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-25 14:01 [RFC] KVM: x86/mmu: Prefetch forward run of pages on TDP page faults Marco Marangoni
2026-08-25 15:00 ` Sean Christopherson
2026-08-25 15:54 ` James Houghton
2026-08-25 15:59 ` James Houghton
2026-08-25 17:38 ` Marangoni, Marco
2026-08-25 18:06 ` James Houghton
2026-08-25 20:55 ` Marangoni, Marco
2026-08-25 21:01 ` Sean Christopherson
2026-08-25 21:04 ` Sean Christopherson [this message]
2026-08-25 21:40 ` Sean Christopherson
2026-08-25 22:33 ` Marangoni, Marco
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ao4Daj0hnwjP9Lpf@google.com \
--to=seanjc@google.com \
--cc=bp@alien8.de \
--cc=dave.hansen@linux.intel.com \
--cc=hpa@zytor.com \
--cc=jthoughton@google.com \
--cc=kernel-patches@amazon.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mamarang@amazon.com \
--cc=mancio@amazon.co.uk \
--cc=marco.marangoni@proton.me \
--cc=mingo@redhat.com \
--cc=pbonzini@redhat.com \
--cc=tglx@kernel.org \
--cc=x86@kernel.org \
--cc=zoumboul@amazon.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox