[PATCH v5 0/6] KVM: arm64: nv: Implement nested stage-2 reverse map (new data structure)

Itaru Kitayama itaru.kitayama at fujitsu.com
Tue Aug 11 19:12:14 PDT 2026


On Mon, Aug 10, 2026 at 09:50:32PM +0100, Wei-Lin Chang wrote:
> Hi,
> 
> This is v5 of optimizing the shadow s2 mmu unmapping during MMU
> notifiers.

I've tested your series v5 on a Grace system. L2 booted into prompt
with the Ubuntu filesystem image, your two kvm selftest for nested 
virtualization ran fine in L1, and also did stress-ng in L1:

projects $ sudo stress-ng --kvm 16 --cpu 16 --vm 8 --vma 8 --fork 8 --timeout 10m --verify    --metrics-brief
stress-ng: info:  [1053] setting to a 10 mins run per stressor
stress-ng: info:  [1053] dispatching hogs: 16 kvm, 16 cpu, 8 vm, 8 vma, 8 fork
stress-ng: info:  [1087] vm: using 32MB per stressor instance (total 256MB of 2.75GB available memory)
stress-ng: metrc: [1053] stressor       bogo ops real time  usr time  sys time   bogo ops/s     bogo ops/s
stress-ng: metrc: [1053]                           (secs)    (secs)    (secs)   (real time) (usr+sys time)
stress-ng: metrc: [1053] kvm                  81    602.31    250.98    357.55         0.13           0.13
stress-ng: metrc: [1053] cpu               63310    595.70    166.40      0.60       106.28         379.12
stress-ng: metrc: [1053] vm              4252063    600.90     38.98     56.59      7076.16       44491.45
stress-ng: metrc: [1053] vma               56633    601.76     12.64    157.96        94.11         331.97
stress-ng: metrc: [1053] fork                149    600.98      0.03      0.45         0.25         314.59
stress-ng: info:  [1053] skipped: 0
stress-ng: info:  [1053] passed: 56: kvm (16) cpu (16) vm (8) vma (8) fork (8)
stress-ng: info:  [1053] failed: 0
stress-ng: info:  [1053] metrics untrustworthy: 0
stress-ng: info:  [1053] successful run completed in 10 mins 8.62 secs

Tested-by: Itaru Kitayama <itaru.kitayama at fujitsu.com>

Thanks,
Itaru.

