The branch stable/13 has been updated by kib:

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

commit d4f7acfad7322852a85cded19af7ebb737119993
Author:     Konstantin Belousov <[email protected]>
AuthorDate: 2022-07-31 08:43:43 +0000
Commit:     Konstantin Belousov <[email protected]>
CommitDate: 2022-08-22 13:37:09 +0000

    iommu_gas: add iommu_gas_remove()
    
    (cherry picked from commit c9e4d25052065d1e0cbdb3a0eea3f21c9225d1d1)
---
 sys/dev/iommu/iommu.h     |   2 +
 sys/dev/iommu/iommu_gas.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++
 sys/dev/iommu/iommu_gas.h |   2 +
 3 files changed, 122 insertions(+)

diff --git a/sys/dev/iommu/iommu.h b/sys/dev/iommu/iommu.h
index ae4022c5c4f7..2531ef09d9cd 100644
--- a/sys/dev/iommu/iommu.h
+++ b/sys/dev/iommu/iommu.h
@@ -171,6 +171,8 @@ struct iommu_map_entry *iommu_gas_alloc_entry(struct 
iommu_domain *domain,
     u_int flags);
 void iommu_gas_free_entry(struct iommu_map_entry *entry);
 void iommu_gas_free_space(struct iommu_map_entry *entry);
+void iommu_gas_remove(struct iommu_domain *domain, iommu_gaddr_t start,
+    iommu_gaddr_t size);
 int iommu_gas_map(struct iommu_domain *domain,
     const struct bus_dma_tag_common *common, iommu_gaddr_t size, int offset,
     u_int eflags, u_int flags, vm_page_t *ma, struct iommu_map_entry **res);
diff --git a/sys/dev/iommu/iommu_gas.c b/sys/dev/iommu/iommu_gas.c
index bad56ab9140e..ca5a614060fe 100644
--- a/sys/dev/iommu/iommu_gas.c
+++ b/sys/dev/iommu/iommu_gas.c
@@ -600,6 +600,124 @@ iommu_gas_free_region(struct iommu_map_entry *entry)
        IOMMU_DOMAIN_UNLOCK(domain);
 }
 
