diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index ccca8dafb7fc..b59978fe4c6e 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -692,19 +692,21 @@ EXPORT_SYMBOL(drm_mm_replace_node); * The DRM range allocator supports this use-case through the scanning * interfaces. First a scan operation needs to be initialized with * drm_mm_scan_init() or drm_mm_scan_init_with_range(). The driver adds - * objects to the roaster (probably by walking an LRU list, but this can be - * freely implemented) until a suitable hole is found or there's no further - * evictable object. + * objects to the roster (probably by walking an LRU list, but this can be + * freely implemented) (using drm_mm_scan_add_block()) until a suitable hole + * is found or there are no further evictable objects. * * The driver must walk through all objects again in exactly the reverse * order to restore the allocator state. Note that while the allocator is used * in the scan mode no other operation is allowed. * - * Finally the driver evicts all objects selected in the scan. Adding and - * removing an object is O(1), and since freeing a node is also O(1) the overall - * complexity is O(scanned_objects). So like the free stack which needs to be - * walked before a scan operation even begins this is linear in the number of - * objects. It doesn't seem to hurt badly. + * Finally the driver evicts all objects selected (drm_mm_scan_remove_block() + * reported true) in the scan, and any overlapping nodes after color adjustment + * (drm_mm_scan_evict_color()). Adding and removing an object is O(1), and + * since freeing a node is also O(1) the overall complexity is + * O(scanned_objects). So like the free stack which needs to be walked before a + * scan operation even begins this is linear in the number of objects. It + * doesn't seem to hurt too badly. */ /** @@ -829,23 +831,8 @@ bool drm_mm_scan_add_block(struct drm_mm_scan *scan, } } - if (mm->color_adjust) { - /* If allocations need adjusting due to neighbouring colours, - * we do not have enough information to decide if we need - * to evict nodes on either side of [adj_start, adj_end]. - * What almost works is - * hit_start = adj_start + (hole_start - col_start); - * hit_end = adj_start + scan->size + (hole_end - col_end); - * but because the decision is only made on the final hole, - * we may underestimate the required adjustments for an - * interior allocation. - */ - scan->hit_start = hole_start; - scan->hit_end = hole_end; - } else { - scan->hit_start = adj_start; - scan->hit_end = adj_start + scan->size; - } + scan->hit_start = adj_start; + scan->hit_end = adj_start + scan->size; DRM_MM_BUG_ON(scan->hit_start >= scan->hit_end); DRM_MM_BUG_ON(scan->hit_start < hole_start); @@ -902,6 +889,45 @@ bool drm_mm_scan_remove_block(struct drm_mm_scan *scan, } EXPORT_SYMBOL(drm_mm_scan_remove_block); +/** + * drm_mm_scan_color_evict - evict overlapping nodes on either side of hole + * @scan: drm_mm scan with target hole + * + * After completing an eviction scan and removing the selected nodes, we may + * need to remove a few more nodes from either side of the target hole if + * mm.color_adjust is being used. + * + * Returns: + * A node to evict, or NULL if there are no overlapping nodes. + */ +struct drm_mm_node *drm_mm_scan_color_evict(struct drm_mm_scan *scan) +{ + struct drm_mm *mm = scan->mm; + struct drm_mm_node *hole; + u64 hole_start, hole_end; + + DRM_MM_BUG_ON(list_empty(&mm->hole_stack)); + + if (!mm->color_adjust) + return NULL; + + hole = list_first_entry(&mm->hole_stack, typeof(*hole), hole_stack); + hole_start = __drm_mm_hole_node_start(hole); + hole_end = __drm_mm_hole_node_end(hole); + + DRM_MM_BUG_ON(hole_start > scan->hit_start); + DRM_MM_BUG_ON(hole_end < scan->hit_end); + + mm->color_adjust(hole, scan->color, &hole_start, &hole_end); + if (hole_start > scan->hit_start) + return hole; + if (hole_end < scan->hit_end) + return list_next_entry(hole, node_list); + + return NULL; +} +EXPORT_SYMBOL(drm_mm_scan_color_evict); + /** * drm_mm_init - initialize a drm-mm allocator * @mm: the drm_mm structure to initialize diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index fa90a0c47976..85ceff1b74b6 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -108,6 +108,7 @@ i915_gem_evict_something(struct i915_address_space *vm, NULL, }, **phase; struct i915_vma *vma, *next; + struct drm_mm_node *node; int ret; lockdep_assert_held(&vm->dev->struct_mutex); @@ -211,6 +212,12 @@ found: if (ret == 0) ret = i915_vma_unbind(vma); } + + while (ret == 0 && (node = drm_mm_scan_color_evict(&scan))) { + vma = container_of(node, struct i915_vma, node); + ret = i915_vma_unbind(vma); + } + return ret; } diff --git a/drivers/gpu/drm/selftests/test-drm_mm.c b/drivers/gpu/drm/selftests/test-drm_mm.c index 1bbfc24342c5..f609e4f1eeaf 100644 --- a/drivers/gpu/drm/selftests/test-drm_mm.c +++ b/drivers/gpu/drm/selftests/test-drm_mm.c @@ -1162,6 +1162,7 @@ static bool evict_nodes(struct drm_mm_scan *scan, struct evict_node *nodes, unsigned int *order, unsigned int count, + bool use_color, struct list_head *evict_list) { struct evict_node *e, *en; @@ -1186,6 +1187,21 @@ static bool evict_nodes(struct drm_mm_scan *scan, list_for_each_entry(e, evict_list, link) drm_mm_remove_node(&e->node); + if (use_color) { + struct drm_mm_node *node; + + while ((node = drm_mm_scan_color_evict(scan))) { + e = container_of(node, typeof(*e), node); + drm_mm_remove_node(&e->node); + list_add(&e->link, evict_list); + } + } else { + if (drm_mm_scan_color_evict(scan)) { + pr_err("drm_mm_scan_color_evict unexpectedly reported overlapping nodes!\n"); + return false; + } + } + return true; } @@ -1299,7 +1315,7 @@ static int evict_something(struct drm_mm *mm, range_start, range_end, mode->create_flags); if (!evict_nodes(&scan, - nodes, order, count, + nodes, order, count, false, &evict_list)) return -EINVAL; @@ -1878,7 +1894,7 @@ static int evict_color(struct drm_mm *mm, range_start, range_end, mode->create_flags); if (!evict_nodes(&scan, - nodes, order, count, + nodes, order, count, true, &evict_list)) return -EINVAL; diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index ff120b7d0f85..aed93cbc4bde 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -422,6 +422,7 @@ bool drm_mm_scan_add_block(struct drm_mm_scan *scan, struct drm_mm_node *node); bool drm_mm_scan_remove_block(struct drm_mm_scan *scan, struct drm_mm_node *node); +struct drm_mm_node *drm_mm_scan_color_evict(struct drm_mm_scan *scan); void drm_mm_debug_table(const struct drm_mm *mm, const char *prefix); #ifdef CONFIG_DEBUG_FS