[Intel-gfx] [PATCH v2 02/13] drm/i915: Rework global state locking
Lisovskiy, Stanislav
stanislav.lisovskiy at intel.com
Thu Oct 24 10:24:12 UTC 2019
On Tue, 2019-10-15 at 22:30 +0300, Ville Syrjala wrote:
> From: Ville Syrjälä <ville.syrjala at linux.intel.com>
>
> So far we've sort of protected the global state under dev_priv with
> the connection_mutex. I wan to change that so that we can change the
> cdclk even for pure plane updates. To that end let's formalize the
> protection of the global state to follow what I started with the
> cdclk
> code already (though not entirely properly) such that any crtc mutex
> will suffice as a read lock, and all crtcs mutexes act as the write
> lock.
>
> We'll also pimp intel_atomic_state_clear() to clear the entire global
> state, so that we don't accidentally leak stale information between
> the locking retries.
>
> As a slight optimization we'll only lock the crtc mutexes to protect
> the global state, however if and when we actually have to poke the
> hw (eg. if the actual cdclk changes) we must serialize commits
> across all crtcs so that a parallel nonblocking commit can't get
> ahead of the cdclk reprogamming. We do that by adding all crtcs to
> the state.
>
> TODO: the old global state examined during commit may still
> be a problem since it always looks at the _latest_ swapped state
> in dev_priv. Need to add proper old/new state for that too I think.
>
> v2: Remeber to serialize the commits if necessary
>
> Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
Reviewed-by: Stanislav Lisovskiy <stanislav.lisovskiy at intel.com>
> ---
> drivers/gpu/drm/i915/display/intel_atomic.c | 44 ++++++++
> drivers/gpu/drm/i915/display/intel_atomic.h | 5 +
> drivers/gpu/drm/i915/display/intel_audio.c | 10 +-
> drivers/gpu/drm/i915/display/intel_cdclk.c | 102 ++++++++++----
> ----
> drivers/gpu/drm/i915/display/intel_display.c | 29 ++++-
> .../drm/i915/display/intel_display_types.h | 8 ++
> drivers/gpu/drm/i915/i915_drv.h | 11 +-
> 7 files changed, 153 insertions(+), 56 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c
> b/drivers/gpu/drm/i915/display/intel_atomic.c
> index c5a552a69752..9cd6d2348a1e 100644
> --- a/drivers/gpu/drm/i915/display/intel_atomic.c
> +++ b/drivers/gpu/drm/i915/display/intel_atomic.c
> @@ -429,6 +429,13 @@ void intel_atomic_state_clear(struct
> drm_atomic_state *s)
> struct intel_atomic_state *state = to_intel_atomic_state(s);
> drm_atomic_state_default_clear(&state->base);
> state->dpll_set = state->modeset = false;
> + state->global_state_changed = false;
> + state->active_pipes = 0;
> + memset(&state->min_cdclk, 0, sizeof(state->min_cdclk));
> + memset(&state->min_voltage_level, 0, sizeof(state-
> >min_voltage_level));
> + memset(&state->cdclk.logical, 0, sizeof(state->cdclk.logical));
> + memset(&state->cdclk.actual, 0, sizeof(state->cdclk.actual));
> + state->cdclk.pipe = INVALID_PIPE;
> }
>
> struct intel_crtc_state *
> @@ -442,3 +449,40 @@ intel_atomic_get_crtc_state(struct
> drm_atomic_state *state,
>
> return to_intel_crtc_state(crtc_state);
> }
> +
> +int intel_atomic_lock_global_state(struct intel_atomic_state *state)
> +{
> + struct drm_i915_private *dev_priv = to_i915(state->base.dev);
> + struct intel_crtc *crtc;
> +
> + state->global_state_changed = true;
> +
> + for_each_intel_crtc(&dev_priv->drm, crtc) {
> + int ret;
> +
> + ret = drm_modeset_lock(&crtc->base.mutex,
> + state->base.acquire_ctx);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +int intel_atomic_serialize_global_state(struct intel_atomic_state
> *state)
> +{
> + struct drm_i915_private *dev_priv = to_i915(state->base.dev);
> + struct intel_crtc *crtc;
> +
> + state->global_state_changed = true;
> +
> + for_each_intel_crtc(&dev_priv->drm, crtc) {
> + struct intel_crtc_state *crtc_state;
> +
> + crtc_state = intel_atomic_get_crtc_state(&state->base,
> crtc);
> + if (IS_ERR(crtc_state))
> + return PTR_ERR(crtc_state);
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/gpu/drm/i915/display/intel_atomic.h
> b/drivers/gpu/drm/i915/display/intel_atomic.h
> index 58065d3161a3..49d5cb1b9e0a 100644
> --- a/drivers/gpu/drm/i915/display/intel_atomic.h
> +++ b/drivers/gpu/drm/i915/display/intel_atomic.h
> @@ -16,6 +16,7 @@ struct drm_crtc_state;
> struct drm_device;
> struct drm_i915_private;
> struct drm_property;
> +struct intel_atomic_state;
> struct intel_crtc;
> struct intel_crtc_state;
>
> @@ -46,4 +47,8 @@ int intel_atomic_setup_scalers(struct
> drm_i915_private *dev_priv,
> struct intel_crtc *intel_crtc,
> struct intel_crtc_state *crtc_state);
>
> +int intel_atomic_lock_global_state(struct intel_atomic_state
> *state);
> +
> +int intel_atomic_serialize_global_state(struct intel_atomic_state
> *state);
> +
> #endif /* __INTEL_ATOMIC_H__ */
> diff --git a/drivers/gpu/drm/i915/display/intel_audio.c
> b/drivers/gpu/drm/i915/display/intel_audio.c
> index ed18511befa3..85e6b2bbb34f 100644
> --- a/drivers/gpu/drm/i915/display/intel_audio.c
> +++ b/drivers/gpu/drm/i915/display/intel_audio.c
> @@ -28,6 +28,7 @@
> #include <drm/i915_component.h>
>
> #include "i915_drv.h"
> +#include "intel_atomic.h"
> #include "intel_audio.h"
> #include "intel_display_types.h"
> #include "intel_lpe_audio.h"
> @@ -818,13 +819,8 @@ static void glk_force_audio_cdclk(struct
> drm_i915_private *dev_priv,
> to_intel_atomic_state(state)->cdclk.force_min_cdclk =
> enable ? 2 * 96000 : 0;
>
> - /*
> - * Protects dev_priv->cdclk.force_min_cdclk
> - * Need to lock this here in case we have no active pipes
> - * and thus wouldn't lock it during the commit otherwise.
> - */
> - ret = drm_modeset_lock(&dev_priv-
> >drm.mode_config.connection_mutex,
> - &ctx);
> + /* Protects dev_priv->cdclk.force_min_cdclk */
> + ret =
> intel_atomic_lock_global_state(to_intel_atomic_state(state));
> if (!ret)
> ret = drm_atomic_commit(state);
>
> diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c
> b/drivers/gpu/drm/i915/display/intel_cdclk.c
> index 6eaebc38ee01..6c17a3bbf866 100644
> --- a/drivers/gpu/drm/i915/display/intel_cdclk.c
> +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c
> @@ -2007,11 +2007,20 @@ static int intel_compute_min_cdclk(struct
> intel_atomic_state *state)
> sizeof(state->min_cdclk));
>
> for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) {
> + int ret;
> +
> min_cdclk = intel_crtc_compute_min_cdclk(crtc_state);
> if (min_cdclk < 0)
> return min_cdclk;
>
> + if (state->min_cdclk[i] == min_cdclk)
> + continue;
> +
> state->min_cdclk[i] = min_cdclk;
> +
> + ret = intel_atomic_lock_global_state(state);
> + if (ret)
> + return ret;
> }
>
> min_cdclk = state->cdclk.force_min_cdclk;
> @@ -2034,7 +2043,7 @@ static int intel_compute_min_cdclk(struct
> intel_atomic_state *state)
> * future platforms this code will need to be
> * adjusted.
> */
> -static u8 bxt_compute_min_voltage_level(struct intel_atomic_state
> *state)
> +static int bxt_compute_min_voltage_level(struct intel_atomic_state
> *state)
> {
> struct drm_i915_private *dev_priv = to_i915(state->base.dev);
> struct intel_crtc *crtc;
> @@ -2047,11 +2056,21 @@ static u8
> bxt_compute_min_voltage_level(struct intel_atomic_state *state)
> sizeof(state->min_voltage_level));
>
> for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) {
> + int ret;
> +
> if (crtc_state->base.enable)
> - state->min_voltage_level[i] =
> - crtc_state->min_voltage_level;
> + min_voltage_level = crtc_state-
> >min_voltage_level;
> else
> - state->min_voltage_level[i] = 0;
> + min_voltage_level = 0;
> +
> + if (state->min_voltage_level[i] == min_voltage_level)
> + continue;
> +
> + state->min_voltage_level[i] = min_voltage_level;
> +
> + ret = intel_atomic_lock_global_state(state);
> + if (ret)
> + return ret;
> }
>
> min_voltage_level = 0;
> @@ -2195,20 +2214,24 @@ static int skl_modeset_calc_cdclk(struct
> intel_atomic_state *state)
> static int bxt_modeset_calc_cdclk(struct intel_atomic_state *state)
> {
> struct drm_i915_private *dev_priv = to_i915(state->base.dev);
> - int min_cdclk, cdclk, vco;
> + int min_cdclk, min_voltage_level, cdclk, vco;
>
> min_cdclk = intel_compute_min_cdclk(state);
> if (min_cdclk < 0)
> return min_cdclk;
>
> + min_voltage_level = bxt_compute_min_voltage_level(state);
> + if (min_voltage_level < 0)
> + return min_voltage_level;
> +
> cdclk = bxt_calc_cdclk(dev_priv, min_cdclk);
> vco = bxt_calc_cdclk_pll_vco(dev_priv, cdclk);
>
> state->cdclk.logical.vco = vco;
> state->cdclk.logical.cdclk = cdclk;
> state->cdclk.logical.voltage_level =
> - max(dev_priv->display.calc_voltage_level(cdclk),
> - bxt_compute_min_voltage_level(state));
> + max_t(int, min_voltage_level,
> + dev_priv->display.calc_voltage_level(cdclk));
>
> if (!state->active_pipes) {
> cdclk = bxt_calc_cdclk(dev_priv, state-
> >cdclk.force_min_cdclk);
> @@ -2225,23 +2248,6 @@ static int bxt_modeset_calc_cdclk(struct
> intel_atomic_state *state)
> return 0;
> }
>
> -static int intel_lock_all_pipes(struct intel_atomic_state *state)
> -{
> - struct drm_i915_private *dev_priv = to_i915(state->base.dev);
> - struct intel_crtc *crtc;
> -
> - /* Add all pipes to the state */
> - for_each_intel_crtc(&dev_priv->drm, crtc) {
> - struct intel_crtc_state *crtc_state;
> -
> - crtc_state = intel_atomic_get_crtc_state(&state->base,
> crtc);
> - if (IS_ERR(crtc_state))
> - return PTR_ERR(crtc_state);
> - }
> -
> - return 0;
> -}
> -
> static int intel_modeset_all_pipes(struct intel_atomic_state *state)
> {
> struct drm_i915_private *dev_priv = to_i915(state->base.dev);
> @@ -2308,46 +2314,56 @@ int intel_modeset_calc_cdclk(struct
> intel_atomic_state *state)
> return ret;
>
> /*
> - * Writes to dev_priv->cdclk.logical must protected by
> - * holding all the crtc locks, even if we don't end up
> + * Writes to dev_priv->cdclk.{actual,logical} must protected
> + * by holding all the crtc mutexes even if we don't end up
> * touching the hardware
> */
> - if (intel_cdclk_changed(&dev_priv->cdclk.logical,
> - &state->cdclk.logical)) {
> - ret = intel_lock_all_pipes(state);
> - if (ret < 0)
> + if (intel_cdclk_changed(&dev_priv->cdclk.actual,
> + &state->cdclk.actual)) {
> + /*
> + * Also serialize commits across all crtcs
> + * if the actual hw needs to be poked.
> + */
> + ret = intel_atomic_serialize_global_state(state);
> + if (ret)
> + return ret;
> + } else if (intel_cdclk_changed(&dev_priv->cdclk.logical,
> + &state->cdclk.logical)) {
> + ret = intel_atomic_lock_global_state(state);
> + if (ret)
> return ret;
> + } else {
> + return 0;
> }
>
> - if (is_power_of_2(state->active_pipes)) {
> + if (is_power_of_2(state->active_pipes) &&
> + intel_cdclk_needs_cd2x_update(dev_priv,
> + &dev_priv->cdclk.actual,
> + &state->cdclk.actual)) {
> struct intel_crtc *crtc;
> struct intel_crtc_state *crtc_state;
>
> pipe = ilog2(state->active_pipes);
> crtc = intel_get_crtc_for_pipe(dev_priv, pipe);
> - crtc_state = intel_atomic_get_new_crtc_state(state,
> crtc);
> - if (crtc_state &&
> - drm_atomic_crtc_needs_modeset(&crtc_state->base))
> +
> + crtc_state = intel_atomic_get_crtc_state(&state->base,
> crtc);
> + if (IS_ERR(crtc_state))
> + return PTR_ERR(crtc_state);
> +
> + if (drm_atomic_crtc_needs_modeset(&crtc_state->base))
> pipe = INVALID_PIPE;
> } else {
> pipe = INVALID_PIPE;
> }
>
> - /* All pipes must be switched off while we change the cdclk. */
> - if (pipe != INVALID_PIPE &&
> - intel_cdclk_needs_cd2x_update(dev_priv,
> - &dev_priv->cdclk.actual,
> - &state->cdclk.actual)) {
> - ret = intel_lock_all_pipes(state);
> - if (ret)
> - return ret;
> -
> + if (pipe != INVALID_PIPE) {
> state->cdclk.pipe = pipe;
>
> DRM_DEBUG_KMS("Can change cdclk with pipe %c active\n",
> pipe_name(pipe));
> } else if (intel_cdclk_needs_modeset(&dev_priv->cdclk.actual,
> &state->cdclk.actual)) {
> + /* All pipes must be switched off while we change the
> cdclk. */
> ret = intel_modeset_all_pipes(state);
> if (ret)
> return ret;
> diff --git a/drivers/gpu/drm/i915/display/intel_display.c
> b/drivers/gpu/drm/i915/display/intel_display.c
> index 7d7d1859775a..a8444d9841c1 100644
> --- a/drivers/gpu/drm/i915/display/intel_display.c
> +++ b/drivers/gpu/drm/i915/display/intel_display.c
> @@ -12191,6 +12191,12 @@ static bool
> check_digital_port_conflicts(struct intel_atomic_state *state)
> unsigned int used_mst_ports = 0;
> bool ret = true;
>
> + /*
> + * We're going to peek into connector->state,
> + * hence connection_mutex must be held.
> + */
> + drm_modeset_lock_assert_held(&dev-
> >mode_config.connection_mutex);
> +
> /*
> * Walk the connector list instead of the encoder
> * list to detect the problem on ddi platforms
> @@ -13463,7 +13469,6 @@ static int intel_modeset_checks(struct
> intel_atomic_state *state)
> state->active_pipes = dev_priv->active_pipes;
> state->cdclk.logical = dev_priv->cdclk.logical;
> state->cdclk.actual = dev_priv->cdclk.actual;
> - state->cdclk.pipe = INVALID_PIPE;
>
> for_each_oldnew_intel_crtc_in_state(state, crtc,
> old_crtc_state,
> new_crtc_state, i) {
> @@ -13476,6 +13481,12 @@ static int intel_modeset_checks(struct
> intel_atomic_state *state)
> state->active_pipe_changes |= BIT(crtc->pipe);
> }
>
> + if (state->active_pipe_changes) {
> + ret = intel_atomic_lock_global_state(state);
> + if (ret)
> + return ret;
> + }
> +
> ret = intel_modeset_calc_cdclk(state);
> if (ret)
> return ret;
> @@ -13577,7 +13588,7 @@ static int intel_atomic_check(struct
> drm_device *dev,
> struct intel_crtc_state *old_crtc_state, *new_crtc_state;
> struct intel_crtc *crtc;
> int ret, i;
> - bool any_ms = state->cdclk.force_min_cdclk_changed;
> + bool any_ms = false;
>
> /* Catch I915_MODE_FLAG_INHERITED */
> for_each_oldnew_intel_crtc_in_state(state, crtc,
> old_crtc_state,
> @@ -13615,6 +13626,8 @@ static int intel_atomic_check(struct
> drm_device *dev,
> if (ret)
> goto fail;
>
> + any_ms |= state->cdclk.force_min_cdclk_changed;
> +
> if (any_ms) {
> ret = intel_modeset_checks(state);
> if (ret)
> @@ -14234,6 +14247,14 @@ static void intel_atomic_track_fbs(struct
> intel_atomic_state *state)
> plane->frontbuffer_bit);
> }
>
> +static void assert_global_state_locked(struct drm_i915_private
> *dev_priv)
> +{
> + struct intel_crtc *crtc;
> +
> + for_each_intel_crtc(&dev_priv->drm, crtc)
> + drm_modeset_lock_assert_held(&crtc->base.mutex);
> +}
> +
> static int intel_atomic_commit(struct drm_device *dev,
> struct drm_atomic_state *_state,
> bool nonblock)
> @@ -14299,7 +14320,9 @@ static int intel_atomic_commit(struct
> drm_device *dev,
> intel_shared_dpll_swap_state(state);
> intel_atomic_track_fbs(state);
>
> - if (state->modeset) {
> + if (state->global_state_changed) {
> + assert_global_state_locked(dev_priv);
> +
> memcpy(dev_priv->min_cdclk, state->min_cdclk,
> sizeof(state->min_cdclk));
> memcpy(dev_priv->min_voltage_level, state-
> >min_voltage_level,
> diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h
> b/drivers/gpu/drm/i915/display/intel_display_types.h
> index 40390d855815..01fed8957ade 100644
> --- a/drivers/gpu/drm/i915/display/intel_display_types.h
> +++ b/drivers/gpu/drm/i915/display/intel_display_types.h
> @@ -506,6 +506,14 @@ struct intel_atomic_state {
>
> bool rps_interactive;
>
> + /*
> + * active_pipes
> + * min_cdclk[]
> + * min_voltage_level[]
> + * cdclk.*
> + */
> + bool global_state_changed;
> +
> /* Gen9+ only */
> struct skl_ddb_values wm_results;
>
> diff --git a/drivers/gpu/drm/i915/i915_drv.h
> b/drivers/gpu/drm/i915/i915_drv.h
> index c46b339064c0..d3e61152d924 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -1105,13 +1105,14 @@ struct drm_i915_private {
> unsigned int fdi_pll_freq;
> unsigned int czclk_freq;
>
> + /*
> + * For reading holding any crtc lock is sufficient,
> + * for writing must hold all of them.
> + */
> struct {
> /*
> * The current logical cdclk state.
> * See intel_atomic_state.cdclk.logical
> - *
> - * For reading holding any crtc lock is sufficient,
> - * for writing must hold all of them.
> */
> struct intel_cdclk_state logical;
> /*
> @@ -1181,6 +1182,10 @@ struct drm_i915_private {
> */
> struct mutex dpll_lock;
>
> + /*
> + * For reading active_pipes, min_cdclk, min_voltage_level
> holding
> + * any crtc lock is sufficient, for writing must hold all of
> them.
> + */
> u8 active_pipes;
> /* minimum acceptable cdclk for each pipe */
> int min_cdclk[I915_MAX_PIPES];
More information about the Intel-gfx
mailing list