diff options
| -rw-r--r-- | arch/x86/include/asm/tdx.h | 16 | ||||
| -rw-r--r-- | arch/x86/virt/vmx/tdx/tdx.c | 61 |
2 files changed, 71 insertions, 6 deletions
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index f7442ad20e46..8c7839d61296 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -120,7 +120,21 @@ static inline bool tdx_supports_runtime_update(const struct tdx_sys_info *sysinf bool tdx_supports_dynamic_pamt(const struct tdx_sys_info *sysinfo); -int tdx_pamt_get(kvm_pfn_t pfn); +/* Simple structure for pre-allocating DPAMT pages outside of spinlocks. */ +struct tdx_pamt_cache { + struct list_head page_list; + int cnt; +}; + +static inline void tdx_init_pamt_cache(struct tdx_pamt_cache *cache) +{ + INIT_LIST_HEAD(&cache->page_list); + cache->cnt = 0; +} + +void tdx_free_pamt_cache(struct tdx_pamt_cache *cache); +int tdx_topup_pamt_cache(struct tdx_pamt_cache *cache, unsigned long npages); +int tdx_pamt_get(kvm_pfn_t pfn, struct tdx_pamt_cache *cache); void tdx_pamt_put(kvm_pfn_t pfn); int tdx_guest_keyid_alloc(void); diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index c347600a0aab..39865a2da582 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -2050,12 +2050,33 @@ bool tdx_supports_dynamic_pamt(const struct tdx_sys_info *sysinfo) return false; } -static int alloc_pamt_array(struct page **pamt_pages) +static struct page *tdx_alloc_page_pamt_cache(struct tdx_pamt_cache *cache) +{ + struct page *page; + + page = list_first_entry_or_null(&cache->page_list, struct page, lru); + if (page) { + list_del(&page->lru); + cache->cnt--; + } + + return page; +} + +static struct page *alloc_dpamt_page(struct tdx_pamt_cache *cache) +{ + if (cache) + return tdx_alloc_page_pamt_cache(cache); + + return alloc_page(GFP_KERNEL_ACCOUNT); +} + +static int alloc_pamt_array(struct page **pamt_pages, struct tdx_pamt_cache *cache) { int i, j; for (i = 0; i < TDX_DPAMT_ENTRY_PAGE_CNT; i++) { - pamt_pages[i] = alloc_page(GFP_KERNEL_ACCOUNT); + pamt_pages[i] = alloc_dpamt_page(cache); if (!pamt_pages[i]) goto err; } @@ -2132,7 +2153,7 @@ static u64 tdh_phymem_pamt_remove(kvm_pfn_t pfn, struct page **pamt_pages) static DEFINE_SPINLOCK(dpamt_lock); /* Bump DPAMT refcount for the given pfn and allocate DPAMT backing if needed. */ -int tdx_pamt_get(kvm_pfn_t pfn) +int tdx_pamt_get(kvm_pfn_t pfn, struct tdx_pamt_cache *cache) { struct page *pamt_pages[TDX_DPAMT_ENTRY_PAGE_CNT]; atomic_t *dpamt_refcount; @@ -2142,7 +2163,7 @@ int tdx_pamt_get(kvm_pfn_t pfn) if (!tdx_supports_dynamic_pamt(&tdx_sysinfo)) return 0; - ret = alloc_pamt_array(pamt_pages); + ret = alloc_pamt_array(pamt_pages, cache); if (ret) return ret; @@ -2220,6 +2241,36 @@ out_unlock: } EXPORT_SYMBOL_FOR_KVM(tdx_pamt_put); +void tdx_free_pamt_cache(struct tdx_pamt_cache *cache) +{ + struct page *page; + + while ((page = tdx_alloc_page_pamt_cache(cache))) + __free_page(page); +} +EXPORT_SYMBOL_FOR_KVM(tdx_free_pamt_cache); + +int tdx_topup_pamt_cache(struct tdx_pamt_cache *cache, unsigned long npages) +{ + if (WARN_ON_ONCE(!tdx_supports_dynamic_pamt(&tdx_sysinfo))) + return 0; + + npages *= TDX_DPAMT_ENTRY_PAGE_CNT; + + while (cache->cnt < npages) { + struct page *page = alloc_page(GFP_KERNEL_ACCOUNT); + + if (!page) + return -ENOMEM; + + list_add(&page->lru, &cache->page_list); + cache->cnt++; + } + + return 0; +} +EXPORT_SYMBOL_FOR_KVM(tdx_topup_pamt_cache); + /* * Return a page that can be gifted to the TDX module for use as a "control" * page, i.e. pages that are used for control structures for a given TDX @@ -2233,7 +2284,7 @@ struct page *tdx_alloc_control_page(void) if (!page) return NULL; - if (tdx_pamt_get(page_to_pfn(page))) { + if (tdx_pamt_get(page_to_pfn(page), NULL)) { __free_page(page); return NULL; } |
