|
@@ -38,11 +38,14 @@
|
|
|
#include <linux/pci.h>
|
|
|
#include <linux/dma-buf.h>
|
|
|
|
|
|
+#define RQ_BUG_ON(expr)
|
|
|
+
|
|
|
static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj);
|
|
|
static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj);
|
|
|
static void
|
|
|
-i915_gem_object_retire(struct drm_i915_gem_object *obj);
|
|
|
-
|
|
|
+i915_gem_object_retire__write(struct drm_i915_gem_object *obj);
|
|
|
+static void
|
|
|
+i915_gem_object_retire__read(struct drm_i915_gem_object *obj, int ring);
|
|
|
static void i915_gem_write_fence(struct drm_device *dev, int reg,
|
|
|
struct drm_i915_gem_object *obj);
|
|
|
static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj,
|
|
@@ -515,8 +518,6 @@ int i915_gem_obj_prepare_shmem_read(struct drm_i915_gem_object *obj,
|
|
|
ret = i915_gem_object_wait_rendering(obj, true);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
-
|
|
|
- i915_gem_object_retire(obj);
|
|
|
}
|
|
|
|
|
|
ret = i915_gem_object_get_pages(obj);
|
|
@@ -936,8 +937,6 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
|
|
|
ret = i915_gem_object_wait_rendering(obj, false);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
-
|
|
|
- i915_gem_object_retire(obj);
|
|
|
}
|
|
|
/* Same trick applies to invalidate partially written cachelines read
|
|
|
* before writing. */
|
|
@@ -1236,6 +1235,9 @@ int __i915_wait_request(struct drm_i915_gem_request *req,
|
|
|
|
|
|
WARN(!intel_irqs_enabled(dev_priv), "IRQs disabled");
|
|
|
|
|
|
+ if (list_empty(&req->list))
|
|
|
+ return 0;
|
|
|
+
|
|
|
if (i915_gem_request_completed(req, true))
|
|
|
return 0;
|
|
|
|
|
@@ -1335,6 +1337,63 @@ out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static inline void
|
|
|
+i915_gem_request_remove_from_client(struct drm_i915_gem_request *request)
|
|
|
+{
|
|
|
+ struct drm_i915_file_private *file_priv = request->file_priv;
|
|
|
+
|
|
|
+ if (!file_priv)
|
|
|
+ return;
|
|
|
+
|
|
|
+ spin_lock(&file_priv->mm.lock);
|
|
|
+ list_del(&request->client_list);
|
|
|
+ request->file_priv = NULL;
|
|
|
+ spin_unlock(&file_priv->mm.lock);
|
|
|
+}
|
|
|
+
|
|
|
+static void i915_gem_request_retire(struct drm_i915_gem_request *request)
|
|
|
+{
|
|
|
+ trace_i915_gem_request_retire(request);
|
|
|
+
|
|
|
+ /* We know the GPU must have read the request to have
|
|
|
+ * sent us the seqno + interrupt, so use the position
|
|
|
+ * of tail of the request to update the last known position
|
|
|
+ * of the GPU head.
|
|
|
+ *
|
|
|
+ * Note this requires that we are always called in request
|
|
|
+ * completion order.
|
|
|
+ */
|
|
|
+ request->ringbuf->last_retired_head = request->postfix;
|
|
|
+
|
|
|
+ list_del_init(&request->list);
|
|
|
+ i915_gem_request_remove_from_client(request);
|
|
|
+
|
|
|
+ put_pid(request->pid);
|
|
|
+
|
|
|
+ i915_gem_request_unreference(request);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+__i915_gem_request_retire__upto(struct drm_i915_gem_request *req)
|
|
|
+{
|
|
|
+ struct intel_engine_cs *engine = req->ring;
|
|
|
+ struct drm_i915_gem_request *tmp;
|
|
|
+
|
|
|
+ lockdep_assert_held(&engine->dev->struct_mutex);
|
|
|
+
|
|
|
+ if (list_empty(&req->list))
|
|
|
+ return;
|
|
|
+
|
|
|
+ do {
|
|
|
+ tmp = list_first_entry(&engine->request_list,
|
|
|
+ typeof(*tmp), list);
|
|
|
+
|
|
|
+ i915_gem_request_retire(tmp);
|
|
|
+ } while (tmp != req);
|
|
|
+
|
|
|
+ WARN_ON(i915_verify_lists(engine->dev));
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* Waits for a request to be signaled, and cleans up the
|
|
|
* request and object lists appropriately for that event.
|
|
@@ -1345,7 +1404,6 @@ i915_wait_request(struct drm_i915_gem_request *req)
|
|
|
struct drm_device *dev;
|
|
|
struct drm_i915_private *dev_priv;
|
|
|
bool interruptible;
|
|
|
- unsigned reset_counter;
|
|
|
int ret;
|
|
|
|
|
|
BUG_ON(req == NULL);
|
|
@@ -1364,29 +1422,13 @@ i915_wait_request(struct drm_i915_gem_request *req)
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
- reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
|
|
|
- i915_gem_request_reference(req);
|
|
|
- ret = __i915_wait_request(req, reset_counter,
|
|
|
+ ret = __i915_wait_request(req,
|
|
|
+ atomic_read(&dev_priv->gpu_error.reset_counter),
|
|
|
interruptible, NULL, NULL);
|
|
|
- i915_gem_request_unreference(req);
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-static int
|
|
|
-i915_gem_object_wait_rendering__tail(struct drm_i915_gem_object *obj)
|
|
|
-{
|
|
|
- if (!obj->active)
|
|
|
- return 0;
|
|
|
-
|
|
|
- /* Manually manage the write flush as we may have not yet
|
|
|
- * retired the buffer.
|
|
|
- *
|
|
|
- * Note that the last_write_req is always the earlier of
|
|
|
- * the two (read/write) requests, so if we haved successfully waited,
|
|
|
- * we know we have passed the last write.
|
|
|
- */
|
|
|
- i915_gem_request_assign(&obj->last_write_req, NULL);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
|
|
|
+ __i915_gem_request_retire__upto(req);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1398,18 +1440,52 @@ int
|
|
|
i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
|
|
|
bool readonly)
|
|
|
{
|
|
|
- struct drm_i915_gem_request *req;
|
|
|
- int ret;
|
|
|
+ int ret, i;
|
|
|
|
|
|
- req = readonly ? obj->last_write_req : obj->last_read_req;
|
|
|
- if (!req)
|
|
|
+ if (!obj->active)
|
|
|
return 0;
|
|
|
|
|
|
- ret = i915_wait_request(req);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
+ if (readonly) {
|
|
|
+ if (obj->last_write_req != NULL) {
|
|
|
+ ret = i915_wait_request(obj->last_write_req);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ i = obj->last_write_req->ring->id;
|
|
|
+ if (obj->last_read_req[i] == obj->last_write_req)
|
|
|
+ i915_gem_object_retire__read(obj, i);
|
|
|
+ else
|
|
|
+ i915_gem_object_retire__write(obj);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for (i = 0; i < I915_NUM_RINGS; i++) {
|
|
|
+ if (obj->last_read_req[i] == NULL)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ ret = i915_wait_request(obj->last_read_req[i]);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ i915_gem_object_retire__read(obj, i);
|
|
|
+ }
|
|
|
+ RQ_BUG_ON(obj->active);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+i915_gem_object_retire_request(struct drm_i915_gem_object *obj,
|
|
|
+ struct drm_i915_gem_request *req)
|
|
|
+{
|
|
|
+ int ring = req->ring->id;
|
|
|
+
|
|
|
+ if (obj->last_read_req[ring] == req)
|
|
|
+ i915_gem_object_retire__read(obj, ring);
|
|
|
+ else if (obj->last_write_req == req)
|
|
|
+ i915_gem_object_retire__write(obj);
|
|
|
|
|
|
- return i915_gem_object_wait_rendering__tail(obj);
|
|
|
+ __i915_gem_request_retire__upto(req);
|
|
|
}
|
|
|
|
|
|
/* A nonblocking variant of the above wait. This is a highly dangerous routine
|
|
@@ -1420,37 +1496,66 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj,
|
|
|
struct drm_i915_file_private *file_priv,
|
|
|
bool readonly)
|
|
|
{
|
|
|
- struct drm_i915_gem_request *req;
|
|
|
struct drm_device *dev = obj->base.dev;
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ struct drm_i915_gem_request *requests[I915_NUM_RINGS];
|
|
|
unsigned reset_counter;
|
|
|
- int ret;
|
|
|
+ int ret, i, n = 0;
|
|
|
|
|
|
BUG_ON(!mutex_is_locked(&dev->struct_mutex));
|
|
|
BUG_ON(!dev_priv->mm.interruptible);
|
|
|
|
|
|
- req = readonly ? obj->last_write_req : obj->last_read_req;
|
|
|
- if (!req)
|
|
|
+ if (!obj->active)
|
|
|
return 0;
|
|
|
|
|
|
ret = i915_gem_check_wedge(&dev_priv->gpu_error, true);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
- ret = i915_gem_check_olr(req);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
-
|
|
|
reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
|
|
|
- i915_gem_request_reference(req);
|
|
|
+
|
|
|
+ if (readonly) {
|
|
|
+ struct drm_i915_gem_request *req;
|
|
|
+
|
|
|
+ req = obj->last_write_req;
|
|
|
+ if (req == NULL)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ ret = i915_gem_check_olr(req);
|
|
|
+ if (ret)
|
|
|
+ goto err;
|
|
|
+
|
|
|
+ requests[n++] = i915_gem_request_reference(req);
|
|
|
+ } else {
|
|
|
+ for (i = 0; i < I915_NUM_RINGS; i++) {
|
|
|
+ struct drm_i915_gem_request *req;
|
|
|
+
|
|
|
+ req = obj->last_read_req[i];
|
|
|
+ if (req == NULL)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ ret = i915_gem_check_olr(req);
|
|
|
+ if (ret)
|
|
|
+ goto err;
|
|
|
+
|
|
|
+ requests[n++] = i915_gem_request_reference(req);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
- ret = __i915_wait_request(req, reset_counter, true, NULL, file_priv);
|
|
|
+ for (i = 0; ret == 0 && i < n; i++)
|
|
|
+ ret = __i915_wait_request(requests[i], reset_counter, true,
|
|
|
+ NULL, file_priv);
|
|
|
mutex_lock(&dev->struct_mutex);
|
|
|
- i915_gem_request_unreference(req);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
|
|
|
- return i915_gem_object_wait_rendering__tail(obj);
|
|
|
+err:
|
|
|
+ for (i = 0; i < n; i++) {
|
|
|
+ if (ret == 0)
|
|
|
+ i915_gem_object_retire_request(obj, requests[i]);
|
|
|
+ i915_gem_request_unreference(requests[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -2231,78 +2336,58 @@ i915_gem_object_get_pages(struct drm_i915_gem_object *obj)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void
|
|
|
-i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
|
|
|
- struct intel_engine_cs *ring)
|
|
|
+void i915_vma_move_to_active(struct i915_vma *vma,
|
|
|
+ struct intel_engine_cs *ring)
|
|
|
{
|
|
|
- struct drm_i915_gem_request *req;
|
|
|
- struct intel_engine_cs *old_ring;
|
|
|
-
|
|
|
- BUG_ON(ring == NULL);
|
|
|
-
|
|
|
- req = intel_ring_get_request(ring);
|
|
|
- old_ring = i915_gem_request_get_ring(obj->last_read_req);
|
|
|
-
|
|
|
- if (old_ring != ring && obj->last_write_req) {
|
|
|
- /* Keep the request relative to the current ring */
|
|
|
- i915_gem_request_assign(&obj->last_write_req, req);
|
|
|
- }
|
|
|
+ struct drm_i915_gem_object *obj = vma->obj;
|
|
|
|
|
|
/* Add a reference if we're newly entering the active list. */
|
|
|
- if (!obj->active) {
|
|
|
+ if (obj->active == 0)
|
|
|
drm_gem_object_reference(&obj->base);
|
|
|
- obj->active = 1;
|
|
|
- }
|
|
|
+ obj->active |= intel_ring_flag(ring);
|
|
|
|
|
|
- list_move_tail(&obj->ring_list, &ring->active_list);
|
|
|
+ list_move_tail(&obj->ring_list[ring->id], &ring->active_list);
|
|
|
+ i915_gem_request_assign(&obj->last_read_req[ring->id],
|
|
|
+ intel_ring_get_request(ring));
|
|
|
|
|
|
- i915_gem_request_assign(&obj->last_read_req, req);
|
|
|
+ list_move_tail(&vma->mm_list, &vma->vm->active_list);
|
|
|
}
|
|
|
|
|
|
-void i915_vma_move_to_active(struct i915_vma *vma,
|
|
|
- struct intel_engine_cs *ring)
|
|
|
+static void
|
|
|
+i915_gem_object_retire__write(struct drm_i915_gem_object *obj)
|
|
|
{
|
|
|
- list_move_tail(&vma->mm_list, &vma->vm->active_list);
|
|
|
- return i915_gem_object_move_to_active(vma->obj, ring);
|
|
|
+ RQ_BUG_ON(obj->last_write_req == NULL);
|
|
|
+ RQ_BUG_ON(!(obj->active & intel_ring_flag(obj->last_write_req->ring)));
|
|
|
+
|
|
|
+ i915_gem_request_assign(&obj->last_write_req, NULL);
|
|
|
+ intel_fb_obj_flush(obj, true);
|
|
|
}
|
|
|
|
|
|
static void
|
|
|
-i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj)
|
|
|
+i915_gem_object_retire__read(struct drm_i915_gem_object *obj, int ring)
|
|
|
{
|
|
|
struct i915_vma *vma;
|
|
|
|
|
|
- BUG_ON(obj->base.write_domain & ~I915_GEM_GPU_DOMAINS);
|
|
|
- BUG_ON(!obj->active);
|
|
|
+ RQ_BUG_ON(obj->last_read_req[ring] == NULL);
|
|
|
+ RQ_BUG_ON(!(obj->active & (1 << ring)));
|
|
|
+
|
|
|
+ list_del_init(&obj->ring_list[ring]);
|
|
|
+ i915_gem_request_assign(&obj->last_read_req[ring], NULL);
|
|
|
+
|
|
|
+ if (obj->last_write_req && obj->last_write_req->ring->id == ring)
|
|
|
+ i915_gem_object_retire__write(obj);
|
|
|
+
|
|
|
+ obj->active &= ~(1 << ring);
|
|
|
+ if (obj->active)
|
|
|
+ return;
|
|
|
|
|
|
list_for_each_entry(vma, &obj->vma_list, vma_link) {
|
|
|
if (!list_empty(&vma->mm_list))
|
|
|
list_move_tail(&vma->mm_list, &vma->vm->inactive_list);
|
|
|
}
|
|
|
|
|
|
- intel_fb_obj_flush(obj, true);
|
|
|
-
|
|
|
- list_del_init(&obj->ring_list);
|
|
|
-
|
|
|
- i915_gem_request_assign(&obj->last_read_req, NULL);
|
|
|
- i915_gem_request_assign(&obj->last_write_req, NULL);
|
|
|
- obj->base.write_domain = 0;
|
|
|
-
|
|
|
i915_gem_request_assign(&obj->last_fenced_req, NULL);
|
|
|
-
|
|
|
- obj->active = 0;
|
|
|
drm_gem_object_unreference(&obj->base);
|
|
|
-
|
|
|
- WARN_ON(i915_verify_lists(dev));
|
|
|
-}
|
|
|
-
|
|
|
-static void
|
|
|
-i915_gem_object_retire(struct drm_i915_gem_object *obj)
|
|
|
-{
|
|
|
- if (obj->last_read_req == NULL)
|
|
|
- return;
|
|
|
-
|
|
|
- if (i915_gem_request_completed(obj->last_read_req, true))
|
|
|
- i915_gem_object_move_to_inactive(obj);
|
|
|
}
|
|
|
|
|
|
static int
|
|
@@ -2479,20 +2564,6 @@ int __i915_add_request(struct intel_engine_cs *ring,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static inline void
|
|
|
-i915_gem_request_remove_from_client(struct drm_i915_gem_request *request)
|
|
|
-{
|
|
|
- struct drm_i915_file_private *file_priv = request->file_priv;
|
|
|
-
|
|
|
- if (!file_priv)
|
|
|
- return;
|
|
|
-
|
|
|
- spin_lock(&file_priv->mm.lock);
|
|
|
- list_del(&request->client_list);
|
|
|
- request->file_priv = NULL;
|
|
|
- spin_unlock(&file_priv->mm.lock);
|
|
|
-}
|
|
|
-
|
|
|
static bool i915_context_is_banned(struct drm_i915_private *dev_priv,
|
|
|
const struct intel_context *ctx)
|
|
|
{
|
|
@@ -2538,16 +2609,6 @@ static void i915_set_reset_status(struct drm_i915_private *dev_priv,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void i915_gem_free_request(struct drm_i915_gem_request *request)
|
|
|
-{
|
|
|
- list_del(&request->list);
|
|
|
- i915_gem_request_remove_from_client(request);
|
|
|
-
|
|
|
- put_pid(request->pid);
|
|
|
-
|
|
|
- i915_gem_request_unreference(request);
|
|
|
-}
|
|
|
-
|
|
|
void i915_gem_request_free(struct kref *req_ref)
|
|
|
{
|
|
|
struct drm_i915_gem_request *req = container_of(req_ref,
|
|
@@ -2648,9 +2709,9 @@ static void i915_gem_reset_ring_cleanup(struct drm_i915_private *dev_priv,
|
|
|
|
|
|
obj = list_first_entry(&ring->active_list,
|
|
|
struct drm_i915_gem_object,
|
|
|
- ring_list);
|
|
|
+ ring_list[ring->id]);
|
|
|
|
|
|
- i915_gem_object_move_to_inactive(obj);
|
|
|
+ i915_gem_object_retire__read(obj, ring->id);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -2686,7 +2747,7 @@ static void i915_gem_reset_ring_cleanup(struct drm_i915_private *dev_priv,
|
|
|
struct drm_i915_gem_request,
|
|
|
list);
|
|
|
|
|
|
- i915_gem_free_request(request);
|
|
|
+ i915_gem_request_retire(request);
|
|
|
}
|
|
|
|
|
|
/* This may not have been flushed before the reset, so clean it now */
|
|
@@ -2734,6 +2795,8 @@ void i915_gem_reset(struct drm_device *dev)
|
|
|
i915_gem_context_reset(dev);
|
|
|
|
|
|
i915_gem_restore_fences(dev);
|
|
|
+
|
|
|
+ WARN_ON(i915_verify_lists(dev));
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -2742,11 +2805,11 @@ void i915_gem_reset(struct drm_device *dev)
|
|
|
void
|
|
|
i915_gem_retire_requests_ring(struct intel_engine_cs *ring)
|
|
|
{
|
|
|
- if (list_empty(&ring->request_list))
|
|
|
- return;
|
|
|
-
|
|
|
WARN_ON(i915_verify_lists(ring->dev));
|
|
|
|
|
|
+ if (list_empty(&ring->active_list))
|
|
|
+ return;
|
|
|
+
|
|
|
/* Retire requests first as we use it above for the early return.
|
|
|
* If we retire requests last, we may use a later seqno and so clear
|
|
|
* the requests lists without clearing the active list, leading to
|
|
@@ -2762,16 +2825,7 @@ i915_gem_retire_requests_ring(struct intel_engine_cs *ring)
|
|
|
if (!i915_gem_request_completed(request, true))
|
|
|
break;
|
|
|
|
|
|
- trace_i915_gem_request_retire(request);
|
|
|
-
|
|
|
- /* We know the GPU must have read the request to have
|
|
|
- * sent us the seqno + interrupt, so use the position
|
|
|
- * of tail of the request to update the last known position
|
|
|
- * of the GPU head.
|
|
|
- */
|
|
|
- request->ringbuf->last_retired_head = request->postfix;
|
|
|
-
|
|
|
- i915_gem_free_request(request);
|
|
|
+ i915_gem_request_retire(request);
|
|
|
}
|
|
|
|
|
|
/* Move any buffers on the active list that are no longer referenced
|
|
@@ -2783,12 +2837,12 @@ i915_gem_retire_requests_ring(struct intel_engine_cs *ring)
|
|
|
|
|
|
obj = list_first_entry(&ring->active_list,
|
|
|
struct drm_i915_gem_object,
|
|
|
- ring_list);
|
|
|
+ ring_list[ring->id]);
|
|
|
|
|
|
- if (!i915_gem_request_completed(obj->last_read_req, true))
|
|
|
+ if (!list_empty(&obj->last_read_req[ring->id]->list))
|
|
|
break;
|
|
|
|
|
|
- i915_gem_object_move_to_inactive(obj);
|
|
|
+ i915_gem_object_retire__read(obj, ring->id);
|
|
|
}
|
|
|
|
|
|
if (unlikely(ring->trace_irq_req &&
|
|
@@ -2883,17 +2937,30 @@ i915_gem_idle_work_handler(struct work_struct *work)
|
|
|
static int
|
|
|
i915_gem_object_flush_active(struct drm_i915_gem_object *obj)
|
|
|
{
|
|
|
- struct intel_engine_cs *ring;
|
|
|
- int ret;
|
|
|
+ int ret, i;
|
|
|
+
|
|
|
+ if (!obj->active)
|
|
|
+ return 0;
|
|
|
|
|
|
- if (obj->active) {
|
|
|
- ring = i915_gem_request_get_ring(obj->last_read_req);
|
|
|
+ for (i = 0; i < I915_NUM_RINGS; i++) {
|
|
|
+ struct drm_i915_gem_request *req;
|
|
|
|
|
|
- ret = i915_gem_check_olr(obj->last_read_req);
|
|
|
+ req = obj->last_read_req[i];
|
|
|
+ if (req == NULL)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (list_empty(&req->list))
|
|
|
+ goto retire;
|
|
|
+
|
|
|
+ ret = i915_gem_check_olr(req);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
- i915_gem_retire_requests_ring(ring);
|
|
|
+ if (i915_gem_request_completed(req, true)) {
|
|
|
+ __i915_gem_request_retire__upto(req);
|
|
|
+retire:
|
|
|
+ i915_gem_object_retire__read(obj, i);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
@@ -2927,9 +2994,10 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
struct drm_i915_gem_wait *args = data;
|
|
|
struct drm_i915_gem_object *obj;
|
|
|
- struct drm_i915_gem_request *req;
|
|
|
+ struct drm_i915_gem_request *req[I915_NUM_RINGS];
|
|
|
unsigned reset_counter;
|
|
|
- int ret = 0;
|
|
|
+ int i, n = 0;
|
|
|
+ int ret;
|
|
|
|
|
|
if (args->flags != 0)
|
|
|
return -EINVAL;
|
|
@@ -2949,11 +3017,9 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
|
|
|
if (ret)
|
|
|
goto out;
|
|
|
|
|
|
- if (!obj->active || !obj->last_read_req)
|
|
|
+ if (!obj->active)
|
|
|
goto out;
|
|
|
|
|
|
- req = obj->last_read_req;
|
|
|
-
|
|
|
/* Do this after OLR check to make sure we make forward progress polling
|
|
|
* on this IOCTL with a timeout == 0 (like busy ioctl)
|
|
|
*/
|
|
@@ -2964,13 +3030,23 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
|
|
|
|
|
|
drm_gem_object_unreference(&obj->base);
|
|
|
reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
|
|
|
- i915_gem_request_reference(req);
|
|
|
+
|
|
|
+ for (i = 0; i < I915_NUM_RINGS; i++) {
|
|
|
+ if (obj->last_read_req[i] == NULL)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ req[n++] = i915_gem_request_reference(obj->last_read_req[i]);
|
|
|
+ }
|
|
|
+
|
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
|
|
|
- ret = __i915_wait_request(req, reset_counter, true,
|
|
|
- args->timeout_ns > 0 ? &args->timeout_ns : NULL,
|
|
|
- file->driver_priv);
|
|
|
- i915_gem_request_unreference__unlocked(req);
|
|
|
+ for (i = 0; i < n; i++) {
|
|
|
+ if (ret == 0)
|
|
|
+ ret = __i915_wait_request(req[i], reset_counter, true,
|
|
|
+ args->timeout_ns > 0 ? &args->timeout_ns : NULL,
|
|
|
+ file->driver_priv);
|
|
|
+ i915_gem_request_unreference__unlocked(req[i]);
|
|
|
+ }
|
|
|
return ret;
|
|
|
|
|
|
out:
|
|
@@ -2979,6 +3055,56 @@ out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static int
|
|
|
+__i915_gem_object_sync(struct drm_i915_gem_object *obj,
|
|
|
+ struct intel_engine_cs *to,
|
|
|
+ struct drm_i915_gem_request *req)
|
|
|
+{
|
|
|
+ struct intel_engine_cs *from;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ from = i915_gem_request_get_ring(req);
|
|
|
+ if (to == from)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (i915_gem_request_completed(req, true))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ ret = i915_gem_check_olr(req);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ if (!i915_semaphore_is_enabled(obj->base.dev)) {
|
|
|
+ ret = __i915_wait_request(req,
|
|
|
+ atomic_read(&to_i915(obj->base.dev)->gpu_error.reset_counter),
|
|
|
+ to_i915(obj->base.dev)->mm.interruptible, NULL, NULL);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ i915_gem_object_retire_request(obj, req);
|
|
|
+ } else {
|
|
|
+ int idx = intel_ring_sync_index(from, to);
|
|
|
+ u32 seqno = i915_gem_request_get_seqno(req);
|
|
|
+
|
|
|
+ if (seqno <= from->semaphore.sync_seqno[idx])
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ trace_i915_gem_ring_sync_to(from, to, req);
|
|
|
+ ret = to->semaphore.sync_to(to, from, seqno);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ /* We use last_read_req because sync_to()
|
|
|
+ * might have just caused seqno wrap under
|
|
|
+ * the radar.
|
|
|
+ */
|
|
|
+ from->semaphore.sync_seqno[idx] =
|
|
|
+ i915_gem_request_get_seqno(obj->last_read_req[from->id]);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* i915_gem_object_sync - sync an object to a ring.
|
|
|
*
|
|
@@ -2987,7 +3113,17 @@ out:
|
|
|
*
|
|
|
* This code is meant to abstract object synchronization with the GPU.
|
|
|
* Calling with NULL implies synchronizing the object with the CPU
|
|
|
- * rather than a particular GPU ring.
|
|
|
+ * rather than a particular GPU ring. Conceptually we serialise writes
|
|
|
+ * between engines inside the GPU. We only allow on engine to write
|
|
|
+ * into a buffer at any time, but multiple readers. To ensure each has
|
|
|
+ * a coherent view of memory, we must:
|
|
|
+ *
|
|
|
+ * - If there is an outstanding write request to the object, the new
|
|
|
+ * request must wait for it to complete (either CPU or in hw, requests
|
|
|
+ * on the same ring will be naturally ordered).
|
|
|
+ *
|
|
|
+ * - If we are a write request (pending_write_domain is set), the new
|
|
|
+ * request must wait for outstanding read requests to complete.
|
|
|
*
|
|
|
* Returns 0 if successful, else propagates up the lower layer error.
|
|
|
*/
|
|
@@ -2995,41 +3131,32 @@ int
|
|
|
i915_gem_object_sync(struct drm_i915_gem_object *obj,
|
|
|
struct intel_engine_cs *to)
|
|
|
{
|
|
|
- struct intel_engine_cs *from;
|
|
|
- u32 seqno;
|
|
|
- int ret, idx;
|
|
|
+ const bool readonly = obj->base.pending_write_domain == 0;
|
|
|
+ struct drm_i915_gem_request *req[I915_NUM_RINGS];
|
|
|
+ int ret, i, n;
|
|
|
|
|
|
- from = i915_gem_request_get_ring(obj->last_read_req);
|
|
|
-
|
|
|
- if (from == NULL || to == from)
|
|
|
- return 0;
|
|
|
-
|
|
|
- if (to == NULL || !i915_semaphore_is_enabled(obj->base.dev))
|
|
|
- return i915_gem_object_wait_rendering(obj, false);
|
|
|
-
|
|
|
- idx = intel_ring_sync_index(from, to);
|
|
|
-
|
|
|
- seqno = i915_gem_request_get_seqno(obj->last_read_req);
|
|
|
- /* Optimization: Avoid semaphore sync when we are sure we already
|
|
|
- * waited for an object with higher seqno */
|
|
|
- if (seqno <= from->semaphore.sync_seqno[idx])
|
|
|
+ if (!obj->active)
|
|
|
return 0;
|
|
|
|
|
|
- ret = i915_gem_check_olr(obj->last_read_req);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
+ if (to == NULL)
|
|
|
+ return i915_gem_object_wait_rendering(obj, readonly);
|
|
|
|
|
|
- trace_i915_gem_ring_sync_to(from, to, obj->last_read_req);
|
|
|
- ret = to->semaphore.sync_to(to, from, seqno);
|
|
|
- if (!ret)
|
|
|
- /* We use last_read_req because sync_to()
|
|
|
- * might have just caused seqno wrap under
|
|
|
- * the radar.
|
|
|
- */
|
|
|
- from->semaphore.sync_seqno[idx] =
|
|
|
- i915_gem_request_get_seqno(obj->last_read_req);
|
|
|
+ n = 0;
|
|
|
+ if (readonly) {
|
|
|
+ if (obj->last_write_req)
|
|
|
+ req[n++] = obj->last_write_req;
|
|
|
+ } else {
|
|
|
+ for (i = 0; i < I915_NUM_RINGS; i++)
|
|
|
+ if (obj->last_read_req[i])
|
|
|
+ req[n++] = obj->last_read_req[i];
|
|
|
+ }
|
|
|
+ for (i = 0; i < n; i++) {
|
|
|
+ ret = __i915_gem_object_sync(obj, to, req[i]);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
|
|
|
- return ret;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static void i915_gem_object_finish_gtt(struct drm_i915_gem_object *obj)
|
|
@@ -3115,10 +3242,6 @@ int i915_vma_unbind(struct i915_vma *vma)
|
|
|
/* Since the unbound list is global, only move to that list if
|
|
|
* no more VMAs exist. */
|
|
|
if (list_empty(&obj->vma_list)) {
|
|
|
- /* Throw away the active reference before
|
|
|
- * moving to the unbound list. */
|
|
|
- i915_gem_object_retire(obj);
|
|
|
-
|
|
|
i915_gem_gtt_finish_object(obj);
|
|
|
list_move_tail(&obj->global_list, &dev_priv->mm.unbound_list);
|
|
|
}
|
|
@@ -3151,6 +3274,7 @@ int i915_gpu_idle(struct drm_device *dev)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+ WARN_ON(i915_verify_lists(dev));
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -3773,8 +3897,6 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
- i915_gem_object_retire(obj);
|
|
|
-
|
|
|
/* Flush and acquire obj->pages so that we are coherent through
|
|
|
* direct access in memory with previous cached writes through
|
|
|
* shmemfs and that our cache domain tracking remains valid.
|
|
@@ -3972,11 +4094,9 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
|
|
|
u32 old_read_domains, old_write_domain;
|
|
|
int ret;
|
|
|
|
|
|
- if (pipelined != i915_gem_request_get_ring(obj->last_read_req)) {
|
|
|
- ret = i915_gem_object_sync(obj, pipelined);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
- }
|
|
|
+ ret = i915_gem_object_sync(obj, pipelined);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
|
|
|
/* Mark the pin_display early so that we account for the
|
|
|
* display coherency whilst setting up the cache domains.
|
|
@@ -4060,7 +4180,6 @@ i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write)
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
- i915_gem_object_retire(obj);
|
|
|
i915_gem_object_flush_gtt_write_domain(obj);
|
|
|
|
|
|
old_write_domain = obj->base.write_domain;
|
|
@@ -4349,15 +4468,15 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
|
|
|
* necessary flushes here.
|
|
|
*/
|
|
|
ret = i915_gem_object_flush_active(obj);
|
|
|
+ if (ret)
|
|
|
+ goto unref;
|
|
|
|
|
|
- args->busy = obj->active;
|
|
|
- if (obj->last_read_req) {
|
|
|
- struct intel_engine_cs *ring;
|
|
|
- BUILD_BUG_ON(I915_NUM_RINGS > 16);
|
|
|
- ring = i915_gem_request_get_ring(obj->last_read_req);
|
|
|
- args->busy |= intel_ring_flag(ring) << 16;
|
|
|
- }
|
|
|
+ BUILD_BUG_ON(I915_NUM_RINGS > 16);
|
|
|
+ args->busy = obj->active << 16;
|
|
|
+ if (obj->last_write_req)
|
|
|
+ args->busy |= obj->last_write_req->ring->id;
|
|
|
|
|
|
+unref:
|
|
|
drm_gem_object_unreference(&obj->base);
|
|
|
unlock:
|
|
|
mutex_unlock(&dev->struct_mutex);
|
|
@@ -4431,8 +4550,11 @@ unlock:
|
|
|
void i915_gem_object_init(struct drm_i915_gem_object *obj,
|
|
|
const struct drm_i915_gem_object_ops *ops)
|
|
|
{
|
|
|
+ int i;
|
|
|
+
|
|
|
INIT_LIST_HEAD(&obj->global_list);
|
|
|
- INIT_LIST_HEAD(&obj->ring_list);
|
|
|
+ for (i = 0; i < I915_NUM_RINGS; i++)
|
|
|
+ INIT_LIST_HEAD(&obj->ring_list[i]);
|
|
|
INIT_LIST_HEAD(&obj->obj_exec_link);
|
|
|
INIT_LIST_HEAD(&obj->vma_list);
|
|
|
INIT_LIST_HEAD(&obj->batch_pool_link);
|