> 
> This time, a major overhaul is done to the implementation. After
> receiving some suggestions from Marc, I have identified that using the
> interval tree to store the guest stage-2 mappings solves many problems
> compared to using the maple tree.
> 
> Interval Tree vs Maple Tree
> ===========================
> 
> First of all, interval trees are capable of storing overlapping ranges,
> which is helpful when the L1 hypervisor maps something like:
> 
> nested IPA [x, x+4K) -> canonical IPA [a, a+4K)
> nested IPA [y, y+2M) -> canonical IPA [a, a+2M)
> 
> No problems with storing that in the interval tree with different nodes.
> We can avoid the maple tree UNKNOWN_IPA mechanism as a compromise.
> 
> Second, ideally we would want to save the canonical IPA <-> nested IPA
> mapping in both directions to allow MMU notifier unmap speed up, and
> stale shadow mapping removals. If we use the maple tree, we'll have to
> have 2 separate trees, and make sure they store the same mappings, which
> isn't simple given the first point.
> 
> On the other hand, by using this pattern:
> 
> /* Record of a guest stage-2 mapping. */
> struct kvm_guest_s2_mapping {
>        struct interval_tree_node canonical; // CIPA range of the mapping
>        struct interval_tree_node nested;    // NIPA range of the mapping
>        struct kvm_s2_mmu *nested_mmu;       // mmu of the NIPA space
> };
> 
> and equip each mmu with an interval tree storing mapping records
> corresponding to the IPA space it represents, we can insert the
> respective nodes into the canonical IPA tree, and the corresponding
> nested IPA tree. This makes it trivial to find the range of the other
> IPA space from a range in one IPA space.
> 
> Diagram to help understanding:
> 
> struct kvm_guest_s2_mapping mapping1, mapping2;
> 
>     ---------------------> mapping2.canonical
>     |                      mapping1.canonical
>     |                          ^   (both stored in canonical mmu's tree)
>     |                          |
> --*****-----------------------*****----------- CIPA
>    \\\\\                      |||||                  mapping1.nested_mmu
>     \\\\\                     \\\\\                            |
>      \\\\\                     \\\\\                           v
> ------\\\\\---------------------*****--------- NIPA #1 (nested mmu #1)
>        \\\\\                      |
>         \\\\\                     -> mapping1.nested
>          \\\\\                       (stored in nested mmu #1's tree)
>           \\\\\
> -----------*****------------------------------ NIPA #2 (nested mmu #2)
>              |                                                 ^
>              -> mapping2.nested                                |
>                 (stored in nested mmu #2's tree)   mapping2.nested_mmu
> 
> Third, maple tree does its own memory allocation. In the KVM stage-2
> fault path we only find out what the mapping ranges are after taking the
> KVM MMU lock, and the maple tree has to know the range and entry to be
> stored to preallocate, therefore in our case the maple tree is forced to
> only use GFP_NOWAIT, which isn't the best. With the interval tree the
> user does the memory management, and we can just allocate before taking
> the locks.
> 
> Locking
> =======
> 
> The guest_s2_tracking_lock serializes accesses to the tracking interval
> trees. It is taken after the mmu_lock. However in reality it is only
> taken after we take the read mmu_lock in the stage-2 fault path, as
> other accesses have the write mmu_lock already. This saves us some
> manual lock/unlocks.
> 
> vCPU Stage-2 Fault Scalability Reduction
> ========================================
> 
> KVM/arm64 is able to handle stage-2 faults from multiple vCPUs in
> parallel, thanks to the engineering done to the s2 pgtable code. However
> to safely insert mappings into the interval trees we have to serialize
> using the guest_s2_tracking_lock. We trade some performance in stage-2
> fault for faster MMU notifier unmaps, and keeping the unaffected shadow
> mappings.
> 
> Memory Usage
> ============
> 
> Each interval tree node is 48 bytes, and a kvm_guest_s2_mapping is 104
> bytes, residing in 128-byte slab objects. Each shadow stage-2 fault
> requires one kvm_guest_s2_mapping instance. This is 32MB for a fully 4KB
> mapped 1GB region, and 64KB for a 2MB mapped 1GB region.
> 
> Series Structure
> ================
> 
> Patch 1:   Preparatory refactoring.
> Patch 2:   Introduce data structures for guest stage-2 tracking.
> Patch 3-4: Guest stage-2 tracking addition and removal
> Patch 5:   Avoid full unmap during MMU notifier unmap using the tracked
>            guest stage-2 mapping information.
> Patch 6:   Minor clean up.
> 
> As this is a complete rework, I will omit the change log this time.
> Series is based on v7.2-rc5.
> 
> Thanks!
> 
> Link to v4: https://lore.kernel.org/kvmarm/20260714115926.2044757-1-weilin.chang@arm.com/
> 
> Wei-Lin Chang (6):
>   KVM: arm64: Use a variable for the canonical IPA in kvm_s2_fault_map()
>   KVM: arm64: nv: Introduce guest stage-2 tracking structures
>   KVM: arm64: nv: Track guest stage-2 mapping creation
>   KVM: arm64: nv: Track guest stage-2 mapping removal
>   KVM: arm64: nv: Avoid full shadow stage-2 unmap
>   KVM: arm64: Refactor kvm_unmap_gfn_range() with common variables
> 
>  arch/arm64/include/asm/kvm_host.h   |  20 ++++++
>  arch/arm64/include/asm/kvm_nested.h |   7 ++
>  arch/arm64/kvm/mmu.c                | 105 ++++++++++++++++++++++++----
>  arch/arm64/kvm/nested.c             |  95 +++++++++++++++++++++++++
>  4 files changed, 215 insertions(+), 12 deletions(-)
> 
> -- 
> 2.43.0
> 



More information about the linux-arm-kernel mailing list