Re: [PATCH v6 09/11] KVM: TDX: Get/put PAMT pages when (un)mapping private memory

From: Binbin Wu

Date: Thu Jul 02 2026 - 23:23:48 EST


On 5/26/2026 10:35 AM, Rick Edgecombe wrote:
> From: "Kirill A. Shutemov" <kirill.shutemov@xxxxxxxxxxxxxxx>
>
> Add Dynamic PAMT support to KVM's S-EPT MMU by "getting" a PAMT page when
> adding guest memory (PAGE.ADD or PAGE.AUG), and "putting" the page when
> removing guest memory (PAGE.REMOVE).
>
> To access the per-vCPU PAMT caches without plumbing @vcpu throughout the
> TDP MMU, begrudgingly use kvm_get_running_vcpu() to get the vCPU, and bug
> the VM if KVM attempts to set an S-EPT leaf without an active vCPU. KVM
> only supports creating _new_ mappings in page (pre)fault paths, all of
> which require an active vCPU.
>
> The PAMT memory holds metadata for TDX-protected memory. With Dynamic
> PAMT, PAMT_4K is allocated on demand. The kernel supplies the TDX module
> with a few pages that cover 2M of host physical memory.
>
> Releases are balanced via tdx_pamt_put(): every control-page free goes
> through tdx_free_control_page(), and guest data pages are put directly on
> the successful tdh_mem_page_remove() path and in the
> tdx_mem_page_add/aug() error path.
>
> Assisted-by: Sashiko:claude-opus-4-6 GitHub Copilot:claude-opus-4-6 Claude:claude-opus-4-7
> Signed-off-by: Kirill A. Shutemov <kirill.shutemov@xxxxxxxxxxxxxxx>
> Co-developed-by: Sean Christopherson <seanjc@xxxxxxxxxx>
> Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx>
> Co-developed-by: Rick Edgecombe <rick.p.edgecombe@xxxxxxxxx>
> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@xxxxxxxxx>

Reviewed-by: Binbin Wu <binbin.wu@xxxxxxxxxxxxxxx>

One nit below.

[...]

> @@ -1669,16 +1683,29 @@ static struct page *tdx_spte_to_sept_pt(struct kvm *kvm, gfn_t gfn,
> static int tdx_sept_map_nonleaf_spte(struct kvm *kvm, gfn_t gfn,
> enum pg_level level, u64 new_spte)
> {
> + struct kvm_vcpu *vcpu = kvm_get_running_vcpu();
> + struct vcpu_tdx *tdx = to_tdx(vcpu);

Nit:
Is it better to move this after checking vcpu is not NULL?
Although tdx is not dereferenced in between, if vcpu is NULL,
it means container_of() does arithmetic to a NULL pointer.


> gpa_t gpa = gfn_to_gpa(gfn);
> u64 err, entry, level_state;
> struct page *sept_pt;
> + int ret;
> +
> + if (KVM_BUG_ON(!vcpu, kvm))
> + return -EIO;
>
> sept_pt = tdx_spte_to_sept_pt(kvm, gfn, new_spte, level);
> if (!sept_pt)
> return -EIO;
>
> + ret = tdx_pamt_get(page_to_pfn(sept_pt), &tdx->pamt_cache);
> + if (ret)
> + return ret;
> +
> err = tdh_mem_sept_add(&to_kvm_tdx(kvm)->td, gpa, level, sept_pt,
> &entry, &level_state);
> + if (err)
> + tdx_pamt_put(page_to_pfn(sept_pt));
> +
> if (unlikely(tdx_operand_busy(err)))
> return -EBUSY;
>
> @@ -1691,8 +1718,14 @@ static int tdx_sept_map_nonleaf_spte(struct kvm *kvm, gfn_t gfn,
> static int tdx_sept_map_leaf_spte(struct kvm *kvm, gfn_t gfn, enum pg_level level,
> u64 new_spte)
> {
> + struct kvm_vcpu *vcpu = kvm_get_running_vcpu();
> struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm);

Ditto

> kvm_pfn_t pfn = spte_to_pfn(new_spte);
> + struct vcpu_tdx *tdx = to_tdx(vcpu);
> + int ret;
> +
> + if (KVM_BUG_ON(!vcpu, kvm))
> + return -EIO;
>
[...]