git: 0c7e13cfe204 - stable/14 - iommu: extend iommu_map_entry to store the list of associated freed page table pages

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Sat, 05 Oct 2024 07:11:35 UTC
The branch stable/14 has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=0c7e13cfe2045fb17a0583d0a2a1f685225bf7d5

commit 0c7e13cfe2045fb17a0583d0a2a1f685225bf7d5
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2024-09-25 01:50:34 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2024-10-05 07:08:56 +0000

    iommu: extend iommu_map_entry to store the list of associated freed page table pages
    
    (cherry picked from commit f713ed6694d949ec37365533afbb47c04f919572)
---
 sys/dev/iommu/iommu.h         |  3 +++
 sys/dev/iommu/iommu_gas.c     | 14 +++++++++++---
 sys/x86/iommu/intel_idpgtbl.c |  4 ++--
 sys/x86/iommu/iommu_utils.c   | 15 +++++++++++----
 sys/x86/iommu/x86_iommu.h     |  3 ++-
 5 files changed, 29 insertions(+), 10 deletions(-)

diff --git a/sys/dev/iommu/iommu.h b/sys/dev/iommu/iommu.h
index 84d8c3680b71..73d0a6f1279b 100644
--- a/sys/dev/iommu/iommu.h
+++ b/sys/dev/iommu/iommu.h
@@ -31,6 +31,8 @@
 #ifndef _DEV_IOMMU_IOMMU_H_
 #define _DEV_IOMMU_IOMMU_H_
 
+#include <vm/vm.h>
+#include <vm/vm_page.h>
 #include <dev/iommu/iommu_types.h>
 
 struct bus_dma_tag_common;
@@ -61,6 +63,7 @@ struct iommu_map_entry {
 	RB_ENTRY(iommu_map_entry) rb_entry;	 /* Links for domain entries */
 	struct iommu_domain *domain;
 	struct iommu_qi_genseq gseq;
+	struct spglist pgtbl_free;
 };
 
 struct iommu_unit {
diff --git a/sys/dev/iommu/iommu_gas.c b/sys/dev/iommu/iommu_gas.c
index 26ac38da3c4f..d97bdee47b28 100644
--- a/sys/dev/iommu/iommu_gas.c
+++ b/sys/dev/iommu/iommu_gas.c
@@ -96,9 +96,12 @@ iommu_gas_alloc_entry(struct iommu_domain *domain, u_int flags)
 
 	res = uma_zalloc(iommu_map_entry_zone, ((flags & IOMMU_PGF_WAITOK) !=
 	    0 ? M_WAITOK : M_NOWAIT) | M_ZERO);
-	if (res != NULL && domain != NULL) {
-		res->domain = domain;
-		atomic_add_int(&domain->entries_cnt, 1);
+	if (res != NULL) {
+		SLIST_INIT(&res->pgtbl_free);
+		if (domain != NULL) {
+			res->domain = domain;
+			atomic_add_int(&domain->entries_cnt, 1);
+		}
 	}
 	return (res);
 }
@@ -107,7 +110,12 @@ void
 iommu_gas_free_entry(struct iommu_map_entry *entry)
 {
 	struct iommu_domain *domain;
+	int n __unused;
 
+	n = vm_page_free_pages_toq(&entry->pgtbl_free, false);
+#if defined(__i386__) || defined(__amd64__)
+	atomic_subtract_int(&iommu_tbl_pagecnt, n);
+#endif
 	domain = entry->domain;
 	if (domain != NULL)
 		atomic_subtract_int(&domain->entries_cnt, 1);
diff --git a/sys/x86/iommu/intel_idpgtbl.c b/sys/x86/iommu/intel_idpgtbl.c
index a949e38cc9b1..9dc7a2d58b3f 100644
--- a/sys/x86/iommu/intel_idpgtbl.c
+++ b/sys/x86/iommu/intel_idpgtbl.c
@@ -368,7 +368,7 @@ retry:
 				    ("loosing root page %p", domain));
 				vm_page_unwire_noq(m);
 				iommu_pgfree(domain->pgtbl_obj, m->pindex,
-				    flags);
+				    flags, NULL);
 				return (NULL);
 			}
 			dmar_pte_store(&ptep->pte, DMAR_PTE_R | DMAR_PTE_W |
@@ -572,7 +572,7 @@ dmar_unmap_clear_pte(struct dmar_domain *domain, iommu_gaddr_t base, int lvl,
 	KASSERT(m->pindex != 0,
 	    ("lost reference (idx) on root pg domain %p base %jx lvl %d",
 	    domain, (uintmax_t)base, lvl));
-	iommu_pgfree(domain->pgtbl_obj, m->pindex, flags);
+	iommu_pgfree(domain->pgtbl_obj, m->pindex, flags, NULL);
 	dmar_free_pgtbl_pde(domain, base, lvl - 1, flags);
 }
 
diff --git a/sys/x86/iommu/iommu_utils.c b/sys/x86/iommu/iommu_utils.c
index 2011c632f770..8ff15cc86cb8 100644
--- a/sys/x86/iommu/iommu_utils.c
+++ b/sys/x86/iommu/iommu_utils.c
@@ -109,7 +109,8 @@ iommu_pgalloc(vm_object_t obj, vm_pindex_t idx, int flags)
 }
 
 void
