diff --git a/share/man/man3/tree.3 b/share/man/man3/tree.3 --- a/share/man/man3/tree.3 +++ b/share/man/man3/tree.3 @@ -523,10 +523,25 @@ and .Fn RB_NFIND macros can be used to find a particular element in the tree. +.Pp +The +.Fn RB_FIND +macro returns the element in the tree equal to the provided +key, or +.Dv NULL +if there is no such element. +.Pp +The +.Fn RB_NFIND +macro returns the least element greater than or equal to the provided +key, or +.Dv NULL +if there is no such element. .Bd -literal -offset indent -struct TYPE find, *res; +struct TYPE find, *res, *resn; find.key = 30; res = RB_FIND(NAME, head, &find); +resn = RB_NFIND(NAME, head, &find); .Ed .Pp The @@ -609,7 +624,9 @@ macro updates augmentation data of the element .Fa elm and its ancestors in the tree. -If RB_AUGMENT is defined by the RB user, then when an element in the +If +.Fn RB_AUGMENT +is defined by the RB user, then when an element in the tree is changed, without changing the order of items in the tree, invoking this function on that element restores consistency of the augmentation state of the tree as if the element had been removed and 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 @@ -171,6 +171,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,103 @@ IOMMU_DOMAIN_UNLOCK(domain); } +static void +iommu_gas_remove_entry_locked(struct iommu_domain *domain, + struct iommu_map_entry *entry) +{ + if ((entry->flags & IOMMU_MAP_ENTRY_RMRR) != 0) + return; + iommu_gas_free_space(entry); + domain->ops->unmap(domain, entry->start, entry->end - entry->start, + IOMMU_PGF_WAITOK); + if ((entry->flags & IOMMU_MAP_ENTRY_PLACE) == 0) + iommu_gas_free_entry(entry); +} + +static void +iommu_gas_remove_locked(struct iommu_domain *domain, iommu_gaddr_t start, + iommu_gaddr_t end, 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) { + if (end < entry->end) { + nentry = *r; + *r = NULL; + *nentry = *entry; + nentry->start = end; + } + entry->end = start; + RB_UPDATE_AUGMENT(entry, rb_entry); + if (*r == NULL) + iommu_gas_rb_insert(domain, nentry); + nentry = iommu_gas_entries_tree_RB_NEXT(entry); + } else { + nentry = entry; + } + + RB_FOREACH_FROM(entry, iommu_gas_entries_tree, nentry) { + if (entry->end >= end) + break; + KASSERT(start <= entry->start, + ("iommu_gas_remove entry (%#jx, %#jx) start %#jx", + entry->start, entry->end, start)); + KASSERT(entry->end <= end, + ("iommu_gas_remove entry (%#jx, %#jx) end %#jx", + entry->start, entry->end, end)); + iommu_gas_remove_entry_locked(domain, entry); + } + + if (entry->start < end && (entry->flags & IOMMU_MAP_ENTRY_RMRR) == 0) { + entry->start = end; + RB_UPDATE_AUGMENT(entry, rb_entry); + } + +#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 +} + +void +iommu_gas_remove(struct iommu_domain *domain, iommu_gaddr_t start, + iommu_gaddr_t size) +{ + struct iommu_map_entry *r; + iommu_gaddr_t end; + + end = start + size; + r = iommu_gas_alloc_entry(domain, IOMMU_PGF_WAITOK); + IOMMU_DOMAIN_LOCK(domain); + iommu_gas_remove_locked(domain, start, end, &r); + IOMMU_DOMAIN_UNLOCK(domain); + if (r != NULL) + iommu_gas_free_entry(r); +} + int iommu_gas_map(struct iommu_domain *domain, const struct bus_dma_tag_common *common, iommu_gaddr_t size, int offset,