On Wed, Apr 05, 2017 at 10:11:18AM +0200, Maarten Lankhorst wrote: > mode_valid() called from drm_helper_probe_single_connector_modes() > may need to look at connector->state because what a valid mode is may > depend on connector properties being set. For example some HDMI modes > might be rejected when a connector property forces the connector > into DVI mode. > > Some implementations of detect() already lock all state, > so we have to pass an acquire_ctx to them to prevent a deadlock. > > This means changing the function signature of detect() slightly, > and passing the acquire_ctx for locking multiple crtc's. > For the callbacks, it will always be non-zero. To allow callers > not to worry about this, drm_helper_probe_detect_ctx is added > which might handle -EDEADLK for you. > > Changes since v1: > - Always set ctx parameter. > Changes since v2: > - Always take connection_mutex when probing. > > Signed-off-by: Maarten Lankhorst <maarten.lankhorst@xxxxxxxxxxxxxxx> > Cc: Boris Brezillon <boris.brezillon@xxxxxxxxxxxxxxxxxx> A few minor nits, mostly to improve checks and docs. With those all addressed: Reviewed-by: Daniel Vetter <daniel.vetter@xxxxxxxx> > --- > Fixed drm_helper_probe_detect, > is this version better? > drivers/gpu/drm/drm_probe_helper.c | 100 ++++++++++++++++++++++++++++--- > drivers/gpu/drm/i915/intel_crt.c | 25 ++++---- > drivers/gpu/drm/i915/intel_display.c | 25 ++++---- > drivers/gpu/drm/i915/intel_dp.c | 24 ++++---- > drivers/gpu/drm/i915/intel_drv.h | 8 +-- > drivers/gpu/drm/i915/intel_hotplug.c | 3 +- > drivers/gpu/drm/i915/intel_tv.c | 21 +++---- > include/drm/drm_connector.h | 5 ++ > include/drm/drm_crtc_helper.h | 3 + > include/drm/drm_modeset_helper_vtables.h | 24 ++++++++ > 10 files changed, 175 insertions(+), 63 deletions(-) > > diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c > index efb5e5e8ce62..ba0160446aac 100644 > --- a/drivers/gpu/drm/drm_probe_helper.c > +++ b/drivers/gpu/drm/drm_probe_helper.c > @@ -169,12 +169,73 @@ void drm_kms_helper_poll_enable(struct drm_device *dev) > EXPORT_SYMBOL(drm_kms_helper_poll_enable); > > static enum drm_connector_status > -drm_connector_detect(struct drm_connector *connector, bool force) > +drm_helper_probe_detect_ctx(struct drm_connector *connector, bool force) > { > - return connector->funcs->detect ? > - connector->funcs->detect(connector, force) : > - connector_status_connected; > + const struct drm_connector_helper_funcs *funcs = connector->helper_private; > + struct drm_modeset_acquire_ctx ctx; > + int ret; > + > + drm_modeset_acquire_init(&ctx, 0); > + > +retry: > + ret = drm_modeset_lock(&connector->dev->mode_config.connection_mutex, &ctx); > + if (!ret) { > + if (funcs->detect_ctx) > + ret = funcs->detect_ctx(connector, &ctx, force); > + else if (connector->funcs->detect) > + ret = connector->funcs->detect(connector, force); > + else > + ret = connector_status_connected; > + } > + > + if (ret == -EDEADLK) { > + drm_modeset_backoff(&ctx); > + goto retry; > + } > + > + if (WARN_ON(ret < 0)) > + ret = connector_status_unknown; > + > + drm_modeset_drop_locks(&ctx); > + drm_modeset_acquire_fini(&ctx); > + > + return ret; > +} > + > +/** > + * drm_helper_probe_detect - probe connector status > + * @connector: connector to probe > + * @ctx: acquire_ctx, or NULL to let this function handle locking. > + * @force: Whether destructive probe operations should be performed. > + * > + * This function calls the detect callbacks of the connector. > + * This function returns drm_connector_status, or > + * if @ctx is set, it might also return -EDEADLK. > + */ > +int > +drm_helper_probe_detect(struct drm_connector *connector, > + struct drm_modeset_acquire_ctx *ctx, > + bool force) > +{ > + const struct drm_connector_helper_funcs *funcs = connector->helper_private; > + struct drm_device *dev = connector->dev; > + int ret; > + > + if (!ctx) > + return drm_helper_probe_detect_ctx(connector, force); > + > + ret = drm_modeset_lock(&dev->mode_config.connection_mutex, ctx); > + if (ret) > + return ret; > + > + if (funcs->detect_ctx) > + return funcs->detect_ctx(connector, ctx, force); > + else if (connector->funcs->detect) > + return connector->funcs->detect(connector, force); > + else > + return connector_status_connected; > } > +EXPORT_SYMBOL(drm_helper_probe_detect); > > /** > * drm_helper_probe_single_connector_modes - get complete set of display modes > @@ -239,15 +300,27 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, > struct drm_display_mode *mode; > const struct drm_connector_helper_funcs *connector_funcs = > connector->helper_private; > - int count = 0; > + int count = 0, ret; > int mode_flags = 0; > bool verbose_prune = true; > enum drm_connector_status old_status; > + struct drm_modeset_acquire_ctx ctx; > > WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); > > + drm_modeset_acquire_init(&ctx, 0); > + > DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, > connector->name); > + > +retry: > + ret = drm_modeset_lock(&dev->mode_config.connection_mutex, &ctx); > + if (ret == -EDEADLK) { > + drm_modeset_backoff(&ctx); > + goto retry; > + } else > + WARN_ON(ret < 0); > + > /* set all old modes to the stale state */ > list_for_each_entry(mode, &connector->modes, head) > mode->status = MODE_STALE; > @@ -263,7 +336,15 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, > if (connector->funcs->force) > connector->funcs->force(connector); > } else { > - connector->status = drm_connector_detect(connector, true); > + ret = drm_helper_probe_detect(connector, &ctx, true); > + > + if (ret == -EDEADLK) { > + drm_modeset_backoff(&ctx); > + goto retry; > + } else if (WARN(ret < 0, "Invalid return value %i for connector detection\n", ret)) > + ret = connector_status_unknown; > + > + connector->status = ret; > } > > /* > @@ -355,6 +436,9 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, > prune: > drm_mode_prune_invalid(dev, &connector->modes, verbose_prune); > > + drm_modeset_drop_locks(&ctx); > + drm_modeset_acquire_fini(&ctx); > + > if (list_empty(&connector->modes)) > return 0; > > @@ -440,7 +524,7 @@ static void output_poll_execute(struct work_struct *work) > > repoll = true; > > - connector->status = drm_connector_detect(connector, false); > + connector->status = drm_helper_probe_detect(connector, NULL, false); > if (old_status != connector->status) { > const char *old, *new; > > @@ -588,7 +672,7 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev) > > old_status = connector->status; > > - connector->status = drm_connector_detect(connector, false); > + connector->status = drm_helper_probe_detect(connector, NULL, false); > DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n", > connector->base.id, > connector->name, > diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c > index 8c82607294c6..2797bf37c3ac 100644 > --- a/drivers/gpu/drm/i915/intel_crt.c > +++ b/drivers/gpu/drm/i915/intel_crt.c > @@ -669,15 +669,16 @@ static const struct dmi_system_id intel_spurious_crt_detect[] = { > { } > }; > > -static enum drm_connector_status > -intel_crt_detect(struct drm_connector *connector, bool force) > +static int > +intel_crt_detect(struct drm_connector *connector, > + struct drm_modeset_acquire_ctx *ctx, > + bool force) > { > struct drm_i915_private *dev_priv = to_i915(connector->dev); > struct intel_crt *crt = intel_attached_crt(connector); > struct intel_encoder *intel_encoder = &crt->base; > - enum drm_connector_status status; > + int status, ret; > struct intel_load_detect_pipe tmp; > - struct drm_modeset_acquire_ctx ctx; > > DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force=%d\n", > connector->base.id, connector->name, > @@ -721,10 +722,9 @@ intel_crt_detect(struct drm_connector *connector, bool force) > goto out; > } > > - drm_modeset_acquire_init(&ctx, 0); > - > /* for pre-945g platforms use load detect */ > - if (intel_get_load_detect_pipe(connector, NULL, &tmp, &ctx)) { > + ret = intel_get_load_detect_pipe(connector, NULL, &tmp, ctx); > + if (ret > 0) { > if (intel_crt_detect_ddc(connector)) > status = connector_status_connected; > else if (INTEL_GEN(dev_priv) < 4) > @@ -734,12 +734,11 @@ intel_crt_detect(struct drm_connector *connector, bool force) > status = connector_status_disconnected; > else > status = connector_status_unknown; > - intel_release_load_detect_pipe(connector, &tmp, &ctx); > - } else > + intel_release_load_detect_pipe(connector, &tmp, ctx); > + } else if (ret == 0) > status = connector_status_unknown; > - > - drm_modeset_drop_locks(&ctx); > - drm_modeset_acquire_fini(&ctx); > + else if (ret < 0) > + status = ret; > > out: > intel_display_power_put(dev_priv, intel_encoder->power_domain); > @@ -811,7 +810,6 @@ void intel_crt_reset(struct drm_encoder *encoder) > > static const struct drm_connector_funcs intel_crt_connector_funcs = { > .dpms = drm_atomic_helper_connector_dpms, > - .detect = intel_crt_detect, > .fill_modes = drm_helper_probe_single_connector_modes, > .late_register = intel_connector_register, > .early_unregister = intel_connector_unregister, > @@ -823,6 +821,7 @@ static const struct drm_connector_funcs intel_crt_connector_funcs = { > }; > > static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = { > + .detect_ctx = intel_crt_detect, > .mode_valid = intel_crt_mode_valid, > .get_modes = intel_crt_get_modes, > }; > diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c > index bf81afe2748c..a76a8b040ccf 100644 > --- a/drivers/gpu/drm/i915/intel_display.c > +++ b/drivers/gpu/drm/i915/intel_display.c > @@ -9503,10 +9503,10 @@ static int intel_modeset_setup_plane_state(struct drm_atomic_state *state, > return 0; > } > > -bool intel_get_load_detect_pipe(struct drm_connector *connector, > - struct drm_display_mode *mode, > - struct intel_load_detect_pipe *old, > - struct drm_modeset_acquire_ctx *ctx) > +int intel_get_load_detect_pipe(struct drm_connector *connector, > + struct drm_display_mode *mode, > + struct intel_load_detect_pipe *old, > + struct drm_modeset_acquire_ctx *ctx) > { > struct intel_crtc *intel_crtc; > struct intel_encoder *intel_encoder = > @@ -9529,10 +9529,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, > > old->restore_state = NULL; > > -retry: > - ret = drm_modeset_lock(&config->connection_mutex, ctx); > - if (ret) > - goto fail; > + WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); > > /* > * Algorithm gets a little messy: > @@ -9682,10 +9679,8 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, > restore_state = NULL; > } > > - if (ret == -EDEADLK) { > - drm_modeset_backoff(ctx); > - goto retry; > - } > + if (ret == -EDEADLK) > + return ret; > > return false; > } > @@ -15116,6 +15111,7 @@ static void intel_enable_pipe_a(struct drm_device *dev) > struct drm_connector *crt = NULL; > struct intel_load_detect_pipe load_detect_temp; > struct drm_modeset_acquire_ctx *ctx = dev->mode_config.acquire_ctx; > + int ret; > > /* We can't just switch on the pipe A, we need to set things up with a > * proper mode and output configuration. As a gross hack, enable pipe A > @@ -15132,7 +15128,10 @@ static void intel_enable_pipe_a(struct drm_device *dev) > if (!crt) > return; > > - if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp, ctx)) > + ret = intel_get_load_detect_pipe(crt, NULL, &load_detect_temp, ctx); > + WARN(ret < 0, "All modeset mutexes are locked, but intel_get_load_detect_pipe failed\n"); > + > + if (ret > 0) > intel_release_load_detect_pipe(crt, &load_detect_temp, ctx); > } > > diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c > index fd96a6cf7326..68896d4742cb 100644 > --- a/drivers/gpu/drm/i915/intel_dp.c > +++ b/drivers/gpu/drm/i915/intel_dp.c > @@ -4566,8 +4566,9 @@ intel_dp_unset_edid(struct intel_dp *intel_dp) > intel_dp->has_audio = false; > } > > -static enum drm_connector_status > -intel_dp_long_pulse(struct intel_connector *intel_connector) > +static int > +intel_dp_long_pulse(struct intel_connector *intel_connector, > + struct drm_modeset_acquire_ctx *ctx) We don't really seem to need the ctx, but no harm in wiring this up either. > { > struct drm_connector *connector = &intel_connector->base; > struct intel_dp *intel_dp = intel_attached_dp(connector); > @@ -4635,14 +4636,7 @@ intel_dp_long_pulse(struct intel_connector *intel_connector) > status = connector_status_disconnected; > goto out; > } else if (connector->status == connector_status_connected) { > - /* > - * If display was connected already and is still connected > - * check links status, there has been known issues of > - * link loss triggerring long pulse!!!! > - */ > - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); WARN_ON(!modeset_locked) would be good here instead. Just in case we refactor and move this into e.g. the hpd handler. > intel_dp_check_link_status(intel_dp); > - drm_modeset_unlock(&dev->mode_config.connection_mutex); > goto out; > } > > @@ -4682,18 +4676,20 @@ intel_dp_long_pulse(struct intel_connector *intel_connector) > return status; > } > > -static enum drm_connector_status > -intel_dp_detect(struct drm_connector *connector, bool force) > +static int > +intel_dp_detect(struct drm_connector *connector, > + struct drm_modeset_acquire_ctx *ctx, > + bool force) > { > struct intel_dp *intel_dp = intel_attached_dp(connector); > - enum drm_connector_status status = connector->status; > + int status = connector->status; > > DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", > connector->base.id, connector->name); > > /* If full detect is not performed yet, do a full detect */ > if (!intel_dp->detect_done) > - status = intel_dp_long_pulse(intel_dp->attached_connector); > + status = intel_dp_long_pulse(intel_dp->attached_connector, ctx); > > intel_dp->detect_done = false; > > @@ -5014,7 +5010,6 @@ void intel_dp_encoder_reset(struct drm_encoder *encoder) > > static const struct drm_connector_funcs intel_dp_connector_funcs = { > .dpms = drm_atomic_helper_connector_dpms, > - .detect = intel_dp_detect, > .force = intel_dp_force, > .fill_modes = drm_helper_probe_single_connector_modes, > .set_property = intel_dp_set_property, > @@ -5027,6 +5022,7 @@ static const struct drm_connector_funcs intel_dp_connector_funcs = { > }; > > static const struct drm_connector_helper_funcs intel_dp_connector_helper_funcs = { > + .detect_ctx = intel_dp_detect, > .get_modes = intel_dp_get_modes, > .mode_valid = intel_dp_mode_valid, > }; > diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h > index 313fad059bfa..aaee3949a422 100644 > --- a/drivers/gpu/drm/i915/intel_drv.h > +++ b/drivers/gpu/drm/i915/intel_drv.h > @@ -1358,10 +1358,10 @@ int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp); > void vlv_wait_port_ready(struct drm_i915_private *dev_priv, > struct intel_digital_port *dport, > unsigned int expected_mask); > -bool intel_get_load_detect_pipe(struct drm_connector *connector, > - struct drm_display_mode *mode, > - struct intel_load_detect_pipe *old, > - struct drm_modeset_acquire_ctx *ctx); > +int intel_get_load_detect_pipe(struct drm_connector *connector, > + struct drm_display_mode *mode, > + struct intel_load_detect_pipe *old, > + struct drm_modeset_acquire_ctx *ctx); > void intel_release_load_detect_pipe(struct drm_connector *connector, > struct intel_load_detect_pipe *old, > struct drm_modeset_acquire_ctx *ctx); > diff --git a/drivers/gpu/drm/i915/intel_hotplug.c b/drivers/gpu/drm/i915/intel_hotplug.c > index 7d210097eefa..f1200272a699 100644 > --- a/drivers/gpu/drm/i915/intel_hotplug.c > +++ b/drivers/gpu/drm/i915/intel_hotplug.c > @@ -243,7 +243,8 @@ static bool intel_hpd_irq_event(struct drm_device *dev, > WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); > old_status = connector->status; > > - connector->status = connector->funcs->detect(connector, false); > + connector->status = drm_helper_probe_detect(connector, NULL, false); > + > if (old_status == connector->status) > return false; > > diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c > index 6ed1a3ce47b7..e077c2a9e694 100644 > --- a/drivers/gpu/drm/i915/intel_tv.c > +++ b/drivers/gpu/drm/i915/intel_tv.c > @@ -1315,8 +1315,10 @@ static void intel_tv_find_better_format(struct drm_connector *connector) > * Currently this always returns CONNECTOR_STATUS_UNKNOWN, as we need to be sure > * we have a pipe programmed in order to probe the TV. > */ > -static enum drm_connector_status > -intel_tv_detect(struct drm_connector *connector, bool force) > +static int > +intel_tv_detect(struct drm_connector *connector, > + struct drm_modeset_acquire_ctx *ctx, > + bool force) > { > struct drm_display_mode mode; > struct intel_tv *intel_tv = intel_attached_tv(connector); > @@ -1331,21 +1333,20 @@ intel_tv_detect(struct drm_connector *connector, bool force) > > if (force) { > struct intel_load_detect_pipe tmp; > - struct drm_modeset_acquire_ctx ctx; > + int ret; > > - drm_modeset_acquire_init(&ctx, 0); > + ret = intel_get_load_detect_pipe(connector, &mode, &tmp, ctx); > + if (ret < 0) > + return ret; > > - if (intel_get_load_detect_pipe(connector, &mode, &tmp, &ctx)) { > + if (ret > 0) { > type = intel_tv_detect_type(intel_tv, connector); > - intel_release_load_detect_pipe(connector, &tmp, &ctx); > + intel_release_load_detect_pipe(connector, &tmp, ctx); > status = type < 0 ? > connector_status_disconnected : > connector_status_connected; > } else > status = connector_status_unknown; > - > - drm_modeset_drop_locks(&ctx); > - drm_modeset_acquire_fini(&ctx); > } else > return connector->status; > > @@ -1516,7 +1517,6 @@ intel_tv_set_property(struct drm_connector *connector, struct drm_property *prop > > static const struct drm_connector_funcs intel_tv_connector_funcs = { > .dpms = drm_atomic_helper_connector_dpms, > - .detect = intel_tv_detect, > .late_register = intel_connector_register, > .early_unregister = intel_connector_unregister, > .destroy = intel_tv_destroy, > @@ -1528,6 +1528,7 @@ static const struct drm_connector_funcs intel_tv_connector_funcs = { > }; > > static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = { > + .detect_ctx = intel_tv_detect, > .mode_valid = intel_tv_mode_valid, > .get_modes = intel_tv_get_modes, > }; > diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h > index 941f57f311aa..8d36ab2b1666 100644 > --- a/include/drm/drm_connector.h > +++ b/include/drm/drm_connector.h > @@ -32,6 +32,7 @@ > struct drm_device; > > struct drm_connector_helper_funcs; > +struct drm_modeset_acquire_ctx; > struct drm_device; > struct drm_crtc; > struct drm_encoder; > @@ -378,6 +379,10 @@ struct drm_connector_funcs { > * the helper library vtable purely for historical reasons. The only DRM > * core entry point to probe connector state is @fill_modes. > * > + * Atomic drivers that might have state that shouldn't race against > + * atomic_check must use &drm_connector_helper_funcs.detect_ctx > + * instead. Nah, we already hold the lock. Instead: "Note that the helper library will already hold @drm_mode_config.connection_mutex. Drivers which need to grab additional locks to avoid races with concurrent modeset changes need to instead use &drm_connector_helper_funcs.detect.ctx." Also, this patch changes the samantics for @force and @get_modes. Please add there a new para to explain this, e.g. "To avoid races with concurrent modeset changes the helper libraries always call this with the &drm_mode_config.connection_mutex held." Also, it would be good if we could smash some more WARN_ON(!locked) over our get_modes functions, to make sure this won't break. We can do that later on in the follow-up patch series to fix up all the i915 connector state though. > + * > * RETURNS: > * > * drm_connector_status indicating the connector's status. > diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h > index 43505c7b2b3f..76e237bd989b 100644 > --- a/include/drm/drm_crtc_helper.h > +++ b/include/drm/drm_crtc_helper.h > @@ -67,6 +67,9 @@ int drm_helper_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, > int drm_helper_probe_single_connector_modes(struct drm_connector > *connector, uint32_t maxX, > uint32_t maxY); > +int drm_helper_probe_detect(struct drm_connector *connector, > + struct drm_modeset_acquire_ctx *ctx, > + bool force); > void drm_kms_helper_poll_init(struct drm_device *dev); > void drm_kms_helper_poll_fini(struct drm_device *dev); > bool drm_helper_hpd_irq_event(struct drm_device *dev); > diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h > index 091c42205667..b304950b402d 100644 > --- a/include/drm/drm_modeset_helper_vtables.h > +++ b/include/drm/drm_modeset_helper_vtables.h > @@ -754,6 +754,30 @@ struct drm_connector_helper_funcs { > int (*get_modes)(struct drm_connector *connector); > > /** > + * @detect_ctx: > + * > + * Check to see if anything is attached to the connector. The parameter > + * force is set to false whilst polling, true when checking the > + * connector due to a user request. force can be used by the driver to > + * avoid expensive, destructive operations during automated probing. > + * > + * This callback is optional, if not implemented the connector will be > + * considered as always being attached. > + * > + * This is the atomic version of &drm_connector_funcs.detect. > + * > + * ctx is always set, and connection_mutex is always locked. &drm_mode_config.connection_mutex > + * > + * RETURNS: > + * > + * drm_connector_status indicating the connector's status, &enum drm_connector_status > + * or the error code returned by drm_modeset_lock (-EDEADLK). drm_modeset_lock() Please build docs to double-check all the links work and point in the right direction :-) > + */ > + int (*detect_ctx)(struct drm_connector *connector, > + struct drm_modeset_acquire_ctx *ctx, > + bool force); > + > + /** > * @mode_valid: > * > * Callback to validate a mode for a connector, irrespective of the > -- > 2.7.4 > > -- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx