git: 3848dc4fe6f3 - main - dmar: on unmap, postpone freeing page table pages after the invalidation is done

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Fri, 27 Sep 2024 17:34:49 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=3848dc4fe6f37cbcfaea29eb94fa560a503f60ab

commit 3848dc4fe6f37cbcfaea29eb94fa560a503f60ab
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2024-09-25 03:54:44 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2024-09-27 17:34:23 +0000

    dmar: on unmap, postpone freeing page table pages after the invalidation is done
    
    IOMMU is free to access page tables until we invalidate them
    
    Sponsored by:   Advanced Micro Devices (AMD)
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
---
 sys/x86/iommu/intel_idpgtbl.c | 31 ++++++++++++++++++-------------
 1 file changed, 18 insertions(+), 13 deletions(-)

diff --git a/sys/x86/iommu/intel_idpgtbl.c b/sys/x86/iommu/intel_idpgtbl.c
index da2750df5567..6f66106822fe 100644
--- a/sys/x86/iommu/intel_idpgtbl.c
+++ b/sys/x86/iommu/intel_idpgtbl.c
@@ -68,7 +68,8 @@
 #include <x86/iommu/intel_dmar.h>
 
 static int dmar_unmap_buf_locked(struct dmar_domain *domain,
-    iommu_gaddr_t base, iommu_gaddr_t size, int flags);
+    iommu_gaddr_t base, iommu_gaddr_t size, int flags,
+    struct iommu_map_entry *entry);
 
 /*
  * The cache of the identity mapping page tables for the DMARs.  Using
@@ -386,7 +387,8 @@ retry:
 
 static int
 dmar_map_buf_locked(struct dmar_domain *domain, iommu_gaddr_t base,
-    iommu_gaddr_t size, vm_page_t *ma, uint64_t pflags, int flags)
+    iommu_gaddr_t size, vm_page_t *ma, uint64_t pflags, int flags,
+    struct iommu_map_entry *entry)
 {
 	iommu_pte_t *pte;
 	struct sf_buf *sf;
@@ -446,7 +448,7 @@ dmar_map_buf_locked(struct dmar_domain *domain, iommu_gaddr_t base,
 			if (sf != NULL)
 				iommu_unmap_pgtbl(sf);
 			dmar_unmap_buf_locked(domain, base1, base - base1,
-			    flags);
+			    flags, entry);
 			TD_PINNED_ASSERT;
 			return (ENOMEM);
 		}
@@ -517,7 +519,8 @@ dmar_map_buf(struct iommu_domain *iodom, struct iommu_map_entry *entry,
 	KASSERT((flags & ~IOMMU_PGF_WAITOK) == 0, ("invalid flags %x", flags));
 
 	DMAR_DOMAIN_PGLOCK(domain);
-	error = dmar_map_buf_locked(domain, base, size, ma, pflags, flags);
+	error = dmar_map_buf_locked(domain, base, size, ma, pflags, flags,
+	    entry);
 	DMAR_DOMAIN_PGUNLOCK(domain);
 	if (error != 0)
 		return (error);
@@ -535,11 +538,11 @@ dmar_map_buf(struct iommu_domain *iodom, struct iommu_map_entry *entry,
 
 static void dmar_unmap_clear_pte(struct dmar_domain *domain,
     iommu_gaddr_t base, int lvl, int flags, iommu_pte_t *pte,
-    struct sf_buf **sf, bool free_fs);
+    struct sf_buf **sf, struct iommu_map_entry *entry, bool free_fs);
 
 static void
 dmar_free_pgtbl_pde(struct dmar_domain *domain, iommu_gaddr_t base,
-    int lvl, int flags)
+    int lvl, int flags, struct iommu_map_entry *entry)
 {
 	struct sf_buf *sf;
 	iommu_pte_t *pde;
@@ -547,12 +550,14 @@ dmar_free_pgtbl_pde(struct dmar_domain *domain, iommu_gaddr_t base,
 
 	sf = NULL;
 	pde = dmar_pgtbl_map_pte(domain, base, lvl, flags, &idx, &sf);
-	dmar_unmap_clear_pte(domain, base, lvl, flags, pde, &sf, true);
+	dmar_unmap_clear_pte(domain, base, lvl, flags, pde, &sf,
+	    entry, true);
 }
 
 static void
 dmar_unmap_clear_pte(struct dmar_domain *domain, iommu_gaddr_t base, int lvl,
-    int flags, iommu_pte_t *pte, struct sf_buf **sf, bool free_sf)
+    int flags, iommu_pte_t *pte, struct sf_buf **sf,
+    struct iommu_map_entry *entry, bool free_sf)
 {
 	vm_page_t m;
 
@@ -571,8 +576,8 @@ 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, NULL);
-	dmar_free_pgtbl_pde(domain, base, lvl - 1, flags);
+	iommu_pgfree(domain->pgtbl_obj, m->pindex, flags, entry);
+	dmar_free_pgtbl_pde(domain, base, lvl - 1, flags, entry);
 }
 
 /*
@@ -580,7 +585,7 @@ dmar_unmap_clear_pte(struct dmar_domain *domain, iommu_gaddr_t base, int lvl,
  */
 static int
 dmar_unmap_buf_locked(struct dmar_domain *domain, iommu_gaddr_t base,
-    iommu_gaddr_t size, int flags)
+    iommu_gaddr_t size, int flags, struct iommu_map_entry *entry)
 {
 	iommu_pte_t *pte;
 	struct sf_buf *sf;
@@ -631,7 +636,7 @@ dmar_unmap_buf_locked(struct dmar_domain *domain, iommu_gaddr_t base,
 			if ((pte->pte & DMAR_PTE_SP) != 0 ||
 			    lvl == domain->pglvl - 1) {
 				dmar_unmap_clear_pte(domain, base, lvl,
-				    flags, pte, &sf, false);
+				    flags, pte, &sf, entry, false);
 				break;
 			}
 		}
@@ -661,7 +666,7 @@ dmar_unmap_buf(struct iommu_domain *iodom, struct iommu_map_entry *entry,
 
 	DMAR_DOMAIN_PGLOCK(domain);
 	error = dmar_unmap_buf_locked(domain, entry->start, entry->end -
-	    entry->start, flags);
+	    entry->start, flags, entry);
 	DMAR_DOMAIN_PGUNLOCK(domain);
 	return (error);
 }