mirror of
https://github.com/torvalds/linux.git
synced 2024-11-13 23:51:39 +00:00
drm/i915: Flush outstanding unpin tasks before pageflipping
If we accumulate unpin tasks because we are pageflipping faster than the system can schedule its workers, we can effectively create a pin-leak. The solution taken here is to limit the number of unpin tasks we have per-crtc and to flush those outstanding tasks if we accumulate too many. This should prevent any jitter in the normal case, and also prevent the hang if we should run too fast. Note: It is important that we switch from the system workqueue to our own dev_priv->wq since all work items on that queue are guaranteed to only need the dev->struct_mutex and not any modeset resources. For otherwise if we have a work item ahead in the queue which needs the modeset lock (like the output detect work used by both polling or hpd), this work and so the unpin work will never execute since the pageflip code already holds that lock. Unfortunately there's no lockdep support for this scenario in the workqueue code. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=46991 Reported-and-tested-by: Tvrtko Ursulin <tvrtko.ursulin@onelan.co.uk> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> [danvet: Added note about workqueu deadlock.] Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=56337 Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
This commit is contained in:
parent
a726915cef
commit
b4a98e57fc
@ -6896,14 +6896,19 @@ static void intel_unpin_work_fn(struct work_struct *__work)
|
||||
{
|
||||
struct intel_unpin_work *work =
|
||||
container_of(__work, struct intel_unpin_work, work);
|
||||
struct drm_device *dev = work->crtc->dev;
|
||||
|
||||
mutex_lock(&work->dev->struct_mutex);
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
intel_unpin_fb_obj(work->old_fb_obj);
|
||||
drm_gem_object_unreference(&work->pending_flip_obj->base);
|
||||
drm_gem_object_unreference(&work->old_fb_obj->base);
|
||||
|
||||
intel_update_fbc(work->dev);
|
||||
mutex_unlock(&work->dev->struct_mutex);
|
||||
intel_update_fbc(dev);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
BUG_ON(atomic_read(&to_intel_crtc(work->crtc)->unpin_work_count) == 0);
|
||||
atomic_dec(&to_intel_crtc(work->crtc)->unpin_work_count);
|
||||
|
||||
kfree(work);
|
||||
}
|
||||
|
||||
@ -6951,9 +6956,9 @@ static void do_intel_finish_page_flip(struct drm_device *dev,
|
||||
|
||||
atomic_clear_mask(1 << intel_crtc->plane,
|
||||
&obj->pending_flip.counter);
|
||||
|
||||
wake_up(&dev_priv->pending_flip_queue);
|
||||
schedule_work(&work->work);
|
||||
|
||||
queue_work(dev_priv->wq, &work->work);
|
||||
|
||||
trace_i915_flip_complete(intel_crtc->plane, work->pending_flip_obj);
|
||||
}
|
||||
@ -7254,7 +7259,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
|
||||
return -ENOMEM;
|
||||
|
||||
work->event = event;
|
||||
work->dev = crtc->dev;
|
||||
work->crtc = crtc;
|
||||
intel_fb = to_intel_framebuffer(crtc->fb);
|
||||
work->old_fb_obj = intel_fb->obj;
|
||||
INIT_WORK(&work->work, intel_unpin_work_fn);
|
||||
@ -7279,6 +7284,9 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
|
||||
intel_fb = to_intel_framebuffer(fb);
|
||||
obj = intel_fb->obj;
|
||||
|
||||
if (atomic_read(&intel_crtc->unpin_work_count) >= 2)
|
||||
flush_workqueue(dev_priv->wq);
|
||||
|
||||
ret = i915_mutex_lock_interruptible(dev);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
@ -7297,6 +7305,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
|
||||
* the flip occurs and the object is no longer visible.
|
||||
*/
|
||||
atomic_add(1 << intel_crtc->plane, &work->old_fb_obj->pending_flip);
|
||||
atomic_inc(&intel_crtc->unpin_work_count);
|
||||
|
||||
ret = dev_priv->display.queue_flip(dev, crtc, fb, obj);
|
||||
if (ret)
|
||||
@ -7311,6 +7320,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
|
||||
return 0;
|
||||
|
||||
cleanup_pending:
|
||||
atomic_dec(&intel_crtc->unpin_work_count);
|
||||
atomic_sub(1 << intel_crtc->plane, &work->old_fb_obj->pending_flip);
|
||||
drm_gem_object_unreference(&work->old_fb_obj->base);
|
||||
drm_gem_object_unreference(&obj->base);
|
||||
|
@ -211,6 +211,8 @@ struct intel_crtc {
|
||||
struct intel_unpin_work *unpin_work;
|
||||
int fdi_lanes;
|
||||
|
||||
atomic_t unpin_work_count;
|
||||
|
||||
/* Display surface base address adjustement for pageflips. Note that on
|
||||
* gen4+ this only adjusts up to a tile, offsets within a tile are
|
||||
* handled in the hw itself (with the TILEOFF register). */
|
||||
@ -395,7 +397,7 @@ intel_get_crtc_for_plane(struct drm_device *dev, int plane)
|
||||
|
||||
struct intel_unpin_work {
|
||||
struct work_struct work;
|
||||
struct drm_device *dev;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_i915_gem_object *old_fb_obj;
|
||||
struct drm_i915_gem_object *pending_flip_obj;
|
||||
struct drm_pending_vblank_event *event;
|
||||
|
Loading…
Reference in New Issue
Block a user