Commit 85345517 authored by Chris Wilson's avatar Chris Wilson

drm/i915: Retire any pending operations on the old scanout when switching

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=29252Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Cc: stable@kernel.org
parent 69669455
...@@ -1045,6 +1045,8 @@ void i915_gem_clflush_object(struct drm_gem_object *obj); ...@@ -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, int i915_gem_object_set_domain(struct drm_gem_object *obj,
uint32_t read_domains, uint32_t read_domains,
uint32_t write_domain); 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); int i915_gem_init_ringbuffer(struct drm_device *dev);
void i915_gem_cleanup_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, int i915_gem_do_init(struct drm_device *dev, unsigned long start,
......
...@@ -2907,6 +2907,20 @@ i915_gem_object_set_to_display_plane(struct drm_gem_object *obj, ...@@ -2907,6 +2907,20 @@ i915_gem_object_set_to_display_plane(struct drm_gem_object *obj,
return 0; 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. * Moves a single object to the CPU read, and possibly write domain.
* *
......
...@@ -1611,6 +1611,18 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, ...@@ -1611,6 +1611,18 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
wait_event(dev_priv->pending_flip_queue, wait_event(dev_priv->pending_flip_queue,
atomic_read(&obj_priv->pending_flip) == 0); 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, ret = intel_pipe_set_base_atomic(crtc, crtc->fb, x, y,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment