summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/tdx.h16
-rw-r--r--arch/x86/virt/vmx/tdx/tdx.c61
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;
}