[PATCH v5 2/6] KVM: arm64: nv: Introduce guest stage-2 tracking structures

Itaru Kitayama itaru.kitayama at fujitsu.com
Thu Aug 13 18:04:57 PDT 2026


On Mon, Aug 10, 2026 at 09:50:34PM +0100, Wei-Lin Chang wrote:
> In order to avoid unmapping all shadow stage-2 mappings when KVM
> receives a MMU notifier unmap call, we have to keep track of the
> canonical IPA -> nested IPA relationship of the shadow mappings
> created. This essentially means tracking the guest's stage-2.
> 
> To do this, represent each mapping by struct kvm_guest_s2_mapping. It
> stores the mapping's canonical IPA range and the nested IPA range using
> two interval tree nodes. Both nodes will be inserted into their
> respective interval trees called guest_s2_mappings. The canonical IPA
> ranges will be stored in the tree within the canonical MMU, and the
> nested IPA ranges will be stored in the corresponding nested MMU's tree.
> 
> For example:
> 
> 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
> 
> Using the trees we can look up nodes in either of the IPA spaces, and
> for each node, find the corresponding range in the other IPA space from
> the other node in the enclosing kvm_guest_s2_mapping.
> 
> Define kvm_guest_s2_mapping and the interval tree here. Guest stage-2
> mapping tracking will come in subsequent patches.
> 
> Signed-off-by: Wei-Lin Chang <weilin.chang at arm.com>
> ---
>  arch/arm64/include/asm/kvm_host.h | 17 +++++++++++++++++
>  arch/arm64/kvm/mmu.c              | 30 ++++++++++++++++++++++++++++++
>  arch/arm64/kvm/nested.c           |  1 +
>  3 files changed, 48 insertions(+)
> 
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index bae2c4f92ef5..0695c4ef93f1 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -14,6 +14,7 @@
>  #include <linux/arm-smccc.h>
>  #include <linux/bitmap.h>
>  #include <linux/types.h>
> +#include <linux/interval_tree.h>
>  #include <linux/jump_label.h>
>  #include <linux/kvm_types.h>
>  #include <linux/maple_tree.h>
> @@ -150,6 +151,16 @@ struct kvm_vmid {
>  	atomic64_t id;
>  };
>  
> +/*
> + * Record of a guest stage-2 mapping, storing canonical and nested IPA
> + * ranges. Both ranges have the same size.
> + */
> +struct kvm_guest_s2_mapping {
> +	struct interval_tree_node canonical;
> +	struct interval_tree_node nested;
> +	struct kvm_s2_mmu *nested_mmu;
> +};

Is this to be used for normal (L1) guests? I guess this series is for
shadow stage 2 unmapping optimization, so not sure.

Thanks,
Itaru.

> +
>  struct kvm_s2_mmu {
>  	struct kvm_vmid vmid;
>  
> @@ -227,6 +238,9 @@ struct kvm_s2_mmu {
>  	 */
>  	bool	pending_unmap;
>  
> +	/* Guest s2 mapping records indexed in this MMU's IPA space. */
> +	struct rb_root_cached guest_s2_mappings;
> +
>  	/*
>  	 *  0: Nobody is currently using this, check vttbr for validity
>  	 * >0: Somebody is actively using this.
> @@ -326,6 +340,9 @@ struct kvm_arch {
>  	size_t nested_mmus_size;
>  	int nested_mmus_next;
>  
> +	/* Guest s2 tracking trees access serialization. */
> +	spinlock_t guest_s2_tracking_lock;
> +
>  	/* Interrupt controller */
>  	struct vgic_dist	vgic;
>  
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 336dd8f7e8ab..59b4f583240e 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -7,6 +7,7 @@
>  #include <linux/acpi.h>
>  #include <linux/mman.h>
>  #include <linux/kvm_host.h>
> +#include <linux/interval_tree.h>
>  #include <linux/io.h>
>  #include <linux/hugetlb.h>
>  #include <linux/sched/signal.h>
> @@ -1033,6 +1034,8 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
>  
>  	mmu->pgd_phys = __pa(pgt->pgd);
>  
> +	mmu->guest_s2_mappings = RB_ROOT_CACHED;
> +
>  	if (kvm_is_nested_s2_mmu(kvm, mmu))
>  		kvm_init_nested_s2_mmu(mmu);
>  
> @@ -1122,10 +1125,32 @@ void stage2_unmap_vm(struct kvm *kvm)
>  	srcu_read_unlock(&kvm->srcu, idx);
>  }
>  
> +static void guest_s2_tracking_destroy(struct kvm_s2_mmu *mmu,
> +				      struct rb_root_cached *tree)
> +{
> +	struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
> +	struct kvm_guest_s2_mapping *mapping;
> +	struct interval_tree_node *node;
> +
> +	while ((node = interval_tree_iter_first(tree, 0, ULONG_MAX))) {
> +		interval_tree_remove(node, tree);
> +
> +		if (!kvm_is_nested_s2_mmu(kvm, mmu)) {
> +			mapping = container_of(node, struct kvm_guest_s2_mapping,
> +					       canonical);
> +			/* The canonical MMU is destroyed after the nested MMUs. */
> +			kfree(mapping);
> +		}
> +
> +		cond_resched();
> +	}
> +}
> +
>  void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu)
>  {
>  	struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
>  	struct kvm_pgtable *pgt = NULL;
> +	struct rb_root_cached mappings_tree;
>  
>  	write_lock(&kvm->mmu_lock);
>  	pgt = mmu->pgt;
> @@ -1138,12 +1163,17 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu)
>  	if (kvm_is_nested_s2_mmu(kvm, mmu))
>  		kvm_init_nested_s2_mmu(mmu);
>  
> +	mappings_tree = mmu->guest_s2_mappings;
> +	mmu->guest_s2_mappings = RB_ROOT_CACHED;
> +
>  	write_unlock(&kvm->mmu_lock);
>  
>  	if (pgt) {
>  		kvm_stage2_destroy(pgt);
>  		kfree(pgt);
>  	}
> +
> +	guest_s2_tracking_destroy(mmu, &mappings_tree);
>  }
>  
>  static void hyp_mc_free_fn(void *addr, void *mc)
> diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
> index dfb96edbdc43..744aacba61ae 100644
> --- a/arch/arm64/kvm/nested.c
> +++ b/arch/arm64/kvm/nested.c
> @@ -49,6 +49,7 @@ void kvm_init_nested(struct kvm *kvm)
>  	kvm->arch.nested_mmus = NULL;
>  	kvm->arch.nested_mmus_size = 0;
>  	atomic_set(&kvm->arch.vncr_map_count, 0);
> +	spin_lock_init(&kvm->arch.guest_s2_tracking_lock);
>  }
>  
>  static int init_nested_s2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu)
> -- 
> 2.43.0
> 



More information about the linux-arm-kernel mailing list