aboutsummaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorRyan Libby <rlibby@FreeBSD.org>2020-12-09 18:43:58 +0000
committerRyan Libby <rlibby@FreeBSD.org>2020-12-09 18:43:58 +0000
commitee47a12a490f89226ae736f92d8578ec1ca3b157 (patch)
treeadb0781217a3a99c4be3504b2864d75660f92e2f /sys/dev
parent6813f2420b236f852d18f1a582c77d17ed15d45f (diff)
Notes
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/iommu/iommu.h2
-rw-r--r--sys/dev/iommu/iommu_gas.c72
2 files changed, 69 insertions, 5 deletions
diff --git a/sys/dev/iommu/iommu.h b/sys/dev/iommu/iommu.h
index 388a8cb3b2ce..ba592ea08ff1 100644
--- a/sys/dev/iommu/iommu.h
+++ b/sys/dev/iommu/iommu.h
@@ -199,6 +199,8 @@ int iommu_gas_map_region(struct iommu_domain *domain,
struct iommu_map_entry *entry, u_int eflags, u_int flags, vm_page_t *ma);
int iommu_gas_reserve_region(struct iommu_domain *domain, iommu_gaddr_t start,
iommu_gaddr_t end, struct iommu_map_entry **entry0);
+int iommu_gas_reserve_region_extend(struct iommu_domain *domain,
+ iommu_gaddr_t start, iommu_gaddr_t end);
void iommu_set_buswide_ctx(struct iommu_unit *unit, u_int busno);
bool iommu_is_buswide_ctx(struct iommu_unit *unit, u_int busno);
diff --git a/sys/dev/iommu/iommu_gas.c b/sys/dev/iommu/iommu_gas.c
index 841bb3c83da5..431c0c195c35 100644
--- a/sys/dev/iommu/iommu_gas.c
+++ b/sys/dev/iommu/iommu_gas.c
@@ -677,6 +677,22 @@ iommu_gas_map_region(struct iommu_domain *domain, struct iommu_map_entry *entry,
return (0);
}
+static int
+iommu_gas_reserve_region_locked(struct iommu_domain *domain,
+ iommu_gaddr_t start, iommu_gaddr_t end, struct iommu_map_entry *entry)
+{
+ int error;
+
+ IOMMU_DOMAIN_ASSERT_LOCKED(domain);
+
+ entry->start = start;
+ entry->end = end;
+ error = iommu_gas_alloc_region(domain, entry, IOMMU_MF_CANWAIT);
+ if (error == 0)
+ entry->flags |= IOMMU_MAP_ENTRY_UNMAPPED;
+ return (error);
+}
+
int
iommu_gas_reserve_region(struct iommu_domain *domain, iommu_gaddr_t start,
iommu_gaddr_t end, struct iommu_map_entry **entry0)
@@ -685,12 +701,8 @@ iommu_gas_reserve_region(struct iommu_domain *domain, iommu_gaddr_t start,
int error;
entry = iommu_gas_alloc_entry(domain, IOMMU_PGF_WAITOK);
- entry->start = start;
- entry->end = end;
IOMMU_DOMAIN_LOCK(domain);
- error = iommu_gas_alloc_region(domain, entry, IOMMU_MF_CANWAIT);
- if (error == 0)
- entry->flags |= IOMMU_MAP_ENTRY_UNMAPPED;
+ error = iommu_gas_reserve_region_locked(domain, start, end, entry);
IOMMU_DOMAIN_UNLOCK(domain);
if (error != 0)
iommu_gas_free_entry(domain, entry);
@@ -699,6 +711,56 @@ iommu_gas_reserve_region(struct iommu_domain *domain, iommu_gaddr_t start,
return (error);
}
+/*
+ * As in iommu_gas_reserve_region, reserve [start, end), but allow for existing
+ * entries.
+ */
+int
+iommu_gas_reserve_region_extend(struct iommu_domain *domain,
+ iommu_gaddr_t start, iommu_gaddr_t end)
+{
+ struct iommu_map_entry *entry, *next, *prev, key = {};
+ iommu_gaddr_t entry_start, entry_end;
+ int error;
+
+ error = 0;
+ entry = NULL;
+ end = ummin(end, domain->end);
+ while (start < end) {
+ /* Preallocate an entry. */
+ if (entry == NULL)
+ entry = iommu_gas_alloc_entry(domain,
+ IOMMU_PGF_WAITOK);
+ /* Calculate the free region from here to the next entry. */
+ key.start = key.end = start;
+ IOMMU_DOMAIN_LOCK(domain);
+ next = RB_NFIND(iommu_gas_entries_tree, &domain->rb_root, &key);
+ KASSERT(next != NULL, ("domain %p with end %#jx has no entry "
+ "after %#jx", domain, (uintmax_t)domain->end,
+ (uintmax_t)start));
+ entry_end = ummin(end, next->start);
+ prev = RB_PREV(iommu_gas_entries_tree, &domain->rb_root, next);
+ if (prev != NULL)
+ entry_start = ummax(start, prev->end);
+ else
+ entry_start = start;
+ start = next->end;
+ /* Reserve the region if non-empty. */
+ if (entry_start != entry_end) {
+ error = iommu_gas_reserve_region_locked(domain,
+ entry_start, entry_end, entry);
+ if (error != 0)
+ break;
+ entry = NULL;
+ }
+ IOMMU_DOMAIN_UNLOCK(domain);
+ }
+ /* Release a preallocated entry if it was not used. */
+ if (entry != NULL)
+ iommu_gas_free_entry(domain, entry);
+ return (error);
+}
+
struct iommu_map_entry *
iommu_map_alloc_entry(struct iommu_domain *domain, u_int flags)
{