Re: [RFC PATCH 1/1] KVM: x86: Skip empty TDP leaf page tables

Hao Zhang <[email protected]>
Newsgroups org.kernel.vger.kvm
Message-ID <[email protected]>
On Fri, Aug 14, 2026, Sean Christopherson wrote:
> On Fri, Aug 14, 2026, Hao Zhang wrote:
> > From: Hao Zhang <[email protected]>
> > 
> > When KVM zaps only leaf SPTEs, the TDP page table hierarchy is
> > intentionally retained so that subsequent faults can reuse it.  However,
> > a later zap of the same range still descends through retained 4K leaf
> > page tables whose leaf SPTEs are all non-present.
> > 
> > Track whether a retained 4K leaf page table contains any present leaf
> > SPTEs.  If a zap fully covers the corresponding 2MiB range and the page
> > table is known to be empty, skip descending into it.
> > 
> > Keep the page table hierarchy linked so that it can still be reused by
> > future page faults.  Installing a new leaf SPTE clears the empty hint.
> > The hint checks are skipped for ranges smaller than a complete 2MiB leaf
> > page table.
> > 
> > The iterator restarts its walk from the root after yielding.  Allow the
> > restarted walk to mark an empty leaf page table again instead of
> > permanently excluding the page table that contained the yield.
> > 
> > Tested on a 4-vCPU, 1GB guest with five repetitions on the same host.
> > The main improvement is seen for repeated same-range invalidation; rolling
> > sweep workloads are mostly neutral.
> > 
> > Signed-off-by: Hao Zhang <[email protected]>
> > ---
> 
> Does the generic iterator solution I provided a (long) while back work for your
> use case?  I would still strongly prefer a generic solution that doesn't rely on
> storing metadata in the MMU page.
> 
> https://lore.kernel.org/all/[email protected]

Hi Sean,

I tested the generic iterator approach you pointed me at, i.e. skipping
non-present SPTEs in the TDP iterator and converting the relevant TDP MMU
walkers to use the shadow-present-only iterator.

The results do show that the generic approach helps, but it doesn't fully cover
the pathology I was trying to address.

All numbers below are medians over 5 runs on the same host/workload:

                original    generic iterator    empty-subtree hint
    same16        330 ms          222 ms              35 ms
    same4         341 ms          234 ms              54 ms
    c16          1456 ms         1696 ms            1386 ms
    c4           1252 ms         1291 ms            1244 ms
    c2           1197 ms         1174 ms            1246 ms
    c1           1353 ms         1199 ms            1138 ms

The generic iterator reduces zap time for the repeated same-range cases by
about 31-33%, but the empty-subtree hint reduces those cases by about 84-89%.

I think the reason is that the generic iterator only skips non-present SPTEs
within the current walk.  It doesn't carry any information across invalidations,
so a later zap of the same range still has to descend into the retained child
page table and rediscover that all 512 entries are non-present.  The hint avoids
that repeated discovery step by remembering that the child page table is empty
until a later fault installs a leaf SPTE.

I also measured fault wait time.  The generic iterator didn't show the severe
~1ms regression I had in an earlier experimental version that reduced yield
opportunities, but its fwait p99.9 was still higher than both the original
kernel and the empty-hint version in this workload:

                original    generic iterator    empty-subtree hint
    same16         78 us          162 us              61 us
    same4          62 us          152 us              38 us
    c16            72 us          242 us              77 us
    c4            102 us          248 us             105 us
    c2            108 us          270 us             107 us
    c1             92 us          266 us              93 us

Although the generic iterator is cleaner, but it may not enough to address 
the retained-empty-subtree case.  The key question seems to be whether KVM 
can keep a very narrow, derived hint on the shadow page: "this 4K leaf page 
table currently has no present leaf SPTEs".  The hint is cleared when a fault 
installs a leaf SPTE, and it is only used to skip a fully covered 2M subtree.

If storing that bit in struct kvm_mmu_page is still a non-starter, I can look at
a metadata-free version that checks whether the child page table is empty before
descending and skips it for the current walk.  But based on the mechanism, I
don't expect that to preserve the same benefit, because it still has to
rediscover the empty state on every invalidation.

Thanks,
Hao
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.