Skip to content

Commit

Permalink
drm: Apply tight eviction scanning to color_adjust
Browse files Browse the repository at this point in the history
Using mm->color_adjust makes the eviction scanner much tricker since we
don't know the actual neighbours of the target hole until after it is
created (after scanning is complete). To work out whether we need to
evict the neighbours because they impact upon the hole, we have to then
check the hole afterwards - requiring an extra step in the user of the
eviction scanner when they apply color_adjust.

v2: Massage kerneldoc.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/20161222083641.2691-34-chris@chris-wilson.co.uk
  • Loading branch information
ickle authored and danvet committed Dec 28, 2016
1 parent f29051f commit 3fa489d
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 27 deletions.
76 changes: 51 additions & 25 deletions drivers/gpu/drm/drm_mm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/

/**
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions drivers/gpu/drm/i915/i915_gem_evict.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -211,6 +212,12 @@ i915_gem_evict_something(struct i915_address_space *vm,
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;
}

Expand Down
20 changes: 18 additions & 2 deletions drivers/gpu/drm/selftests/test-drm_mm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions include/drm/drm_mm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 3fa489d

Please sign in to comment.