summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h3
-rw-r--r--drivers/gpu/drm/i915/i915_gem_evict.c104
-rw-r--r--drivers/gpu/drm/i915/i915_gem_execbuffer.c1
-rw-r--r--drivers/gpu/drm/i915/i915_trace.h28
-rw-r--r--drivers/gpu/drm/i915/i915_vma.c2
5 files changed, 111 insertions, 27 deletions
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index a0c6791..605247b 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -3341,7 +3341,8 @@ int __must_check i915_gem_evict_something(struct i915_address_space *vm,
unsigned cache_level,
u64 start, u64 end,
unsigned flags);
-int __must_check i915_gem_evict_for_vma(struct i915_vma *target);
+int __must_check i915_gem_evict_for_vma(struct i915_vma *vma,
+ unsigned int flags);
int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle);
/* belongs in i915_gem_gtt.h */
diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c
index 739ede7..fa40100 100644
--- a/drivers/gpu/drm/i915/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/i915_gem_evict.c
@@ -212,45 +212,99 @@ found:
return ret;
}
-int
-i915_gem_evict_for_vma(struct i915_vma *target)
+/**
+ * i915_gem_evict_for_vma - Evict vmas to make room for binding a new one
+ * @target: address space and range to evict for
+ * @flags: additional flags to control the eviction algorithm
+ *
+ * This function will try to evict vmas that overlap the target node.
+ *
+ * To clarify: This is for freeing up virtual address space, not for freeing
+ * memory in e.g. the shrinker.
+ */
+int i915_gem_evict_for_vma(struct i915_vma *target, unsigned int flags)
{
- struct drm_mm_node *node, *next;
+ LIST_HEAD(eviction_list);
+ struct drm_mm_node *node;
+ u64 start = target->node.start;
+ u64 end = start + target->node.size;
+ struct i915_vma *vma, *next;
+ bool check_color;
+ int ret = 0;
lockdep_assert_held(&target->vm->i915->drm.struct_mutex);
+ trace_i915_gem_evict_vma(target, flags);
+
+ check_color = target->vm->mm.color_adjust;
+ if (check_color) {
+ /* Expand search to cover neighbouring guard pages (or lack!) */
+ if (start > target->vm->start)
+ start -= 4096;
+ if (end < target->vm->start + target->vm->total)
+ end += 4096;
+ }
- list_for_each_entry_safe(node, next,
- &target->vm->mm.head_node.node_list,
- node_list) {
- struct i915_vma *vma;
- int ret;
-
- if (node->start + node->size <= target->node.start)
- continue;
- if (node->start >= target->node.start + target->node.size)
+ drm_mm_for_each_node_in_range(node, &target->vm->mm, start, end) {
+ /* If we find any non-objects (!vma), we cannot evict them */
+ if (node->color == I915_COLOR_UNEVICTABLE) {
+ ret = -ENOSPC;
break;
+ }
vma = container_of(node, typeof(*vma), node);
- if (i915_vma_is_pinned(vma)) {
- if (!vma->exec_entry || i915_vma_pin_count(vma) > 1)
- /* Object is pinned for some other use */
- return -EBUSY;
+ /* If we are using coloring to insert guard pages between
+ * different cache domains within the address space, we have
+ * to check whether the objects on either side of our range
+ * abutt and conflict. If they are in conflict, then we evict
+ * those as well to make room for our guard pages.
+ */
+ if (check_color) {
+ if (vma->node.start + vma->node.size == target->node.start) {
+ if (vma->node.color == target->node.color)
+ continue;
+ }
+ if (vma->node.start == target->node.start + target->node.size) {
+ if (vma->node.color == target->node.color)
+ continue;
+ }
+ }
- /* We need to evict a buffer in the same batch */
- if (vma->exec_entry->flags & EXEC_OBJECT_PINNED)
- /* Overlapping fixed objects in the same batch */
- return -EINVAL;
+ if (flags & PIN_NONBLOCK &&
+ (i915_vma_is_pinned(vma) || i915_vma_is_active(vma))) {
+ ret = -ENOSPC;
+ break;
+ }
- return -ENOSPC;
+ /* Overlap of objects in the same batch? */
+ if (i915_vma_is_pinned(vma)) {
+ ret = -ENOSPC;
+ if (vma->exec_entry &&
+ vma->exec_entry->flags & EXEC_OBJECT_PINNED)
+ ret = -EINVAL;
+ break;
}
- ret = i915_vma_unbind(vma);
- if (ret)
- return ret;
+ /* Never show fear in the face of dragons!
+ *
+ * We cannot directly remove this node from within this
+ * iterator and as with i915_gem_evict_something() we employ
+ * the vma pin_count in order to prevent the action of
+ * unbinding one vma from freeing (by dropping its active
+ * reference) another in our eviction list.
+ */
+ __i915_vma_pin(vma);
+ list_add(&vma->exec_list, &eviction_list);
}
- return 0;
+ list_for_each_entry_safe(vma, next, &eviction_list, exec_list) {
+ list_del_init(&vma->exec_list);
+ __i915_vma_unpin(vma);
+ if (ret == 0)
+ ret = i915_vma_unbind(vma);
+ }
+
+ return ret;
}
/**
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 5f164ad..d665a33 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -274,6 +274,7 @@ static void eb_destroy(struct eb_vmas *eb)
exec_list);
list_del_init(&vma->exec_list);
i915_gem_execbuffer_unreserve_vma(vma);
+ vma->exec_entry = NULL;
i915_vma_put(vma);
}
kfree(eb);
diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h
index 2400677..18ae37c 100644
--- a/drivers/gpu/drm/i915/i915_trace.h
+++ b/drivers/gpu/drm/i915/i915_trace.h
@@ -450,6 +450,34 @@ TRACE_EVENT(i915_gem_evict_vm,
TP_printk("dev=%d, vm=%p", __entry->dev, __entry->vm)
);
+TRACE_EVENT(i915_gem_evict_vma,
+ TP_PROTO(struct i915_vma *vma, unsigned int flags),
+ TP_ARGS(vma, flags),
+
+ TP_STRUCT__entry(
+ __field(u32, dev)
+ __field(struct i915_address_space *, vm)
+ __field(u64, start)
+ __field(u64, size)
+ __field(unsigned long, color)
+ __field(unsigned int, flags)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = vma->vm->i915->drm.primary->index;
+ __entry->vm = vma->vm;
+ __entry->start = vma->node.start;
+ __entry->size = vma->node.size;
+ __entry->color = vma->node.color;
+ __entry->flags = flags;
+ ),
+
+ TP_printk("dev=%d, vm=%p, start=%llx size=%llx, color=%lx, flags=%x",
+ __entry->dev, __entry->vm,
+ __entry->start, __entry->size,
+ __entry->color, __entry->flags)
+);
+
TRACE_EVENT(i915_gem_ring_sync_to,
TP_PROTO(struct drm_i915_gem_request *to,
struct drm_i915_gem_request *from),
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index 4c91a68..bc077e5 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -401,7 +401,7 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
vma->node.color = obj->cache_level;
ret = drm_mm_reserve_node(&vma->vm->mm, &vma->node);
if (ret) {
- ret = i915_gem_evict_for_vma(vma);
+ ret = i915_gem_evict_for_vma(vma, flags);
if (ret == 0)
ret = drm_mm_reserve_node(&vma->vm->mm, &vma->node);
if (ret)
OpenPOWER on IntegriCloud