diff options
| author | Ryan Libby <rlibby@FreeBSD.org> | 2020-12-09 18:43:58 +0000 |
|---|---|---|
| committer | Ryan Libby <rlibby@FreeBSD.org> | 2020-12-09 18:43:58 +0000 |
| commit | ee47a12a490f89226ae736f92d8578ec1ca3b157 (patch) | |
| tree | adb0781217a3a99c4be3504b2864d75660f92e2f /sys/dev | |
| parent | 6813f2420b236f852d18f1a582c77d17ed15d45f (diff) | |
Notes
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/iommu/iommu.h | 2 | ||||
| -rw-r--r-- | sys/dev/iommu/iommu_gas.c | 72 |
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) { |
