Re: [PATCH v4 02/17] x86/virt/tdx: Add a SEAMCALL wrapper to demote a 2MB huge page

From: Yan Zhao

Date: Thu Oct 22 2026 - 04:47:44 EST


On Thu, Oct 22, 2026 at 06:02:18AM +0800, Edgecombe, Rick P wrote:
> > > This patch will end up getting stripped down the minimum we need for this
> > > series. Any new functions or extra code will get scrutiny.
> > I agree!
> > (my only concern is the risk of NULL pointer dereference).
>
> As above, let's just match the existing code. Otherwise I think its just going
> to get questions and not merged. We want this to have as little to talk about as
> possible. If we want to change the defensive level (I don't), it is going to
> need a bunch of prep patches instead of just embarking on a new paradigm for
> additional huge page functions. I don't see a need to do that, and for the sake
> of getting basic huge page support, I think that is a good thing. Right?
Thanks, I'll follow your suggestion.

Does the following code look good to you?

u64 tdh_mem_page_demote_2mb(struct tdx_td *td, u64 gpa, kvm_pfn_t pfn, struct page *new_sept_pt,
struct tdx_pamt_cache *pamt_cache, u64 *ext_err1, u64 *ext_err2)
{
struct tdx_module_args args = {
.rcx = gpa | pg_level_to_tdx_sept_level(PG_LEVEL_2M),
.rdx = tdx_tdr_pa(td),
.r8 = page_to_phys(new_sept_pt),
};
struct page *dpamt_pages[TDX_DPAMT_ENTRY_PAGE_CNT];
atomic_t *dpamt_refcount;
u64 ret;

/*
* For implementation simplicity, only support demotion when
* DPAMT is enabled.
*/
WARN_ON_ONCE(!tdx_supports_dynamic_pamt(&tdx_sysinfo));

/* A NULL pamt_cache is unexpected, so failure indicates a software error */
ret = alloc_pamt_array(dpamt_pages, pamt_cache);
if (ret) {
*ext_err1 = 0;
*ext_err2 = 0;
return TDX_SW_ERROR;
}

dpamt_refcount = tdx_find_dpamt_refcount(pfn);

args.r12 = page_to_phys(dpamt_pages[0]);
args.r13 = page_to_phys(dpamt_pages[1]);

/*
* The dpamt_lock is used to avoid contention between the
* TDH_MEM_PAGE_DEMOTE and TDH_PHYMEM_PAMT_{ADD/REMOVE} SEAMCALLs.
*/
spin_lock(&dpamt_lock);

ret = seamcall_saved_ret(TDH_MEM_PAGE_DEMOTE, &args);
*ext_err1 = args.rcx;
*ext_err2 = args.rdx;

if (WARN_ON_ONCE(ret != TDX_SUCCESS))
goto out_free;

atomic_set(dpamt_refcount, PTRS_PER_PMD);
spin_unlock(&dpamt_lock);

return TDX_SUCCESS;

out_free:
spin_unlock(&dpamt_lock);
free_pamt_array(pamt_cache, dpamt_pages);

return ret;
}
EXPORT_SYMBOL_FOR_KVM(tdh_mem_page_demote_2mb);