Index: sys/dev/iommu/busdma_iommu.c =================================================================== --- sys/dev/iommu/busdma_iommu.c +++ sys/dev/iommu/busdma_iommu.c @@ -594,8 +594,7 @@ if (seg + 1 < tag->common.nsegments) gas_flags |= IOMMU_MF_CANSPLIT; - error = iommu_map(domain, &tag->common, - round_page(offset + buflen1), + error = iommu_map(domain, &tag->common, buflen1, offset, e_flags, gas_flags, ma + idx, &entry); if (error != 0) break; Index: sys/dev/iommu/iommu_gas.c =================================================================== --- sys/dev/iommu/iommu_gas.c +++ sys/dev/iommu/iommu_gas.c @@ -301,60 +301,47 @@ iommu_gas_match_one(struct iommu_gas_match_args *a, iommu_gaddr_t beg, iommu_gaddr_t end, iommu_gaddr_t maxaddr) { - iommu_gaddr_t bs, start; - - a->entry->start = roundup2(beg + IOMMU_PAGE_SIZE, - a->common->alignment); - if (a->entry->start + a->offset + a->size > maxaddr) - return (false); - - /* IOMMU_PAGE_SIZE to create gap after new entry. */ - if (a->entry->start < beg + IOMMU_PAGE_SIZE || - a->entry->start + a->size + a->offset + IOMMU_PAGE_SIZE > end) - return (false); - - /* No boundary crossing. */ - if (vm_addr_bound_ok(a->entry->start + a->offset, a->size, - a->common->boundary)) - return (true); + iommu_gaddr_t first, size, start; + bool found __diagused; - /* - * The start + offset to start + offset + size region crosses - * the boundary. Check if there is enough space after the - * next boundary after the beg. - */ - bs = rounddown2(a->entry->start + a->offset + a->common->boundary, - a->common->boundary); - start = roundup2(bs, a->common->alignment); - /* IOMMU_PAGE_SIZE to create gap after new entry. */ - if (start + a->offset + a->size + IOMMU_PAGE_SIZE <= end && - start + a->offset + a->size <= maxaddr && - vm_addr_bound_ok(start + a->offset, a->size, - a->common->boundary)) { - a->entry->start = start; - return (true); + /* IOMMU_PAGE_SIZE to create gaps before, after new entry. */ + size = a->size; + end -= size + a->offset + IOMMU_PAGE_SIZE; + maxaddr -= size + a->offset; + end = ummin(end, maxaddr); + + beg += IOMMU_PAGE_SIZE; + first = start = roundup2(beg, a->common->alignment); + + /* Check for and try to skip past boundary crossing. */ + if (!vm_addr_bound_ok(start + a->offset, size, a->common->boundary)) { + /* + * The start + offset to start + offset + size region crosses + * the boundary. Check if there is enough space after the next + * boundary after the beg. + */ + beg = roundup2(start + a->offset + 1, a->common->boundary); + start = roundup2(beg, a->common->alignment); } - /* - * Not enough space to align at the requested boundary, or - * boundary is smaller than the size, but allowed to split. - * We already checked that start + size does not overlap maxaddr. - * - * XXXKIB. It is possible that bs is exactly at the start of - * the next entry, then we do not have gap. Ignore for now. - */ - if ((a->gas_flags & IOMMU_MF_CANSPLIT) != 0) { - a->size = bs - a->entry->start; - return (true); + /* Check for and try to shrink size to boundary crossing. */ + if (!vm_addr_bound_ok(start + a->offset, size, a->common->boundary) || + start > end) { + /* + * Not enough space to align at the requested boundary, or + * boundary is smaller than the size, but allowed to split. + * + * XXXKIB. It is possible that beg is exactly at the start of + * the next entry, then we do not have gap. Ignore for now. + */ + if ((a->gas_flags & IOMMU_MF_CANSPLIT) == 0) + return (false); + start = first; + size = beg - start; + } - - return (false); -} - -static void -iommu_gas_match_insert(struct iommu_gas_match_args *a) -{ - bool found __diagused; + if (start > end) + return (false); /* * The prev->end is always aligned on the page size, which @@ -364,12 +351,15 @@ * The page sized gap is created between consequent * allocations to ensure that out-of-bounds accesses fault. */ - a->entry->end = a->entry->start + a->size; + a->entry->start = start; + a->size = size; + a->entry->end = start + roundup2(size + a->offset, IOMMU_PAGE_SIZE); found = iommu_gas_rb_insert(a->domain, a->entry); KASSERT(found, ("found dup %p start %jx size %jx", - a->domain, (uintmax_t)a->entry->start, (uintmax_t)a->size)); + a->domain, (uintmax_t)start, (uintmax_t)size)); a->entry->flags = IOMMU_MAP_ENTRY_MAP; + return (true); } static int @@ -380,16 +370,15 @@ child = RB_RIGHT(entry, rb_entry); if (child != NULL && entry->end < a->common->lowaddr && iommu_gas_match_one(a, entry->end, child->first, - a->common->lowaddr)) { - iommu_gas_match_insert(a); + a->common->lowaddr)) return (0); - } /* * If the subtree doesn't have free space for the requested allocation * plus two guard pages, give up. */ - if (entry->free_down < a->size + a->offset + 2 * IOMMU_PAGE_SIZE) + if (entry->free_down < 2 * IOMMU_PAGE_SIZE + + roundup2(a->size + a->offset, IOMMU_PAGE_SIZE)) return (ENOMEM); if (entry->first >= a->common->lowaddr) return (ENOMEM); @@ -398,10 +387,8 @@ return (0); if (child != NULL && child->last < a->common->lowaddr && iommu_gas_match_one(a, child->last, entry->start, - a->common->lowaddr)) { - iommu_gas_match_insert(a); + a->common->lowaddr)) return (0); - } child = RB_RIGHT(entry, rb_entry); if (child != NULL && 0 == iommu_gas_lowermatch(a, child)) return (0); @@ -417,7 +404,8 @@ * If the subtree doesn't have free space for the requested allocation * plus two guard pages, give up. */ - if (entry->free_down < a->size + a->offset + 2 * IOMMU_PAGE_SIZE) + if (entry->free_down < 2 * IOMMU_PAGE_SIZE + + roundup2(a->size + a->offset, IOMMU_PAGE_SIZE)) return (ENOMEM); if (entry->last < a->common->highaddr) return (ENOMEM); @@ -426,17 +414,13 @@ return (0); if (child != NULL && child->last >= a->common->highaddr && iommu_gas_match_one(a, child->last, entry->start, - a->domain->end)) { - iommu_gas_match_insert(a); + a->domain->end)) return (0); - } child = RB_RIGHT(entry, rb_entry); if (child != NULL && entry->end >= a->common->highaddr && iommu_gas_match_one(a, entry->end, child->first, - a->domain->end)) { - iommu_gas_match_insert(a); + a->domain->end)) return (0); - } if (child != NULL && 0 == iommu_gas_uppermatch(a, child)) return (0); return (ENOMEM); @@ -452,7 +436,6 @@ IOMMU_DOMAIN_ASSERT_LOCKED(domain); KASSERT(entry->flags == 0, ("dirty entry %p %p", domain, entry)); - KASSERT((size & IOMMU_PAGE_MASK) == 0, ("size %jx", (uintmax_t)size)); a.domain = domain; a.size = size;