[PATCH v3 13/16] KVM: TDX: Handle PAMT allocation in fault path

Rick Edgecombe
2025-09-18T23:23:09Z

From: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>

Install PAMT pages for TDX call backs called during the fault path.

There are two distinct cases when the kernel needs to allocate PAMT memory
in the fault path: for SEPT page tables in tdx_sept_link_private_spt() and
for leaf pages in tdx_sept_set_private_spte().

These code paths run in atomic context. Previous changes have made the
fault path top up the per-VCPU pool for memory allocations. Use it to do
tdx_pamt_get/put() for the fault path operations.

In the generic MMU these ops are inside functions that don’t always
operate from the vCPU contexts (for example zap paths), which means they
don’t have a struct kvm_vcpu handy. But for TDX they are always in a vCPU
context. Since the pool of pre-allocated pages is on the vCPU, use
kvm_get_running_vcpu() to get the vCPU. In case a new path appears where
this is not the case, leave some KVM_BUG_ON()’s.

Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
[Add feedback, update log]
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
---
v3:
- Use new pre-allocation method
- Updated log
- Some extra safety around kvm_get_running_vcpu()
---
arch/x86/kvm/vmx/tdx.c | 45 +++++++++++++++++++++++++++++++++++++-----
1 file changed, 40 insertions(+), 5 deletions(-)

diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index b274d350165c..a55a95558557 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -836,6 +836,7 @@ void tdx_vcpu_free(struct kvm_vcpu *vcpu)
{
struct kvm_tdx *kvm_tdx = to_kvm_tdx(vcpu->kvm);
struct vcpu_tdx *tdx = to_tdx(vcpu);
+ struct page *page;
int i;

/*
@@ -862,6 +863,9 @@ void tdx_vcpu_free(struct kvm_vcpu *vcpu)
tdx->vp.tdvpr_page = 0;
}

+ while ((page = get_tdx_prealloc_page(&tdx->prealloc)))
+ __free_page(page);
+
tdx->state = VCPU_TD_STATE_UNINITIALIZED;
}

@@ -1665,13 +1669,23 @@ static int tdx_mem_page_record_premap_cnt(struct kvm *kvm, gfn_t gfn,
static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn,
enum pg_level level, kvm_pfn_t pfn)
{
+ struct kvm_vcpu *vcpu = kvm_get_running_vcpu();
struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm);
+ struct vcpu_tdx *tdx = to_tdx(vcpu);
struct page *page = pfn_to_page(pfn);
+ int ret;
+
+ if (KVM_BUG_ON(!vcpu, kvm))
+ return -EINVAL;

/* TODO: handle large pages. */
if (KVM_BUG_ON(level != PG_LEVEL_4K, kvm))
return -EINVAL;

+ ret = tdx_pamt_get(page, &tdx->prealloc);
+ if (ret)
+ return ret;
+
/*
* Because guest_memfd doesn't support page migration with
* a_ops->migrate_folio (yet), no callback is triggered for KVM on page
@@ -1687,10 +1701,16 @@ static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn,
* barrier in tdx_td_finalize().
*/
smp_rmb();
- if (likely(kvm_tdx->state == TD_STATE_RUNNABLE))
- return tdx_mem_page_aug(kvm, gfn, level, page);

- return tdx_mem_page_record_premap_cnt(kvm, gfn, level, pfn);
+ if (likely(kvm_tdx->state == TD_STATE_RUNNABLE))
+ ret = tdx_mem_page_aug(kvm, gfn, level, page);
+ else
+ ret = tdx_mem_page_record_premap_cnt(kvm, gfn, level, pfn);
+
+ if (ret)
+ tdx_pamt_put(page);
+
+ return ret;
}

static int tdx_sept_drop_private_spte(struct kvm *kvm, gfn_t gfn,
@@ -1747,17 +1767,30 @@ static int tdx_sept_link_private_spt(struct kvm *kvm, gfn_t gfn,
enum pg_level level, void *private_spt)
{
int tdx_level = pg_level_to_tdx_sept_level(level);
- gpa_t gpa = gfn_to_gpa(gfn);
+ struct kvm_vcpu *vcpu = kvm_get_running_vcpu();
struct page *page = virt_to_page(private_spt);
+ struct vcpu_tdx *tdx = to_tdx(vcpu);
+ gpa_t gpa = gfn_to_gpa(gfn);
u64 err, entry, level_state;
+ int ret;
+
+ if (KVM_BUG_ON(!vcpu, kvm))
+ return -EINVAL;
+
+ ret = tdx_pamt_get(page, &tdx->prealloc);
+ if (ret)
+ return ret;

err = tdh_mem_sept_add(&to_kvm_tdx(kvm)->td, gpa, tdx_level, page, &entry,
&level_state);
- if (unlikely(IS_TDX_OPERAND_BUSY(err)))
+ if (unlikely(IS_TDX_OPERAND_BUSY(err))) {
+ tdx_pamt_put(page);
return -EBUSY;
+ }

if (KVM_BUG_ON(err, kvm)) {
pr_tdx_error_2(TDH_MEM_SEPT_ADD, err, entry, level_state);
+ tdx_pamt_put(page);
return -EIO;
}

@@ -2966,6 +2999,8 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx)
int ret, i;
u64 err;

+ INIT_LIST_HEAD(&tdx->prealloc.page_list);
+
page = tdx_alloc_page();
if (!page)
return -ENOMEM;
--
2.51.0