Among other potential issues, this fixes a race between output polling and status_show() that could cause a load detection false positive with the nouveau driver.
Signed-off-by: Francisco Jerez <currojerez at riseup.net> --- drivers/gpu/drm/drm_sysfs.c | 71 ++++++++++++++++++++++++++++--------------- 1 files changed, 46 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 85da4c4..25a9d9e 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -157,9 +157,13 @@ static ssize_t status_show(struct device *device, char *buf) { struct drm_connector *connector = to_drm_connector(device); + struct drm_device *dev = connector->dev; enum drm_connector_status status; + mutex_lock(&dev->mode_config.mutex); status = connector->funcs->detect(connector, true); + mutex_unlock(&dev->mode_config.mutex); + return snprintf(buf, PAGE_SIZE, "%s\n", drm_get_connector_status_name(status)); } @@ -173,9 +177,11 @@ static ssize_t dpms_show(struct device *device, uint64_t dpms_status; int ret; + mutex_lock(&dev->mode_config.mutex); ret = drm_connector_property_get_value(connector, dev->mode_config.dpms_property, &dpms_status); + mutex_unlock(&dev->mode_config.mutex); if (ret) return 0; @@ -199,25 +205,27 @@ static ssize_t edid_show(struct file *filp, struct kobject *kobj, { struct device *connector_dev = container_of(kobj, struct device, kobj); struct drm_connector *connector = to_drm_connector(connector_dev); + struct drm_device *dev = connector->dev; unsigned char *edid; - size_t size; + size_t size, n = 0; + mutex_lock(&dev->mode_config.mutex); if (!connector->edid_blob_ptr) - return 0; + goto out; edid = connector->edid_blob_ptr->data; size = connector->edid_blob_ptr->length; if (!edid) - return 0; + goto out; if (off >= size) - return 0; - - if (off + count > size) - count = size - off; - memcpy(buf, edid + off, count); + goto out; - return count; + n = min(count, size - (size_t)off); + memcpy(buf, edid + off, n); +out: + mutex_unlock(&dev->mode_config.mutex); + return n; } static ssize_t modes_show(struct device *device, @@ -225,13 +233,16 @@ static ssize_t modes_show(struct device *device, char *buf) { struct drm_connector *connector = to_drm_connector(device); + struct drm_device *dev = connector->dev; struct drm_display_mode *mode; int written = 0; + mutex_lock(&dev->mode_config.mutex); list_for_each_entry(mode, &connector->modes, head) { written += snprintf(buf + written, PAGE_SIZE - written, "%s\n", mode->name); } + mutex_unlock(&dev->mode_config.mutex); return written; } @@ -245,7 +256,9 @@ static ssize_t subconnector_show(struct device *device, struct drm_property *prop = NULL; uint64_t subconnector; int is_tv = 0; - int ret; + int ret = 0; + + mutex_lock(&dev->mode_config.mutex); switch (connector->connector_type) { case DRM_MODE_CONNECTOR_DVII: @@ -260,21 +273,24 @@ static ssize_t subconnector_show(struct device *device, break; default: DRM_ERROR("Wrong connector type for this property\n"); - return 0; + goto out; } if (!prop) { DRM_ERROR("Unable to find subconnector property\n"); - return 0; + goto out; } ret = drm_connector_property_get_value(connector, prop, &subconnector); if (ret) - return 0; + goto out; - return snprintf(buf, PAGE_SIZE, "%s", is_tv ? - drm_get_tv_subconnector_name((int)subconnector) : - drm_get_dvi_i_subconnector_name((int)subconnector)); + ret = snprintf(buf, PAGE_SIZE, "%s", is_tv ? + drm_get_tv_subconnector_name((int)subconnector) : + drm_get_dvi_i_subconnector_name((int)subconnector)); +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; } static ssize_t select_subconnector_show(struct device *device, @@ -286,7 +302,9 @@ static ssize_t select_subconnector_show(struct device *device, struct drm_property *prop = NULL; uint64_t subconnector; int is_tv = 0; - int ret; + int ret = 0; + + mutex_lock(&dev->mode_config.mutex); switch (connector->connector_type) { case DRM_MODE_CONNECTOR_DVII: @@ -301,21 +319,24 @@ static ssize_t select_subconnector_show(struct device *device, break; default: DRM_ERROR("Wrong connector type for this property\n"); - return 0; + goto out; } if (!prop) { DRM_ERROR("Unable to find select subconnector property\n"); - return 0; + goto out; } ret = drm_connector_property_get_value(connector, prop, &subconnector); if (ret) - return 0; + goto out; - return snprintf(buf, PAGE_SIZE, "%s", is_tv ? - drm_get_tv_select_name((int)subconnector) : - drm_get_dvi_i_select_name((int)subconnector)); + ret = snprintf(buf, PAGE_SIZE, "%s", is_tv ? + drm_get_tv_select_name((int)subconnector) : + drm_get_dvi_i_select_name((int)subconnector)); +out: + mutex_unlock(&dev->mode_config.mutex); + return ret; } static struct device_attribute connector_attrs[] = { -- 1.6.4.4