[PATCH] drm: Handle atomic state properly in kms getfoo ioctl
Sean Paul
seanpaul at chromium.org
Wed Nov 26 07:52:24 PST 2014
On Tue, Nov 25, 2014 at 5:50 PM, Daniel Vetter <daniel.vetter at ffwll.ch> wrote:
> So the problem with async commit (especially async modeset commit) is
> that the legacy pointers only get updated after the point of no
> return, in the async part of the modeset sequence. At least as
> implemented by the current helper functions. This is done in the
> set_routing_links function in drm_atomic_helper.c.
>
> Which also means that access isn't protected by locks but only
> coordinated by synchronizing with async workers. No problem thus far,
> until we lock at the getconnector/encoder ioctls.
>
> So fix this up by adding special cases for atomic drivers: For those
> we need to look at state objects. Unfortunately digging out the
> correct encoder->crtc link is a bit of work, so wrap this up in a
> helper function.
>
> Moving the assignments of connector->encoder and encoder->crtc earlier
> isn't a good idea because the point of the atomic helpers is that we
> stage the state updates. That way the disable functions can still
> inspect the links and rely upon them.
>
> v2: Extract full encoder->crtc lookup into helper (Rob).
>
> v3: Extract drm_connector_get_encoder too since - we need to always
> return state->best_encoder when there is a state otherwise we might
> return stale data if there's a pending async disable (and chase
> unlocked pointers, too). Same issue with encoder_get_crtc but there
> it's a bit more tricky to handle.
>
> Cc: Rob Clark <robdclark at gmail.com>
> Cc: Sean Paul <seanpaul at chromium.org>
> Signed-off-by: Daniel Vetter <daniel.vetter at intel.com>
My canary is still alive.
Reviewed-by: Sean Paul <seanpaul at chromium.org>
Lightly-Tested-by: Sean Paul <seanpaul at chromium.org>
> ---
> drivers/gpu/drm/drm_crtc.c | 49 +++++++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 46 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index 589a921d4313..d51b1c1f6726 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -1955,6 +1955,15 @@ static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode,
> return true;
> }
>
> +static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *connector)
> +{
> + /* For atomic drivers only state objects are synchronously updated and
> + * protected by modeset locks, so check those first. */
> + if (connector->state)
> + return connector->state->best_encoder;
> + return connector->encoder;
> +}
> +
> /**
> * drm_mode_getconnector - get connector configuration
> * @dev: drm device for the ioctl
> @@ -1973,6 +1982,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
> {
> struct drm_mode_get_connector *out_resp = data;
> struct drm_connector *connector;
> + struct drm_encoder *encoder;
> struct drm_display_mode *mode;
> int mode_count = 0;
> int props_count = 0;
> @@ -2028,8 +2038,10 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
> out_resp->subpixel = connector->display_info.subpixel_order;
> out_resp->connection = connector->status;
> drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> - if (connector->encoder)
> - out_resp->encoder_id = connector->encoder->base.id;
> +
> + encoder = drm_connector_get_encoder(connector);
> + if (encoder)
> + out_resp->encoder_id = encoder->base.id;
> else
> out_resp->encoder_id = 0;
> drm_modeset_unlock(&dev->mode_config.connection_mutex);
> @@ -2099,6 +2111,33 @@ out:
> return ret;
> }
>
> +static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder)
> +{
> + struct drm_connector *connector;
> + struct drm_device *dev = encoder->dev;
> + bool uses_atomic = false;
> +
> + /* For atomic drivers only state objects are synchronously updated and
> + * protected by modeset locks, so check those first. */
> + list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> + if (!connector->state)
> + continue;
> +
> + uses_atomic = true;
> +
> + if (connector->state->best_encoder != encoder)
> + continue;
> +
> + return connector->state->crtc;
> + }
> +
> + /* Don't return stale data (e.g. pending async disable). */
> + if (uses_atomic)
> + return NULL;
> +
> + return encoder->crtc;
> +}
> +
> /**
> * drm_mode_getencoder - get encoder configuration
> * @dev: drm device for the ioctl
> @@ -2117,6 +2156,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
> {
> struct drm_mode_get_encoder *enc_resp = data;
> struct drm_encoder *encoder;
> + struct drm_crtc *crtc;
>
> if (!drm_core_check_feature(dev, DRIVER_MODESET))
> return -EINVAL;
> @@ -2126,7 +2166,10 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
> return -ENOENT;
>
> drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
> - if (encoder->crtc)
> + crtc = drm_encoder_get_crtc(encoder);
> + if (crtc)
> + enc_resp->crtc_id = crtc->base.id;
> + else if (encoder->crtc)
> enc_resp->crtc_id = encoder->crtc->base.id;
> else
> enc_resp->crtc_id = 0;
> --
> 2.1.1
>
More information about the dri-devel
mailing list