Page MenuHomeFreeBSD

D36001.id108953.diff
No OneTemporary

D36001.id108953.diff

diff --git a/sys/dev/iommu/iommu.h b/sys/dev/iommu/iommu.h
--- a/sys/dev/iommu/iommu.h
+++ b/sys/dev/iommu/iommu.h
@@ -140,6 +140,7 @@
page table */
#define IOMMU_DOMAIN_RMRR 0x0020 /* Domain contains RMRR entry,
cannot be turned off */
+#define IOMMU_DOMAIN_BUSY 0x0040
#define IOMMU_LOCK(unit) mtx_lock(&(unit)->lock)
#define IOMMU_UNLOCK(unit) mtx_unlock(&(unit)->lock)
@@ -171,6 +172,8 @@
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
--- a/sys/dev/iommu/iommu_gas.c
+++ b/sys/dev/iommu/iommu_gas.c
@@ -600,6 +600,131 @@
IOMMU_DOMAIN_UNLOCK(domain);
}
+static void
+iommu_gas_remove_clip_left(struct iommu_domain *domain, iommu_gaddr_t start,
+ iommu_gaddr_t end, struct iommu_map_entry **first,
+ struct iommu_map_entry **r)
+{
+ struct iommu_map_entry *entry, *nentry, 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);
+ MPASS(start <= entry->start || (entry->start <= start &&
+ start <= entry->end));
+
+ if (entry->start < start &&
+ (entry->flags & IOMMU_MAP_ENTRY_RMRR) == 0) {
+ nentry = *r;
+ *r = NULL;
+ *nentry = *entry;
+ nentry->start = entry->end = start;
+ RB_UPDATE_AUGMENT(entry, rb_entry);
+ iommu_gas_rb_insert(domain, nentry);
+ *first = nentry;
+ } else {
+ *first = entry;
+ }
+}
+
+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)
+{
+ int error __diagused;
+
+ entry->flags &= ~IOMMU_MAP_ENTRY_MAP;
+ if ((entry->flags & IOMMU_MAP_ENTRY_UNMAPPED) == 0) {
+ MPASS((entry->flags & IOMMU_MAP_ENTRY_PLACE) == 0);
+ error = domain->ops->unmap(domain, entry->start,
+ entry->end - entry->start, 0);
+ MPASS(error == 0);
+
+ IOMMU_DOMAIN_UNLOCK(domain);
+ iommu_domain_unload_entry(entry, true, true);
+ IOMMU_DOMAIN_LOCK(domain);
+ }
+}
+
+void
+iommu_gas_remove(struct iommu_domain *domain, iommu_gaddr_t start,
+ iommu_gaddr_t size)
+{
+ struct iommu_map_entry *r1, *r2, *first, *nentry;
+ struct iommu_map_entry *entry __diagused;
+ 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);
+
+ IOMMU_DOMAIN_LOCK(domain);
+ while ((domain->flags & IOMMU_DOMAIN_BUSY) != 0)
+ msleep(&domain->flags, &domain->lock, 0, "ioubsy", 0);
+ domain->flags |= IOMMU_DOMAIN_BUSY;
+
+ iommu_gas_remove_clip_left(domain, start, end, &first, &r1);
+ nentry = first;
+ 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);
+ }
+ if (iommu_gas_remove_clip_right(domain, end, entry, r2)) {
+ iommu_gas_remove_unmap(domain, r2);
+ 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
+
+ domain->flags &= ~IOMMU_DOMAIN_BUSY;
+ wakeup(&domain->flags);
+ IOMMU_DOMAIN_UNLOCK(domain);
+ if (r1 != NULL)
+ iommu_gas_free_entry(r1);
+ if (r2 != NULL)
+ iommu_gas_free_entry(r2);
+}
+
int
iommu_gas_map(struct iommu_domain *domain,
const struct bus_dma_tag_common *common, iommu_gaddr_t size, int offset,
@@ -623,6 +748,8 @@
return (ENOMEM);
a.entry = entry;
IOMMU_DOMAIN_LOCK(domain);
+ while ((domain->flags & IOMMU_DOMAIN_BUSY) != 0)
+ msleep(&domain->flags, &domain->lock, 0, "iombsy", 0);
error = iommu_gas_find_space(&a);
if (error == ENOMEM) {
IOMMU_DOMAIN_UNLOCK(domain);

File Metadata

Mime Type
text/plain
Expires
Tue, Dec 30, 8:08 AM (15 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27379265
Default Alt Text
D36001.id108953.diff (4 KB)

Event Timeline