Index: sys/dev/iommu/iommu.h =================================================================== --- sys/dev/iommu/iommu.h +++ sys/dev/iommu/iommu.h @@ -109,6 +109,7 @@ struct iommu_map_entries_tailq unload_entries; /* (d) Entries to unload */ struct iommu_gas_entries_tree rb_root; /* (d) */ + struct iommu_map_entry *start_gap; /* (d) */ iommu_gaddr_t end; /* (c) Highest address + 1 in the guest AS */ struct iommu_map_entry *first_place, *last_place; /* (d) */ Index: sys/dev/iommu/iommu_gas.c =================================================================== --- sys/dev/iommu/iommu_gas.c +++ sys/dev/iommu/iommu_gas.c @@ -210,6 +210,12 @@ iommu_gas_rb_remove(struct iommu_domain *domain, struct iommu_map_entry *entry) { + /* + * The predecessor to the removed entry may become the new greatest + * lower bound on free gaps. + */ + if (entry->end <= domain->start_gap->end) + domain->start_gap = iommu_gas_entries_tree_RB_PREV(entry); RB_REMOVE(iommu_gas_entries_tree, &domain->rb_root, entry); } @@ -253,6 +259,7 @@ begin->flags = IOMMU_MAP_ENTRY_PLACE | IOMMU_MAP_ENTRY_UNMAPPED; RB_INSERT_PREV(iommu_gas_entries_tree, &domain->rb_root, end, begin); + domain->start_gap = begin; domain->first_place = begin; domain->last_place = end; domain->flags |= IOMMU_DOMAIN_GAS_INITED; @@ -369,6 +376,9 @@ entry->start = start; entry->end = start + roundup2(size + offset, IOMMU_PAGE_SIZE); entry->flags = IOMMU_MAP_ENTRY_MAP; + /* The new entry may be a new, greater lower bound on free gaps. */ + if (start <= a->domain->start_gap->end + 3 * IOMMU_PAGE_SIZE) + a->domain->start_gap = entry; return (true); } @@ -418,12 +428,9 @@ * range. */ domain = a->domain; - curr = RB_ROOT(&domain->rb_root); - first = NULL; - while (curr != NULL && curr->free_down >= min_free) { - first = curr; - curr = RB_LEFT(curr, rb_entry); - } + first = domain->start_gap; + while (first != NULL && first->free_down < min_free) + first = RB_PARENT(first, rb_entry); /* * Walk the big-enough ranges until one satisfies alignment