Commit 8d52e447 authored by Chris Wilson's avatar Chris Wilson

drm/i915: Defer modeset cleanup to a secondary task

If we avoid cleaning up the old state immediately in
intel_atomic_commit_tail() and defer it to a second task, we can avoid
taking heavily contended locks when the caller is ready to procede.
Subsequent modesets will wait for the cleanup operation (either directly
via the ordered modeset wq or indirectly through the atomic helperr)
which keeps the number of inflight cleanup tasks in check.

As an example, during reset an immediate modeset is performed to disable
the displays before the HW is reset, which must avoid struct_mutex to
avoid recursion. Moving the cleanup to a separate task, defers acquiring
the struct_mutex to after the GPU is running again, allowing it to
complete. Even in a few patches time (optimist!) when we no longer
require struct_mutex to unpin the framebuffers, it will still be good
practice to minimise the number of contention points along reset. The
mutex dependency still exists (as one modeset flushes the other), but in
the short term it resolves the deadlock for simple reset cases.

Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=101600Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Link: https://patchwork.freedesktop.org/patch/msgid/20180623103951.23889-1-chris@chris-wilson.co.ukAcked-by: default avatarMaarten Lankhorst <maarten.lankhorst@linux.intel.com>
Acked-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent aa58f58d
...@@ -12555,6 +12555,19 @@ static void intel_atomic_commit_fence_wait(struct intel_atomic_state *intel_stat ...@@ -12555,6 +12555,19 @@ static void intel_atomic_commit_fence_wait(struct intel_atomic_state *intel_stat
finish_wait(&dev_priv->gpu_error.wait_queue, &wait_reset); finish_wait(&dev_priv->gpu_error.wait_queue, &wait_reset);
} }
static void intel_atomic_cleanup_work(struct work_struct *work)
{
struct drm_atomic_state *state =
container_of(work, struct drm_atomic_state, commit_work);
struct drm_i915_private *i915 = to_i915(state->dev);
drm_atomic_helper_cleanup_planes(&i915->drm, state);
drm_atomic_helper_commit_cleanup_done(state);
drm_atomic_state_put(state);
intel_atomic_helper_free_state(i915);
}
static void intel_atomic_commit_tail(struct drm_atomic_state *state) static void intel_atomic_commit_tail(struct drm_atomic_state *state)
{ {
struct drm_device *dev = state->dev; struct drm_device *dev = state->dev;
...@@ -12715,13 +12728,16 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state) ...@@ -12715,13 +12728,16 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
intel_display_power_put(dev_priv, POWER_DOMAIN_MODESET); intel_display_power_put(dev_priv, POWER_DOMAIN_MODESET);
} }
drm_atomic_helper_cleanup_planes(dev, state); /*
* Defer the cleanup of the old state to a separate worker to not
drm_atomic_helper_commit_cleanup_done(state); * impede the current task (userspace for blocking modesets) that
* are executed inline. For out-of-line asynchronous modesets/flips,
drm_atomic_state_put(state); * deferring to a new worker seems overkill, but we would place a
* schedule point (cond_resched()) here anyway to keep latencies
intel_atomic_helper_free_state(dev_priv); * down.
*/
INIT_WORK(&state->commit_work, intel_atomic_cleanup_work);
schedule_work(&state->commit_work);
} }
static void intel_atomic_commit_work(struct work_struct *work) static void intel_atomic_commit_work(struct work_struct *work)
......
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