[PATCH] drm/probe-helper: don't lose hotplug event

Rob Clark robdclark at gmail.com
Wed Jan 21 13:40:31 PST 2015


On Wed, Jan 21, 2015 at 9:41 AM, Daniel Vetter <daniel.vetter at ffwll.ch> wrote:
> There's a race window (small for hpd, 10s large for polled outputs)
> where userspace could sneak in with an unrelated connnector probe
> ioctl call and eat the hotplug event (since neither the hpd nor the
> poll code see a state change).
>
> To avoid this, check whether the connector state changes in all other
> ->detect calls (in the current helper code that's only probe_single)
> and if that's the case, fire off a hotplug event. Note that we can't
> directly call the hotplug event handler, since that expects that no
> locks are held (due to reentrancy with the fb code to update the kms
> console).
>
> Also, this requires that drivers using the probe_single helper
> function set up the poll work. All current drivers do that already,
> and with the reworked hpd handling there'll be no downside to
> unconditionally setting up the poll work any more.
>
> v2: Review from Rob Clark
> - Don't bail out of the output poll work immediately if it's disabled
>   to make sure we deliver the delayed hoptplug events. Instead just
>   jump to the tail.
> - Don't scheduel the work when it's not set up. Would be a driver bug
>   since using probe helpers for anything dynamic without them
>   initialized makes them all noops.
>
> Reviewed-by: Chris Wilson <chris at chris-wilson.co.uk> (v1)
> Cc: Rob Clark <robdclark at gmail.com>
> Signed-off-by: Daniel Vetter <daniel.vetter at ffwll.ch>

Reviewed-by: Rob Clark <robdclark at gmail.com>

and with the monitor that started all this, plus the addition of:
http://lists.freedesktop.org/archives/dri-devel/2015-January/075901.html

Tested-by: Rob Clark <robdclark at gmail.com>

> ---
>  drivers/gpu/drm/drm_probe_helper.c | 36 ++++++++++++++++++++++++++++++++++--
>  include/drm/drm_crtc.h             |  1 +
>  2 files changed, 35 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
> index 2fbdcca7ca9a..33bf550a1d3f 100644
> --- a/drivers/gpu/drm/drm_probe_helper.c
> +++ b/drivers/gpu/drm/drm_probe_helper.c
> @@ -103,6 +103,7 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
>         int count = 0;
>         int mode_flags = 0;
>         bool verbose_prune = true;
> +       enum drm_connector_status old_status;
>
>         WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
>
> @@ -121,7 +122,33 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
>                 if (connector->funcs->force)
>                         connector->funcs->force(connector);
>         } else {
> +               old_status = connector->status;
> +
>                 connector->status = connector->funcs->detect(connector, true);
> +
> +               /*
> +                * Normally either the driver's hpd code or the poll loop should
> +                * pick up any changes and fire the hotplug event. But if
> +                * userspace sneaks in a probe, we might miss a change. Hence
> +                * check here, and if anything changed start the hotplug code.
> +                */
> +               if (old_status != connector->status) {
> +                       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n",
> +                                     connector->base.id,
> +                                     connector->name,
> +                                     old_status, connector->status);
> +
> +                       /*
> +                        * The hotplug event code might call into the fb
> +                        * helpers, and so expects that we do not hold any
> +                        * locks. Fire up the poll struct instead, it will
> +                        * disable itself again.
> +                        */
> +                       dev->mode_config.delayed_event = true;
> +                       if (dev->mode_config.poll_enabled)
> +                               schedule_delayed_work(&dev->mode_config.output_poll_work,
> +                                                     0);
> +               }
>         }
>
>         /* Re-enable polling in case the global poll config changed. */
> @@ -274,10 +301,14 @@ static void output_poll_execute(struct work_struct *work)
>         struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_work);
>         struct drm_connector *connector;
>         enum drm_connector_status old_status;
> -       bool repoll = false, changed = false;
> +       bool repoll = false, changed;
> +
> +       /* Pick up any changes detected by the probe functions. */
> +       changed = dev->mode_config.delayed_event;
> +       dev->mode_config.delayed_event = false;
>
>         if (!drm_kms_helper_poll)
> -               return;
> +               goto out;
>
>         mutex_lock(&dev->mode_config.mutex);
>         list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> @@ -319,6 +350,7 @@ static void output_poll_execute(struct work_struct *work)
>
>         mutex_unlock(&dev->mode_config.mutex);
>
> +out:
>         if (changed)
>                 drm_kms_helper_hotplug_event(dev);
>
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index f444263055c5..65da9fb939a7 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -1089,6 +1089,7 @@ struct drm_mode_config {
>         /* output poll support */
>         bool poll_enabled;
>         bool poll_running;
> +       bool delayed_event;
>         struct delayed_work output_poll_work;
>
>         /* pointers to standard properties */
> --
> 2.1.4
>


More information about the dri-devel mailing list