Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm: Split connection_mutex out of mode_config.mutex (v3)

After the split-out of crtc locks from the big mode_config.mutex
there's still two major areas it protects:
- Various connector probe states, like connector->status, EDID
properties, probed mode lists and similar information.
- The links from connector->encoder and encoder->crtc and other
modeset-relevant connector state (e.g. properties which control the
panel fitter).

The later is used by modeset operations. But they don't really care
about the former since it's allowed to e.g. enable a disconnected VGA
output or with a mode not in the probed list.

Thus far this hasn't been a problem, but for the atomic modeset
conversion Rob Clark needs to convert all modeset relevant locks into
w/w locks. This is required because the order of acquisition is
determined by how userspace supplies the atomic modeset data. This has
run into troubles in the detect path since the i915 load detect code
needs _both_ protections offered by the mode_config.mutex: It updates
probe state and it needs to change the modeset configuration to enable
the temporary load detect pipe.

The big deal here is that for the probe/detect users of this lock a
plain mutex fits best, but for atomic modesets we really want a w/w
mutex. To fix this lets split out a new connection_mutex lock for the
modeset relevant parts.

For simplicity I've decided to only add one additional lock for all
connector/encoder links and modeset configuration states. We have
piles of different modeset objects in addition to those (like bridges
or panels), so adding per-object locks would be much more effort.

Also, we're guaranteed (at least for now) to do a full modeset if we
need to acquire this lock. Which means that fine-grained locking is
fairly irrelevant compared to the amount of time the full modeset will
take.

I've done a full audit, and there's just a few things that justify
special focus:
- Locking in drm_sysfs.c is almost completely absent. We should
sprinkle mode_config.connection_mutex over this file a bit, but
since it already lacks mode_config.mutex this patch wont make the
situation any worse. This is material for a follow-up patch.

- omap has a omap_framebuffer_flush function which walks the
connector->encoder->crtc links and is called from many contexts.
Some look like they don't acquire mode_config.mutex, so this is
already racy. Again fixing this is material for a separate patch.

- The radeon hot_plug function to retrain DP links looks at
connector->dpms. Currently this happens without any locking, so is
already racy. I think radeon_hotplug_work_func should gain
mutex_lock/unlock calls for the mode_config.connection_mutex.

- Same applies to i915's intel_dp_hot_plug. But again, this is already
racy.

- i915 load_detect code needs to acquire this lock. Which means the
w/w dance due to Rob's work will be nicely contained to _just_ this
function.

I've added fixme comments everywhere where it looks suspicious but in
the sysfs code. After a quick irc discussion with Dave Airlie it
sounds like the lack of locking in there is due to sysfs cleanup fun
at module unload.

v1: original (only compile tested)

v2: missing mutex_init(), etc (from Rob Clark)

v3: i915 needs more care in the conversion:
- Protect the edp pp logic with the connection_mutex.
- Use connection_mutex in the backlight code due to
get_pipe_from_connector.
- Use drm_modeset_lock_all in suspend/resume paths.
- Update lock checks in the overlay code.

Cc: Alex Deucher <alexdeucher@gmail.com>
Cc: Rob Clark <robdclark@gmail.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Reviewed-by: Rob Clark <robdclark@gmail.com>

authored by

Daniel Vetter and committed by
Dave Airlie
6e9f798d 8291272a

+54 -25
+8
drivers/gpu/drm/drm_crtc.c
··· 54 54 55 55 mutex_lock(&dev->mode_config.mutex); 56 56 57 + mutex_lock(&dev->mode_config.connection_mutex); 58 + 57 59 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 58 60 mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex); 59 61 } ··· 73 71 74 72 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 75 73 mutex_unlock(&crtc->mutex); 74 + 75 + mutex_unlock(&dev->mode_config.connection_mutex); 76 76 77 77 mutex_unlock(&dev->mode_config.mutex); 78 78 } ··· 97 93 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 98 94 WARN_ON(!mutex_is_locked(&crtc->mutex)); 99 95 96 + WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex)); 100 97 WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); 101 98 } 102 99 EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); ··· 1798 1793 DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id); 1799 1794 1800 1795 mutex_lock(&dev->mode_config.mutex); 1796 + mutex_lock(&dev->mode_config.connection_mutex); 1801 1797 1802 1798 connector = drm_connector_find(dev, out_resp->connector_id); 1803 1799 if (!connector) { ··· 1897 1891 out_resp->count_encoders = encoders_count; 1898 1892 1899 1893 out: 1894 + mutex_unlock(&dev->mode_config.connection_mutex); 1900 1895 mutex_unlock(&dev->mode_config.mutex); 1901 1896 1902 1897 return ret; ··· 4647 4640 void drm_mode_config_init(struct drm_device *dev) 4648 4641 { 4649 4642 mutex_init(&dev->mode_config.mutex); 4643 + mutex_init(&dev->mode_config.connection_mutex); 4650 4644 mutex_init(&dev->mode_config.idr_mutex); 4651 4645 mutex_init(&dev->mode_config.fb_lock); 4652 4646 INIT_LIST_HEAD(&dev->mode_config.fb_list);
+2
drivers/gpu/drm/drm_crtc_helper.c
··· 89 89 struct drm_device *dev = encoder->dev; 90 90 91 91 WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); 92 + WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex)); 93 + 92 94 list_for_each_entry(connector, &dev->mode_config.connector_list, head) 93 95 if (connector->encoder == encoder) 94 96 return true;
+2
drivers/gpu/drm/drm_edid.c
··· 3304 3304 struct drm_connector *connector; 3305 3305 struct drm_device *dev = encoder->dev; 3306 3306 3307 + WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); 3308 + 3307 3309 list_for_each_entry(connector, &dev->mode_config.connector_list, head) 3308 3310 if (connector->encoder == encoder && connector->eld[0]) 3309 3311 return connector;
+7
drivers/gpu/drm/drm_plane_helper.c
··· 54 54 struct drm_connector *connector; 55 55 int count = 0; 56 56 57 + /* 58 + * Note: Once we change the plane hooks to more fine-grained locking we 59 + * need to grab the connection_mutex here to be able to make these 60 + * checks. 61 + */ 62 + WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex)); 63 + 57 64 list_for_each_entry(connector, &dev->mode_config.connector_list, head) 58 65 if (connector->encoder && connector->encoder->crtc == crtc) { 59 66 if (connector_list != NULL && count < num_connectors)
+2 -2
drivers/gpu/drm/i915/i915_debugfs.c
··· 2609 2609 2610 2610 *source = INTEL_PIPE_CRC_SOURCE_PIPE; 2611 2611 2612 - mutex_lock(&dev->mode_config.mutex); 2612 + drm_modeset_lock_all(dev); 2613 2613 list_for_each_entry(encoder, &dev->mode_config.encoder_list, 2614 2614 base.head) { 2615 2615 if (!encoder->base.crtc) ··· 2645 2645 break; 2646 2646 } 2647 2647 } 2648 - mutex_unlock(&dev->mode_config.mutex); 2648 + drm_modeset_unlock_all(dev); 2649 2649 2650 2650 return ret; 2651 2651 }
+2 -4
drivers/gpu/drm/i915/i915_drv.c
··· 533 533 * Disable CRTCs directly since we want to preserve sw state 534 534 * for _thaw. 535 535 */ 536 - mutex_lock(&dev->mode_config.mutex); 536 + drm_modeset_lock_all(dev); 537 537 for_each_crtc(dev, crtc) { 538 - mutex_lock(&crtc->mutex); 539 538 dev_priv->display.crtc_disable(crtc); 540 - mutex_unlock(&crtc->mutex); 541 539 } 542 - mutex_unlock(&dev->mode_config.mutex); 540 + drm_modeset_unlock_all(dev); 543 541 544 542 intel_modeset_suspend_hw(dev); 545 543 }
+12 -4
drivers/gpu/drm/i915/intel_display.c
··· 8323 8323 connector->base.id, connector->name, 8324 8324 encoder->base.id, encoder->name); 8325 8325 8326 + mutex_lock(&dev->mode_config.connection_mutex); 8327 + 8326 8328 /* 8327 8329 * Algorithm gets a little messy: 8328 8330 * ··· 8367 8365 */ 8368 8366 if (!crtc) { 8369 8367 DRM_DEBUG_KMS("no pipe available for load-detect\n"); 8370 - return false; 8368 + goto fail_unlock_connector; 8371 8369 } 8372 8370 8373 8371 mutex_lock(&crtc->mutex); ··· 8421 8419 else 8422 8420 intel_crtc->new_config = NULL; 8423 8421 mutex_unlock(&crtc->mutex); 8422 + fail_unlock_connector: 8423 + mutex_unlock(&dev->mode_config.connection_mutex); 8424 + 8424 8425 return false; 8425 8426 } 8426 8427 ··· 8453 8448 } 8454 8449 8455 8450 mutex_unlock(&crtc->mutex); 8451 + mutex_unlock(&connector->dev->mode_config.connection_mutex); 8456 8452 return; 8457 8453 } 8458 8454 ··· 8462 8456 connector->funcs->dpms(connector, old->dpms_mode); 8463 8457 8464 8458 mutex_unlock(&crtc->mutex); 8459 + mutex_unlock(&connector->dev->mode_config.connection_mutex); 8465 8460 } 8466 8461 8467 8462 static int i9xx_pll_refclk(struct drm_device *dev, ··· 10993 10986 enum pipe intel_get_pipe_from_connector(struct intel_connector *connector) 10994 10987 { 10995 10988 struct drm_encoder *encoder = connector->base.encoder; 10989 + struct drm_device *dev = connector->base.dev; 10996 10990 10997 - WARN_ON(!mutex_is_locked(&connector->base.dev->mode_config.mutex)); 10991 + WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex)); 10998 10992 10999 10993 if (!encoder) 11000 10994 return INVALID_PIPE; ··· 11764 11756 /* Just in case the BIOS is doing something questionable. */ 11765 11757 intel_disable_fbc(dev); 11766 11758 11767 - mutex_lock(&dev->mode_config.mutex); 11759 + drm_modeset_lock_all(dev); 11768 11760 intel_modeset_setup_hw_state(dev, false); 11769 - mutex_unlock(&dev->mode_config.mutex); 11761 + drm_modeset_unlock_all(dev); 11770 11762 11771 11763 for_each_intel_crtc(dev, crtc) { 11772 11764 if (!crtc->active)
+8 -7
drivers/gpu/drm/i915/intel_dp.c
··· 1154 1154 u32 pp; 1155 1155 u32 pp_stat_reg, pp_ctrl_reg; 1156 1156 1157 - WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); 1157 + WARN_ON(!mutex_is_locked(&dev->mode_config.connection_mutex)); 1158 1158 1159 1159 if (!intel_dp->want_panel_vdd && edp_have_panel_vdd(intel_dp)) { 1160 1160 struct intel_digital_port *intel_dig_port = ··· 1191 1191 struct intel_dp, panel_vdd_work); 1192 1192 struct drm_device *dev = intel_dp_to_dev(intel_dp); 1193 1193 1194 - mutex_lock(&dev->mode_config.mutex); 1194 + mutex_lock(&dev->mode_config.connection_mutex); 1195 1195 edp_panel_vdd_off_sync(intel_dp); 1196 - mutex_unlock(&dev->mode_config.mutex); 1196 + mutex_unlock(&dev->mode_config.connection_mutex); 1197 1197 } 1198 1198 1199 1199 static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync) ··· 3235 3235 u8 sink_irq_vector; 3236 3236 u8 link_status[DP_LINK_STATUS_SIZE]; 3237 3237 3238 + /* FIXME: This access isn't protected by any locks. */ 3238 3239 if (!intel_encoder->connectors_active) 3239 3240 return; 3240 3241 ··· 3666 3665 drm_encoder_cleanup(encoder); 3667 3666 if (is_edp(intel_dp)) { 3668 3667 cancel_delayed_work_sync(&intel_dp->panel_vdd_work); 3669 - mutex_lock(&dev->mode_config.mutex); 3668 + mutex_lock(&dev->mode_config.connection_mutex); 3670 3669 edp_panel_vdd_off_sync(intel_dp); 3671 - mutex_unlock(&dev->mode_config.mutex); 3670 + mutex_unlock(&dev->mode_config.connection_mutex); 3672 3671 } 3673 3672 kfree(intel_dig_port); 3674 3673 } ··· 4247 4246 drm_dp_aux_unregister_i2c_bus(&intel_dp->aux); 4248 4247 if (is_edp(intel_dp)) { 4249 4248 cancel_delayed_work_sync(&intel_dp->panel_vdd_work); 4250 - mutex_lock(&dev->mode_config.mutex); 4249 + mutex_lock(&dev->mode_config.connection_mutex); 4251 4250 edp_panel_vdd_off_sync(intel_dp); 4252 - mutex_unlock(&dev->mode_config.mutex); 4251 + mutex_unlock(&dev->mode_config.connection_mutex); 4253 4252 } 4254 4253 drm_sysfs_connector_remove(connector); 4255 4254 drm_connector_cleanup(connector);
+2 -2
drivers/gpu/drm/i915/intel_opregion.c
··· 410 410 if (bclp > 255) 411 411 return ASLC_BACKLIGHT_FAILED; 412 412 413 - mutex_lock(&dev->mode_config.mutex); 413 + mutex_lock(&dev->mode_config.connection_mutex); 414 414 415 415 /* 416 416 * Update backlight on all connectors that support backlight (usually ··· 421 421 intel_panel_set_backlight(intel_connector, bclp, 255); 422 422 iowrite32(DIV_ROUND_UP(bclp * 100, 255) | ASLE_CBLV_VALID, &asle->cblv); 423 423 424 - mutex_unlock(&dev->mode_config.mutex); 424 + mutex_unlock(&dev->mode_config.connection_mutex); 425 425 426 426 427 427 return 0;
+2 -2
drivers/gpu/drm/i915/intel_overlay.c
··· 688 688 u32 swidth, swidthsw, sheight, ostride; 689 689 690 690 BUG_ON(!mutex_is_locked(&dev->struct_mutex)); 691 - BUG_ON(!mutex_is_locked(&dev->mode_config.mutex)); 691 + BUG_ON(!mutex_is_locked(&dev->mode_config.connection_mutex)); 692 692 BUG_ON(!overlay); 693 693 694 694 ret = intel_overlay_release_old_vid(overlay); ··· 793 793 int ret; 794 794 795 795 BUG_ON(!mutex_is_locked(&dev->struct_mutex)); 796 - BUG_ON(!mutex_is_locked(&dev->mode_config.mutex)); 796 + BUG_ON(!mutex_is_locked(&dev->mode_config.connection_mutex)); 797 797 798 798 ret = intel_overlay_recover_from_interrupt(overlay); 799 799 if (ret != 0)
+4 -4
drivers/gpu/drm/i915/intel_panel.c
··· 876 876 struct intel_connector *connector = bl_get_data(bd); 877 877 struct drm_device *dev = connector->base.dev; 878 878 879 - mutex_lock(&dev->mode_config.mutex); 879 + mutex_lock(&dev->mode_config.connection_mutex); 880 880 DRM_DEBUG_KMS("updating intel_backlight, brightness=%d/%d\n", 881 881 bd->props.brightness, bd->props.max_brightness); 882 882 intel_panel_set_backlight(connector, bd->props.brightness, 883 883 bd->props.max_brightness); 884 - mutex_unlock(&dev->mode_config.mutex); 884 + mutex_unlock(&dev->mode_config.connection_mutex); 885 885 return 0; 886 886 } 887 887 ··· 893 893 int ret; 894 894 895 895 intel_runtime_pm_get(dev_priv); 896 - mutex_lock(&dev->mode_config.mutex); 896 + mutex_lock(&dev->mode_config.connection_mutex); 897 897 ret = intel_panel_get_backlight(connector); 898 - mutex_unlock(&dev->mode_config.mutex); 898 + mutex_unlock(&dev->mode_config.connection_mutex); 899 899 intel_runtime_pm_put(dev_priv); 900 900 901 901 return ret;
+1
drivers/gpu/drm/omapdrm/omap_fb.c
··· 346 346 347 347 VERB("flush: %d,%d %dx%d, fb=%p", x, y, w, h, fb); 348 348 349 + /* FIXME: This is racy - no protection against modeset config changes. */ 349 350 while ((connector = omap_framebuffer_get_next_connector(fb, connector))) { 350 351 /* only consider connectors that are part of a chain */ 351 352 if (connector->encoder && connector->encoder->crtc) {
+1
drivers/gpu/drm/radeon/radeon_connectors.c
··· 48 48 radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd); 49 49 50 50 /* if the connector is already off, don't turn it back on */ 51 + /* FIXME: This access isn't protected by any locks. */ 51 52 if (connector->dpms != DRM_MODE_DPMS_ON) 52 53 return; 53 54
+1
include/drm/drm_crtc.h
··· 738 738 */ 739 739 struct drm_mode_config { 740 740 struct mutex mutex; /* protects configuration (mode lists etc.) */ 741 + struct mutex connection_mutex; /* protects connector->encoder and encoder->crtc links */ 741 742 struct mutex idr_mutex; /* for IDR management */ 742 743 struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */ 743 744 /* this is limited to one for now */