summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2016-12-14 00:08:10 +0100
committerDaniel Vetter <daniel.vetter@ffwll.ch>2016-12-18 14:36:43 +0100
commit91eefc05f0ac71902906b2058360e61bd25137fe (patch)
tree33fdaad18a9ffc544e68466ab5e64949b6d1a962
parente73ab00e9a0f1731f34d0620a9c55f5c30c4ad4e (diff)
downloadlinux-stable-91eefc05f0ac71902906b2058360e61bd25137fe.tar.gz
linux-stable-91eefc05f0ac71902906b2058360e61bd25137fe.tar.bz2
linux-stable-91eefc05f0ac71902906b2058360e61bd25137fe.zip
drm: Tighten locking in drm_mode_getconnector
- Modeset state needs mode_config->connection mutex, that covers figuring out the encoder, and reading properties (since in the atomic case those need to look at connector->state). - Don't hold any locks for stuff that's invariant (i.e. possible connectors). - Same for connector lookup and unref, those don't need any locks. - And finally the probe stuff is only protected by mode_config->mutex. While at it updated the kerneldoc for these fields in drm_connector and add docs explaining what's protected by which locks. Reviewed-by: Sean Paul <seanpaul@chromium.org> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com> Link: http://patchwork.freedesktop.org/patch/msgid/20161213230814.19598-10-daniel.vetter@ffwll.ch
-rw-r--r--drivers/gpu/drm/drm_connector.c92
-rw-r--r--include/drm/drm_connector.h23
2 files changed, 63 insertions, 52 deletions
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index b40401278077..3115db2ae6b1 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -1163,43 +1163,65 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
- mutex_lock(&dev->mode_config.mutex);
-
connector = drm_connector_lookup(dev, out_resp->connector_id);
- if (!connector) {
- ret = -ENOENT;
- goto out_unlock;
- }
+ if (!connector)
+ return -ENOENT;
+
+ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+ encoder = drm_connector_get_encoder(connector);
+ if (encoder)
+ out_resp->encoder_id = encoder->base.id;
+ else
+ out_resp->encoder_id = 0;
+
+ ret = drm_mode_object_get_properties(&connector->base, file_priv->atomic,
+ (uint32_t __user *)(unsigned long)(out_resp->props_ptr),
+ (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr),
+ &out_resp->count_props);
+ drm_modeset_unlock(&dev->mode_config.connection_mutex);
+ if (ret)
+ goto out_unref;
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++)
if (connector->encoder_ids[i] != 0)
encoders_count++;
+ if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
+ copied = 0;
+ encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr);
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+ if (connector->encoder_ids[i] != 0) {
+ if (put_user(connector->encoder_ids[i],
+ encoder_ptr + copied)) {
+ ret = -EFAULT;
+ goto out_unref;
+ }
+ copied++;
+ }
+ }
+ }
+ out_resp->count_encoders = encoders_count;
+
+ out_resp->connector_id = connector->base.id;
+ out_resp->connector_type = connector->connector_type;
+ out_resp->connector_type_id = connector->connector_type_id;
+
+ mutex_lock(&dev->mode_config.mutex);
if (out_resp->count_modes == 0) {
connector->funcs->fill_modes(connector,
dev->mode_config.max_width,
dev->mode_config.max_height);
}
- /* delayed so we get modes regardless of pre-fill_modes state */
- list_for_each_entry(mode, &connector->modes, head)
- if (drm_mode_expose_to_userspace(mode, file_priv))
- mode_count++;
-
- out_resp->connector_id = connector->base.id;
- out_resp->connector_type = connector->connector_type;
- out_resp->connector_type_id = connector->connector_type_id;
out_resp->mm_width = connector->display_info.width_mm;
out_resp->mm_height = connector->display_info.height_mm;
out_resp->subpixel = connector->display_info.subpixel_order;
out_resp->connection = connector->status;
- drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
- encoder = drm_connector_get_encoder(connector);
- if (encoder)
- out_resp->encoder_id = encoder->base.id;
- else
- out_resp->encoder_id = 0;
+ /* delayed so we get modes regardless of pre-fill_modes state */
+ list_for_each_entry(mode, &connector->modes, head)
+ if (drm_mode_expose_to_userspace(mode, file_priv))
+ mode_count++;
/*
* This ioctl is called twice, once to determine how much space is
@@ -1222,36 +1244,10 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
}
}
out_resp->count_modes = mode_count;
-
- ret = drm_mode_object_get_properties(&connector->base, file_priv->atomic,
- (uint32_t __user *)(unsigned long)(out_resp->props_ptr),
- (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr),
- &out_resp->count_props);
- if (ret)
- goto out;
-
- if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
- copied = 0;
- encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr);
- for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
- if (connector->encoder_ids[i] != 0) {
- if (put_user(connector->encoder_ids[i],
- encoder_ptr + copied)) {
- ret = -EFAULT;
- goto out;
- }
- copied++;
- }
- }
- }
- out_resp->count_encoders = encoders_count;
-
out:
- drm_modeset_unlock(&dev->mode_config.connection_mutex);
-
- drm_connector_unreference(connector);
-out_unlock:
mutex_unlock(&dev->mode_config.mutex);
+out_unref:
+ drm_connector_unreference(connector);
return ret;
}
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index e4f23cc2aaab..b018e7684e9b 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -563,9 +563,6 @@ struct drm_cmdline_mode {
* @interlace_allowed: can this connector handle interlaced modes?
* @doublescan_allowed: can this connector handle doublescan?
* @stereo_allowed: can this connector handle stereo modes?
- * @modes: modes available on this connector (from fill_modes() + user)
- * @status: one of the drm_connector_status enums (connected, not, or unknown)
- * @probed_modes: list of modes derived directly from the display
* @funcs: connector control functions
* @edid_blob_ptr: DRM property containing EDID if present
* @properties: property tracking for this connector
@@ -635,11 +632,27 @@ struct drm_connector {
* Protected by @mutex.
*/
bool registered;
+
+ /**
+ * @modes:
+ * Modes available on this connector (from fill_modes() + user).
+ * Protected by dev->mode_config.mutex.
+ */
struct list_head modes; /* list of modes on this connector */
+ /**
+ * @status:
+ * One of the drm_connector_status enums (connected, not, or unknown).
+ * Protected by dev->mode_config.mutex.
+ */
enum drm_connector_status status;
- /* these are modes added by probing with DDC or the BIOS */
+ /**
+ * @probed_modes:
+ * These are modes added by probing with DDC or the BIOS, before
+ * filtering is applied. Used by the probe helpers.Protected by
+ * dev->mode_config.mutex.
+ */
struct list_head probed_modes;
/**
@@ -648,6 +661,8 @@ struct drm_connector {
* flat panels in embedded systems, the driver should initialize the
* display_info.width_mm and display_info.height_mm fields with the
* physical size of the display.
+ *
+ * Protected by dev->mode_config.mutex.
*/
struct drm_display_info display_info;
const struct drm_connector_funcs *funcs;