Skip to content

Commit

Permalink
drm/i915: Retire any pending operations on the old scanout when switc…
Browse files Browse the repository at this point in the history
…hing

An old and oft reported bug, is that of the GPU hanging on a
MI_WAIT_FOR_EVENT following a mode switch. The cause is that the GPU is
waiting on a scanline counter on an inactive pipe, and so waits for a
very long time until eventually the user reboots his machine.

We can prevent this either by moving the WAIT into the kernel and
thereby incurring considerable cost on every swapbuffers, or by waiting
for the GPU to retire the last batch that accesses the framebuffer
before installing a new one. As mode switches are much rarer than swap
buffers, this looks like an easy choice.

Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=28964
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=29252
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: stable@kernel.org
  • Loading branch information
Chris Wilson committed Nov 13, 2010
1 parent 6966945 commit 8534551
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 0 deletions.
2 changes: 2 additions & 0 deletions drivers/gpu/drm/i915/i915_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,8 @@ void i915_gem_clflush_object(struct drm_gem_object *obj);
int i915_gem_object_set_domain(struct drm_gem_object *obj,
uint32_t read_domains,
uint32_t write_domain);
int i915_gem_object_flush_gpu(struct drm_i915_gem_object *obj,
bool interruptible);
int i915_gem_init_ringbuffer(struct drm_device *dev);
void i915_gem_cleanup_ringbuffer(struct drm_device *dev);
int i915_gem_do_init(struct drm_device *dev, unsigned long start,
Expand Down
14 changes: 14 additions & 0 deletions drivers/gpu/drm/i915/i915_gem.c
Original file line number Diff line number Diff line change
Expand Up @@ -2907,6 +2907,20 @@ i915_gem_object_set_to_display_plane(struct drm_gem_object *obj,
return 0;
}

int
i915_gem_object_flush_gpu(struct drm_i915_gem_object *obj,
bool interruptible)
{
if (!obj->active)
return 0;

if (obj->base.write_domain & I915_GEM_GPU_DOMAINS)
i915_gem_flush_ring(obj->base.dev, NULL, obj->ring,
0, obj->base.write_domain);

return i915_gem_object_wait_rendering(&obj->base, interruptible);
}

/**
* Moves a single object to the CPU read, and possibly write domain.
*
Expand Down
12 changes: 12 additions & 0 deletions drivers/gpu/drm/i915/intel_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -1611,6 +1611,18 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,

wait_event(dev_priv->pending_flip_queue,
atomic_read(&obj_priv->pending_flip) == 0);

/* Big Hammer, we also need to ensure that any pending
* MI_WAIT_FOR_EVENT inside a user batch buffer on the
* current scanout is retired before unpinning the old
* framebuffer.
*/
ret = i915_gem_object_flush_gpu(obj_priv, false);
if (ret) {
i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj);
mutex_unlock(&dev->struct_mutex);
return ret;
}
}

ret = intel_pipe_set_base_atomic(crtc, crtc->fb, x, y,
Expand Down

0 comments on commit 8534551

Please sign in to comment.