Re: [PATCH v6 04/11] x86/virt/tdx: Allocate ref counts for Dynamic PAMT memory

From: Sohil Mehta

Date: Wed Jul 08 2026 - 16:54:57 EST


On 5/25/2026 7:35 PM, Rick Edgecombe wrote:
> From: "Kirill A. Shutemov" <kirill.shutemov@xxxxxxxxxxxxxxx>
>
> The PAMT memory holds metadata for all possible TDX protected memory. Each
> physical address range is covered by PAMT entries at three levels (1GB,
> 2MB, 4KB). With Dynamic PAMT, the 4KB range of PAMT is allocated on
> demand. The kernel supplies the TDX module with page pairs to store the
> 4KB entries, which cover 2MB of host physical memory. The kernel must
> provide this page pair before using pages from the range for TDX. If this
> is not done, SEAMCALLs that give the pages to be protected by the TDX module
> will fail.
>
> Allocate reference counters for every 2MB range to track TDX memory usage.
> This can be used to handle concurrent get/put callers, in order to
> accurately determine when the dynamic 4KB level of Dynamic PAMT needs to
> be allocated and when it can be freed.
>
> This allocation will currently consume 2 MB for every 1 TB of address
> space from 0 to max_pfn. The allocation size will depend on how the RAM is
> physically laid out. In a worst case scenario where the entire 52-bit
> address space is covered this would be 8GB. Then the DPAMT refcount
> allocations could hypothetically cause the savings from Dynamic PAMT to go
> negative on exotic platforms with sparse, small amounts of memory.
>

...

> +/*
> + * On a machine with Dynamic PAMT, the kernel maintains a reference counter
> + * for every 2M range.

Commit log says every 2MB range.

The counter indicates how many users there are for
> + * the PAMT memory of the 2M range. The kernel allocates PAMT refcounts at
> + * initialization.
> + */
> +static atomic_t *pamt_refcounts;
> +
> /* All TDX-usable memory regions. Protected by mem_hotplug_lock. */
> static LIST_HEAD(tdx_memlist);
>
> @@ -254,6 +263,43 @@ static struct syscore tdx_syscore = {
> .ops = &tdx_syscore_ops,
> };
>
> +/*
> + * Allocate PAMT reference counters for all physical memory.
> + *
> + * It consumes 2MiB for every 1TiB of physical memory.

Commit log says 2MB and 1TB. I would make both consistent.

> + */
> +static int init_pamt_refcounts(void)
> +{
> + size_t size = DIV_ROUND_UP(max_pfn, PTRS_PER_PTE) * sizeof(*pamt_refcounts);
> +
> + if (!tdx_supports_dynamic_pamt(&tdx_sysinfo))
> + return 0;
> +
> + pamt_refcounts = __vmalloc(size, GFP_KERNEL | __GFP_ZERO);

vzalloc()?

> + if (!pamt_refcounts)
> + return -ENOMEM;
> +
> + return 0;
> +}
> +
> +static void free_pamt_refcounts(void)
> +{
> + if (!tdx_supports_dynamic_pamt(&tdx_sysinfo))
> + return;
> +
> + vfree(pamt_refcounts);
> + pamt_refcounts = NULL;
> +}
> +
> +/* Find PAMT refcount for a given physical address */

This comment is probably not that useful. The function name is
descriptive by itself.

> +static atomic_t * __maybe_unused tdx_find_pamt_refcount(unsigned long pfn)
> +{
> + /* Find which PMD a PFN is in. */
> + unsigned long index = pfn >> (PMD_SHIFT - PAGE_SHIFT);
> +
> + return &pamt_refcounts[index];
> +}
> +