[PATCH] [RFC] drm: Split connection_mutex out of mode_config.mutex

Rob Clark robdclark at gmail.com
Wed May 28 05:04:23 PDT 2014


On Tue, May 27, 2014 at 5:59 PM, Daniel Vetter <daniel.vetter at ffwll.ch> wrote:
> After the split-out of crtc locks from the big mode_config.mutex
> there's still two major areas it protects:
> - Various connector probe states, like connector->status, EDID
>   properties, probed mode lists and similar information.
> - The links from connector->encoder and encoder->crtc and other
>   modeset-relevant connector state (e.g. properties which control the
>   panel fitter).
>
> The later is used by modeset operations. But they don't really care
> about the former since it's allowed to e.g. enable a disconnected VGA
> output or with a mode not in the probed list.
>
> Thus far this hasn't been a problem, but for the atomic modeset
> conversion Rob Clark needs to convert all modeset relevant locks into
> w/w locks. This is required because the order of acquisition is
> determined by how userspace supplies the atomic modeset data. This has
> run into troubles in the detect path since the i915 load detect code
> needs _both_ protections offered by the mode_config.mutex: It updates
> probe state and it needs to change the modeset configuration to enable
> the temporary load detect pipe.
>
> The big deal here is that for the probe/detect users of this lock a
> plain mutex fits best, but for atomic modesets we really want a w/w
> mutex. To fix this lets split out a new connection_mutex lock for the
> modeset relevant parts.
>
> For simplicity I've decided to only add one additional lock for all
> connector/encoder links and modeset configuration states. We have
> piles of different modeset objects in addition to those (like bridges
> or panels), so adding per-object locks would be much more effort.
>
> Also, we're guaranteed (at least for now) to do a full modeset if we
> need to acquire this lock. Which means that fine-grained locking is
> fairly irrelevant compared to the amount of time the full modeset will
> take.
>
> I've done a full audit, and there's just a few things that justify
> special focus:
> - Locking in drm_sysfs.c is almost completely absent. We should
>   sprinkle mode_config.connection_mutex over this file a bit, but
>   since it already lacks mode_config.mutex this patch wont make the
>   situation any worse. This is material for a follow-up patch.
>
> - omap has a omap_framebuffer_flush function which walks the
>   connector->encoder->crtc links and is called from many contexts.
>   Some look like they don't acquire mode_config.mutex, so this is
>   already racy. Again fixing this is material for a separate patch.

btw, I guess until someone makes cmd mode dsi panels work properly
with omapdrm, the flush stuff could probably be dropped.  Otherwise it
should learn the ww dance.. mode_config.mutex isn't the right thing
here, the new connection_mutex is what it needs.

otherwise, looks good.. I'll pull it plus ww_mutex stuff (and maybe
few of the less controvercial atomic patches) into an atomic-prep
branch and see what explodes

BR,
-R