-iommu_pgfree(vm_object_t obj, vm_pindex_t idx, int flags)
+iommu_pgfree(vm_object_t obj, vm_pindex_t idx, int flags,
+    struct iommu_map_entry *entry)
 {
 	vm_page_t m;
 
@@ -117,8 +118,13 @@ iommu_pgfree(vm_object_t obj, vm_pindex_t idx, int flags)
 		VM_OBJECT_WLOCK(obj);
 	m = vm_page_grab(obj, idx, VM_ALLOC_NOCREAT);
 	if (m != NULL) {
-		vm_page_free(m);
-		atomic_subtract_int(&iommu_tbl_pagecnt, 1);
+		if (entry == NULL) {
+			vm_page_free(m);
+			atomic_subtract_int(&iommu_tbl_pagecnt, 1);
+		} else {
+			vm_page_remove_xbusy(m);	/* keep page busy */
+			SLIST_INSERT_HEAD(&entry->pgtbl_free, m, plinks.s.ss);
+		}
 	}
 	if ((flags & IOMMU_PGF_OBJL) == 0)
 		VM_OBJECT_WUNLOCK(obj);
@@ -154,7 +160,8 @@ iommu_map_pgtbl(vm_object_t obj, vm_pindex_t idx, int flags,
 		sched_unpin();
 		if (allocated) {
 			VM_OBJECT_ASSERT_WLOCKED(obj);
-			iommu_pgfree(obj, m->pindex, flags | IOMMU_PGF_OBJL);
+			iommu_pgfree(obj, m->pindex, flags | IOMMU_PGF_OBJL,
+			    NULL);
 		}
 		if ((flags & IOMMU_PGF_OBJL) == 0)
 			VM_OBJECT_WUNLOCK(obj);
diff --git a/sys/x86/iommu/x86_iommu.h b/sys/x86/iommu/x86_iommu.h
index a1ed5c71c513..4d0ac8351e2e 100644
--- a/sys/x86/iommu/x86_iommu.h
+++ b/sys/x86/iommu/x86_iommu.h
@@ -48,7 +48,8 @@ struct vm_object;
 
 struct vm_page *iommu_pgalloc(struct vm_object *obj, vm_pindex_t idx,
     int flags);
-void iommu_pgfree(struct vm_object *obj, vm_pindex_t idx, int flags);
+void iommu_pgfree(struct vm_object *obj, vm_pindex_t idx, int flags,
+    struct iommu_map_entry *entry);
 void *iommu_map_pgtbl(struct vm_object *obj, vm_pindex_t idx, int flags,
     struct sf_buf **sf);
 void iommu_unmap_pgtbl(struct sf_buf *sf);