Skip to content

Commit

Permalink
drm/i915: Replace open-coded eviction in i915_gem_idle()
Browse files Browse the repository at this point in the history
With the introduction of the hang-check, we can safely expect that
i915_wait_request() will always return even when the GPU hangs, and so
do not need to open code the wait in order to manually check for the
hang. Also we do not need to always evict all buffers, so only flush
the GPU (and wait for it to idle) for KMS, but continue to evict for UMS.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Eric Anholt <eric@anholt.net>
  • Loading branch information
Chris Wilson authored and Eric Anholt committed Feb 16, 2010
1 parent 724e6d3 commit 29105cc
Showing 1 changed file with 44 additions and 100 deletions.
144 changes: 44 additions & 100 deletions drivers/gpu/drm/i915/i915_gem.c
Original file line number Diff line number Diff line change
Expand Up @@ -4441,129 +4441,73 @@ i915_gem_evict_from_inactive_list(struct drm_device *dev)
return 0;
}

int
i915_gem_idle(struct drm_device *dev)
static int
i915_gpu_idle(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
uint32_t seqno, cur_seqno, last_seqno;
int stuck, ret;
bool lists_empty;
uint32_t seqno;

mutex_lock(&dev->struct_mutex);
spin_lock(&dev_priv->mm.active_list_lock);
lists_empty = list_empty(&dev_priv->mm.flushing_list) &&
list_empty(&dev_priv->mm.active_list);
spin_unlock(&dev_priv->mm.active_list_lock);

if (dev_priv->mm.suspended || dev_priv->ring.ring_obj == NULL) {
mutex_unlock(&dev->struct_mutex);
if (lists_empty)
return 0;
}

/* Hack! Don't let anybody do execbuf while we don't control the chip.
* We need to replace this with a semaphore, or something.
*/
dev_priv->mm.suspended = 1;
del_timer(&dev_priv->hangcheck_timer);

/* Cancel the retire work handler, wait for it to finish if running
*/
mutex_unlock(&dev->struct_mutex);
cancel_delayed_work_sync(&dev_priv->mm.retire_work);
mutex_lock(&dev->struct_mutex);

i915_kernel_lost_context(dev);

/* Flush the GPU along with all non-CPU write domains
*/
/* Flush everything onto the inactive list. */
i915_gem_flush(dev, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
seqno = i915_add_request(dev, NULL, I915_GEM_GPU_DOMAINS);

if (seqno == 0) {
mutex_unlock(&dev->struct_mutex);
if (seqno == 0)
return -ENOMEM;
}

dev_priv->mm.waiting_gem_seqno = seqno;
last_seqno = 0;
stuck = 0;
for (;;) {
cur_seqno = i915_get_gem_seqno(dev);
if (i915_seqno_passed(cur_seqno, seqno))
break;
if (last_seqno == cur_seqno) {
if (stuck++ > 100) {
DRM_ERROR("hardware wedged\n");
atomic_set(&dev_priv->mm.wedged, 1);
DRM_WAKEUP(&dev_priv->irq_queue);
break;
}
}
msleep(10);
last_seqno = cur_seqno;
}
dev_priv->mm.waiting_gem_seqno = 0;

i915_gem_retire_requests(dev);

spin_lock(&dev_priv->mm.active_list_lock);
if (!atomic_read(&dev_priv->mm.wedged)) {
/* Active and flushing should now be empty as we've
* waited for a sequence higher than any pending execbuffer
*/
WARN_ON(!list_empty(&dev_priv->mm.active_list));
WARN_ON(!list_empty(&dev_priv->mm.flushing_list));
/* Request should now be empty as we've also waited
* for the last request in the list
*/
WARN_ON(!list_empty(&dev_priv->mm.request_list));
}

/* Empty the active and flushing lists to inactive. If there's
* anything left at this point, it means that we're wedged and
* nothing good's going to happen by leaving them there. So strip
* the GPU domains and just stuff them onto inactive.
*/
while (!list_empty(&dev_priv->mm.active_list)) {
struct drm_gem_object *obj;
uint32_t old_write_domain;

obj = list_first_entry(&dev_priv->mm.active_list,
struct drm_i915_gem_object,
list)->obj;
old_write_domain = obj->write_domain;
obj->write_domain &= ~I915_GEM_GPU_DOMAINS;
i915_gem_object_move_to_inactive(obj);

trace_i915_gem_object_change_domain(obj,
obj->read_domains,
old_write_domain);
}
spin_unlock(&dev_priv->mm.active_list_lock);
return i915_wait_request(dev, seqno);
}

while (!list_empty(&dev_priv->mm.flushing_list)) {
struct drm_gem_object *obj;
uint32_t old_write_domain;
int
i915_gem_idle(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
int ret;

obj = list_first_entry(&dev_priv->mm.flushing_list,
struct drm_i915_gem_object,
list)->obj;
old_write_domain = obj->write_domain;
obj->write_domain &= ~I915_GEM_GPU_DOMAINS;
i915_gem_object_move_to_inactive(obj);
mutex_lock(&dev->struct_mutex);

trace_i915_gem_object_change_domain(obj,
obj->read_domains,
old_write_domain);
if (dev_priv->mm.suspended || dev_priv->ring.ring_obj == NULL) {
mutex_unlock(&dev->struct_mutex);
return 0;
}


/* Move all inactive buffers out of the GTT. */
ret = i915_gem_evict_from_inactive_list(dev);
WARN_ON(!list_empty(&dev_priv->mm.inactive_list));
ret = i915_gpu_idle(dev);
if (ret) {
mutex_unlock(&dev->struct_mutex);
return ret;
}

/* Under UMS, be paranoid and evict. */
if (!drm_core_check_feature(dev, DRIVER_MODESET)) {
ret = i915_gem_evict_from_inactive_list(dev);
if (ret) {
mutex_unlock(&dev->struct_mutex);
return ret;
}
}

/* Hack! Don't let anybody do execbuf while we don't control the chip.
* We need to replace this with a semaphore, or something.
* And not confound mm.suspended!
*/
dev_priv->mm.suspended = 1;
del_timer(&dev_priv->hangcheck_timer);

i915_kernel_lost_context(dev);
i915_gem_cleanup_ringbuffer(dev);

mutex_unlock(&dev->struct_mutex);

/* Cancel the retire work handler, which should be idle now. */
cancel_delayed_work_sync(&dev_priv->mm.retire_work);

return 0;
}

Expand Down

0 comments on commit 29105cc

Please sign in to comment.