> - The radeon hot_plug function to retrain DP links looks at
>   connector->dpms. Currently this happens without any locking, so is
>   already racy. I think radeon_hotplug_work_func should gain
>   mutex_lock/unlock calls for the mode_config.connection_mutex.
>
> - Same applies to i915's intel_dp_hot_plug. But again, this is already
>   racy.
>
> - i915 load_detect code needs to acquire this lock. Which means the
>   w/w dance due to Rob's work will be nicely contained to _just_ this
>   function.
>
> I've added fixme comments everywhere where it looks suspicious but in
> the sysfs code. After a quick irc discussion with Dave Airlie it
> sounds like the lack of locking in there is due to sysfs cleanup fun
> at module unload.
>
> Only compiled tested thus far.
>
> Cc: Rob Clark <robdclark at gmail.com>
> Cc: Alex Deucher <alexdeucher at gmail.com>
> Signed-off-by: Daniel Vetter <daniel.vetter at ffwll.ch>
> ---
>  drivers/gpu/drm/drm_crtc.c                 | 7 +++++++
>  drivers/gpu/drm/drm_crtc_helper.c          | 2 ++
>  drivers/gpu/drm/drm_edid.c                 | 2 ++
>  drivers/gpu/drm/drm_plane_helper.c         | 7 +++++++
>  drivers/gpu/drm/i915/intel_display.c       | 9 ++++++++-
>  drivers/gpu/drm/i915/intel_dp.c            | 1 +
>  drivers/gpu/drm/omapdrm/omap_fb.c          | 2 ++
>  drivers/gpu/drm/radeon/radeon_connectors.c | 1 +
>  include/drm/drm_crtc.h                     | 1 +
>  9 files changed, 31 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index 34f0bf18d80d..85f15bea88ed 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -54,6 +54,8 @@ void drm_modeset_lock_all(struct drm_device *dev)
>
>         mutex_lock(&dev->mode_config.mutex);
>
> +       mutex_lock(&dev->mode_config.connection_mutex);
> +
>         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
>                 mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
>  }
> @@ -72,6 +74,8 @@ void drm_modeset_unlock_all(struct drm_device *dev)
>         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
>                 mutex_unlock(&crtc->mutex);
>
> +       mutex_unlock(&dev->mode_config.connection_mutex);
> +
>         mutex_unlock(&dev->mode_config.mutex);
>  }
>  EXPORT_SYMBOL(drm_modeset_unlock_all);
> @@ -93,6 +97,7 @@ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
>         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
>                 WARN_ON(!mutex_is_locked(&crtc->mutex));
>
> +       WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
>         WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
>  }
>  EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
> @@ -1795,6 +1800,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
>         DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id);
>
>         mutex_lock(&dev->mode_config.mutex);
> +       mutex_lock(&dev->mode_config.connection_mutex);
>
>         obj = drm_mode_object_find(dev, out_resp->connector_id,
>                                    DRM_MODE_OBJECT_CONNECTOR);
> @@ -1895,6 +1901,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
>         out_resp->count_encoders = encoders_count;
>
>  out:
> +       mutex_unlock(&dev->mode_config.connection_mutex);
>         mutex_unlock(&dev->mode_config.mutex);
>
>         return ret;
> diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
> index a8b78e7bde50..422beb5d9c17 100644
> --- a/drivers/gpu/drm/drm_crtc_helper.c
> +++ b/drivers/gpu/drm/drm_crtc_helper.c
> @@ -89,6 +89,8 @@ bool drm_helper_encoder_in_use(struct drm_encoder *encoder)
>         struct drm_device *dev = encoder->dev;
>
>         WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
> +       WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
> +
>         list_for_each_entry(connector, &dev->mode_config.connector_list, head)
>                 if (connector->encoder == encoder)
>                         return true;
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 7a4fd2ed1280..99b9836c8433 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -3300,6 +3300,8 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
>         struct drm_connector *connector;
>         struct drm_device *dev = encoder->dev;
>
> +       WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
> +
>         list_for_each_entry(connector, &dev->mode_config.connector_list, head)
>                 if (connector->encoder == encoder && connector->eld[0])
>                         return connector;
> diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
> index d966afa7ecae..458d9bf09209 100644
> --- a/drivers/gpu/drm/drm_plane_helper.c
> +++ b/drivers/gpu/drm/drm_plane_helper.c
> @@ -54,6 +54,13 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
>         struct drm_connector *connector;
>         int count = 0;
>
> +       /*
> +        * Note: Once we change the plane hooks to more fine-grained locking we
> +        * need to grab the connection_mutex here to be able to make these
> +        * checks.
> +        */
> +       WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex));
> +
>         list_for_each_entry(connector, &dev->mode_config.connector_list, head)
>                 if (connector->encoder && connector->encoder->crtc == crtc) {
>                         if (connector_list != NULL && count < num_connectors)
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index 883c69a47a99..ea24c602ad6f 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -8327,6 +8327,8 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
>                       connector->base.id, drm_get_connector_name(connector),
>                       encoder->base.id, drm_get_encoder_name(encoder));
>
> +
> +       mutex_lock(&dev->mode_config.connection_mutex);
>         /*
>          * Algorithm gets a little messy:
>          *
> @@ -8369,7 +8371,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
>          */
>         if (!crtc) {
>                 DRM_DEBUG_KMS("no pipe available for load-detect\n");
> -               return false;
> +               goto fail_unlock_connector;
>         }
>
>         mutex_lock(&crtc->mutex);
> @@ -8423,6 +8425,9 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
>         else
>                 intel_crtc->new_config = NULL;
>         mutex_unlock(&crtc->mutex);
> +fail_unlock_connector:
> +       mutex_unlock(&dev->mode_config.connection_mutex);
> +
>         return false;
>  }
>
> @@ -8452,6 +8457,7 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
>                 }
>
>                 mutex_unlock(&crtc->mutex);
> +               mutex_unlock(&connector->dev->mode_config.connection_mutex);
>                 return;
>         }
>
> @@ -8460,6 +8466,7 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
>                 connector->funcs->dpms(connector, old->dpms_mode);
>
>         mutex_unlock(&crtc->mutex);
> +       mutex_unlock(&connector->dev->mode_config.connection_mutex);
>  }
>
>  static int i9xx_pll_refclk(struct drm_device *dev,
> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
> index 398d45a6e071..3b888a128a6f 100644
> --- a/drivers/gpu/drm/i915/intel_dp.c
> +++ b/drivers/gpu/drm/i915/intel_dp.c
> @@ -3299,6 +3299,7 @@ intel_dp_check_link_status(struct intel_dp *intel_dp)
>         u8 sink_irq_vector;
>         u8 link_status[DP_LINK_STATUS_SIZE];
>
> +       /* FIXME: This access isn't protected by any locks. */
>         if (!intel_encoder->connectors_active)
>                 return;
>
> diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c
> index 8b019602ffe6..7a02ab6cd3ad 100644
> --- a/drivers/gpu/drm/omapdrm/omap_fb.c
> +++ b/drivers/gpu/drm/omapdrm/omap_fb.c
> @@ -346,8 +346,10 @@ void omap_framebuffer_flush(struct drm_framebuffer *fb,
>
>         VERB("flush: %d,%d %dx%d, fb=%p", x, y, w, h, fb);
>
> +       /* FIXME: This is racy - no protection against modeset config changes. */
>         while ((connector = omap_framebuffer_get_next_connector(fb, connector))) {
>                 /* only consider connectors that are part of a chain */
> +
>                 if (connector->encoder && connector->encoder->crtc) {
>                         /* TODO: maybe this should propagate thru the crtc who
>                          * could do the coordinate translation..
> diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
> index ea50e0ae7bf7..bcfe5d51ab15 100644
> --- a/drivers/gpu/drm/radeon/radeon_connectors.c
> +++ b/drivers/gpu/drm/radeon/radeon_connectors.c
> @@ -48,6 +48,7 @@ void radeon_connector_hotplug(struct drm_connector *connector)
>         radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
>
>         /* if the connector is already off, don't turn it back on */
> +       /* FIXME: This access isn't protected by any locks. */
>         if (connector->dpms != DRM_MODE_DPMS_ON)
>                 return;
>
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 698d54e27f39..c7a65f309e74 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -723,6 +723,7 @@ struct drm_mode_group {
>   */
>  struct drm_mode_config {
>         struct mutex mutex; /* protects configuration (mode lists etc.) */
> +       struct mutex connection_mutex; /* protects connector->encoder and encoder->crtc links */
>         struct mutex idr_mutex; /* for IDR management */
>         struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */
>         /* this is limited to one for now */
> --
> 1.9.2
>


More information about the dri-devel mailing list