On Tue, 2019-10-15 at 22:30 +0300, Ville Syrjala wrote:
> From: Ville Syrjälä <ville.syrj...@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.syrj...@linux.intel.com>

Reviewed-by: Stanislav Lisovskiy <stanislav.lisovs...@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];
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to