+static struct iommu_map_entry *
+iommu_gas_remove_clip_left(struct iommu_domain *domain, iommu_gaddr_t start,
+    iommu_gaddr_t end, struct iommu_map_entry **r)
+{
+       struct iommu_map_entry *entry, *res, fentry;
+
+       IOMMU_DOMAIN_ASSERT_LOCKED(domain);
+       MPASS(start <= end);
+       MPASS(end <= domain->last_place->end);
+
+       /*
+        * Find an entry which contains the supplied guest's address
+        * start, or the first entry after the start.  Since we
+        * asserted that start is below domain end, entry should
+        * exist.  Then clip it if needed.
+        */
+       fentry.start = start + 1;
+       fentry.end = start + 1;
+       entry = RB_NFIND(iommu_gas_entries_tree, &domain->rb_root, &fentry);
+
+       if (entry->start >= start ||
+           (entry->flags & IOMMU_MAP_ENTRY_RMRR) != 0)
+               return (entry);
+
+       res = *r;
+       *r = NULL;
+       *res = *entry;
+       res->start = entry->end = start;
+       RB_UPDATE_AUGMENT(entry, rb_entry);
+       iommu_gas_rb_insert(domain, res);
+       return (res);
+}
+
+static bool
+iommu_gas_remove_clip_right(struct iommu_domain *domain,
+    iommu_gaddr_t end, struct iommu_map_entry *entry,
+    struct iommu_map_entry *r)
+{
+       if (entry->start >= end || (entry->flags & IOMMU_MAP_ENTRY_RMRR) != 0)
+               return (false);
+
+       *r = *entry;
+       r->end = entry->start = end;
+       RB_UPDATE_AUGMENT(entry, rb_entry);
+       iommu_gas_rb_insert(domain, r);
+       return (true);
+}
+
+static void
+iommu_gas_remove_unmap(struct iommu_domain *domain,
+    struct iommu_map_entry *entry, struct iommu_map_entries_tailq *gcp)
+{
+       IOMMU_DOMAIN_ASSERT_LOCKED(domain);
+
+       if ((entry->flags & (IOMMU_MAP_ENTRY_UNMAPPED |
+           IOMMU_MAP_ENTRY_REMOVING)) != 0)
+               return;
+       MPASS((entry->flags & IOMMU_MAP_ENTRY_PLACE) == 0);
+       entry->flags |= IOMMU_MAP_ENTRY_REMOVING;
+       TAILQ_INSERT_TAIL(gcp, entry, dmamap_link);
+}
+
+/*
+ * Remove specified range from the GAS of the domain.  Note that the
+ * removal is not guaranteed to occur upon the function return, it
+ * might be finalized some time after, when hardware reports that
+ * (queued) IOTLB invalidation was performed.
+ */
+void
+iommu_gas_remove(struct iommu_domain *domain, iommu_gaddr_t start,
+    iommu_gaddr_t size)
+{
+       struct iommu_map_entry *entry, *nentry, *r1, *r2;
+       struct iommu_map_entries_tailq gc;
+       iommu_gaddr_t end;
+
+       end = start + size;
+       r1 = iommu_gas_alloc_entry(domain, IOMMU_PGF_WAITOK);
+       r2 = iommu_gas_alloc_entry(domain, IOMMU_PGF_WAITOK);
+       TAILQ_INIT(&gc);
+
+       IOMMU_DOMAIN_LOCK(domain);
+
+       nentry = iommu_gas_remove_clip_left(domain, start, end, &r1);
+       RB_FOREACH_FROM(entry, iommu_gas_entries_tree, nentry) {
+               if (entry->start >= end)
+                       break;
+               KASSERT(start <= entry->start,
+                   ("iommu_gas_remove entry (%#jx, %#jx) start %#jx",
+                   entry->start, entry->end, start));
+               if ((entry->flags & IOMMU_MAP_ENTRY_RMRR) != 0)
+                       continue;
+               iommu_gas_remove_unmap(domain, entry, &gc);
+       }
+       if (iommu_gas_remove_clip_right(domain, end, entry, r2)) {
+               iommu_gas_remove_unmap(domain, r2, &gc);
+               r2 = NULL;
+       }
+
+#ifdef INVARIANTS
+       RB_FOREACH(entry, iommu_gas_entries_tree, &domain->rb_root) {
+               if ((entry->flags & IOMMU_MAP_ENTRY_RMRR) != 0)
+                       continue;
+               KASSERT(entry->end <= start || entry->start >= end,
+                   ("iommu_gas_remove leftover entry (%#jx, %#jx) range "
+                   "(%#jx, %#jx)",
+                   entry->start, entry->end, start, end));
+       }
+#endif
+
+       IOMMU_DOMAIN_UNLOCK(domain);
+       if (r1 != NULL)
+               iommu_gas_free_entry(r1);
+       if (r2 != NULL)
+               iommu_gas_free_entry(r2);
+       iommu_domain_unload(domain, &gc, true);
+}
+
 int
 iommu_gas_map(struct iommu_domain *domain,
     const struct bus_dma_tag_common *common, iommu_gaddr_t size, int offset,
diff --git a/sys/dev/iommu/iommu_gas.h b/sys/dev/iommu/iommu_gas.h
index a9d0df5f272f..75a57ec0478f 100644
--- a/sys/dev/iommu/iommu_gas.h
+++ b/sys/dev/iommu/iommu_gas.h
@@ -50,6 +50,8 @@
 #define        IOMMU_MAP_ENTRY_MAP     0x0004  /* Busdma created, linked by
                                           dmamap_link */
 #define        IOMMU_MAP_ENTRY_UNMAPPED        0x0010  /* No backing pages */
+#define        IOMMU_MAP_ENTRY_REMOVING        0x0020  /* In process of 
removal by
+                                                  iommu_gas_remove() */
 #define        IOMMU_MAP_ENTRY_READ    0x1000  /* Read permitted */
 #define        IOMMU_MAP_ENTRY_WRITE   0x2000  /* Write permitted */
 #define        IOMMU_MAP_ENTRY_SNOOP   0x4000  /* Snoop */

Reply via email to