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@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[] = {
Signed-off-by: Francisco Jerez currojerez@riseup.net --- drivers/gpu/drm/drm_fb_helper.c | 14 ++++++++++++-- 1 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 6a5e403..4d608b7 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1449,6 +1449,8 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) struct drm_device *dev = fb_helper->dev; int count = 0;
+ mutex_lock(&dev->mode_config.mutex); + /* disable all the possible outputs/crtcs before entering KMS mode */ drm_helper_disable_unused_functions(fb_helper->dev);
@@ -1465,19 +1467,23 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) } drm_setup_crtcs(fb_helper);
+ mutex_unlock(&dev->mode_config.mutex); + return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); } EXPORT_SYMBOL(drm_fb_helper_initial_config);
bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) { + struct drm_device *dev = fb_helper->dev; int count = 0; u32 max_width, max_height, bpp_sel; bool bound = false, crtcs_bound = false; struct drm_crtc *crtc;
+ mutex_lock(&dev->mode_config.mutex); if (!fb_helper->fb) - return false; + goto fail;
list_for_each_entry(crtc, &fb_helper->dev->mode_config.crtc_list, head) { if (crtc->fb) @@ -1488,7 +1494,7 @@ bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
if (!bound && crtcs_bound) { fb_helper->delayed_hotplug = true; - return false; + goto fail; } DRM_DEBUG_KMS("\n");
@@ -1500,7 +1506,11 @@ bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) max_height); drm_setup_crtcs(fb_helper);
+ mutex_unlock(&dev->mode_config.mutex); return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); +fail: + mutex_unlock(&dev->mode_config.mutex); + return false; } EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
dri-devel@lists.freedesktop.org