diff options
| author | Chris Wilson <chris@chris-wilson.co.uk> | 2011-04-22 06:03:57 -0400 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2011-05-15 22:01:43 -0400 |
| commit | 752d2635ebb12b6122ba05775f7d1ccfef14b275 (patch) | |
| tree | 787f76b453e15a03e7db938eaace598dc36273eb | |
| parent | 8eea1be174a1ea4b86323167bbadc8a6abdca613 (diff) | |
drm: Take lock around probes for drm_fb_helper_hotplug_event
We need to hold the dev->mode_config.mutex whilst detecting the output
status. But we also need to drop it for the call into
drm_fb_helper_single_fb_probe(), which indirectly acquires the lock when
attaching the fbcon.
Failure to do so exposes a race with normal output probing. Detected by
adding some warnings that the mutex is held to the backend detect routines:
[ 17.772456] WARNING: at drivers/gpu/drm/i915/intel_crt.c:471 intel_crt_detect+0x3e/0x373 [i915]()
[ 17.772458] Hardware name: Latitude E6400
[ 17.772460] Modules linked in: ....
[ 17.772582] Pid: 11, comm: kworker/0:1 Tainted: G W 2.6.38.4-custom.2 #8
[ 17.772584] Call Trace:
[ 17.772591] [<ffffffff81046af5>] ? warn_slowpath_common+0x78/0x8c
[ 17.772603] [<ffffffffa03f3e5c>] ? intel_crt_detect+0x3e/0x373 [i915]
[ 17.772612] [<ffffffffa0355d49>] ? drm_helper_probe_single_connector_modes+0xbf/0x2af [drm_kms_helper]
[ 17.772619] [<ffffffffa03534d5>] ? drm_fb_helper_probe_connector_modes+0x39/0x4d [drm_kms_helper]
[ 17.772625] [<ffffffffa0354760>] ? drm_fb_helper_hotplug_event+0xa5/0xc3 [drm_kms_helper]
[ 17.772633] [<ffffffffa035577f>] ? output_poll_execute+0x146/0x17c [drm_kms_helper]
[ 17.772638] [<ffffffff81193c01>] ? cfq_init_queue+0x247/0x345
[ 17.772644] [<ffffffffa0355639>] ? output_poll_execute+0x0/0x17c [drm_kms_helper]
[ 17.772648] [<ffffffff8105b540>] ? process_one_work+0x193/0x28e
[ 17.772652] [<ffffffff8105c6bc>] ? worker_thread+0xef/0x172
[ 17.772655] [<ffffffff8105c5cd>] ? worker_thread+0x0/0x172
[ 17.772658] [<ffffffff8105c5cd>] ? worker_thread+0x0/0x172
[ 17.772663] [<ffffffff8105f767>] ? kthread+0x7a/0x82
[ 17.772668] [<ffffffff8100a724>] ? kernel_thread_helper+0x4/0x10
[ 17.772671] [<ffffffff8105f6ed>] ? kthread+0x0/0x82
[ 17.772674] [<ffffffff8100a720>] ? kernel_thread_helper+0x0/0x10
Reported-by: Frederik Himpe <fhimpe@telenet.be>
References: https://bugs.freedesktop.org/show_bug.cgi?id=36394
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Dave Airlie <airlied@redhat.com>
| -rw-r--r-- | drivers/gpu/drm/drm_fb_helper.c | 26 | ||||
| -rw-r--r-- | include/drm/drm_fb_helper.h | 2 |
2 files changed, 23 insertions, 5 deletions
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 11d7a72c22d9..140b9525b48a 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c | |||
| @@ -1516,17 +1516,33 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) | |||
| 1516 | } | 1516 | } |
| 1517 | EXPORT_SYMBOL(drm_fb_helper_initial_config); | 1517 | EXPORT_SYMBOL(drm_fb_helper_initial_config); |
| 1518 | 1518 | ||
| 1519 | bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) | 1519 | /** |
| 1520 | * drm_fb_helper_hotplug_event - respond to a hotplug notification by | ||
| 1521 | * probing all the outputs attached to the fb. | ||
| 1522 | * @fb_helper: the drm_fb_helper | ||
| 1523 | * | ||
| 1524 | * LOCKING: | ||
| 1525 | * Called at runtime, must take mode config lock. | ||
| 1526 | * | ||
| 1527 | * Scan the connectors attached to the fb_helper and try to put together a | ||
| 1528 | * setup after *notification of a change in output configuration. | ||
| 1529 | * | ||
| 1530 | * RETURNS: | ||
| 1531 | * 0 on success and a non-zero error code otherwise. | ||
| 1532 | */ | ||
| 1533 | int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) | ||
| 1520 | { | 1534 | { |
| 1535 | struct drm_device *dev = fb_helper->dev; | ||
| 1521 | int count = 0; | 1536 | int count = 0; |
| 1522 | u32 max_width, max_height, bpp_sel; | 1537 | u32 max_width, max_height, bpp_sel; |
| 1523 | bool bound = false, crtcs_bound = false; | 1538 | bool bound = false, crtcs_bound = false; |
| 1524 | struct drm_crtc *crtc; | 1539 | struct drm_crtc *crtc; |
| 1525 | 1540 | ||
| 1526 | if (!fb_helper->fb) | 1541 | if (!fb_helper->fb) |
| 1527 | return false; | 1542 | return 0; |
| 1528 | 1543 | ||
| 1529 | list_for_each_entry(crtc, &fb_helper->dev->mode_config.crtc_list, head) { | 1544 | mutex_lock(&dev->mode_config.mutex); |
| 1545 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
| 1530 | if (crtc->fb) | 1546 | if (crtc->fb) |
| 1531 | crtcs_bound = true; | 1547 | crtcs_bound = true; |
| 1532 | if (crtc->fb == fb_helper->fb) | 1548 | if (crtc->fb == fb_helper->fb) |
| @@ -1535,7 +1551,8 @@ bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) | |||
| 1535 | 1551 | ||
| 1536 | if (!bound && crtcs_bound) { | 1552 | if (!bound && crtcs_bound) { |
| 1537 | fb_helper->delayed_hotplug = true; | 1553 | fb_helper->delayed_hotplug = true; |
| 1538 | return false; | 1554 | mutex_unlock(&dev->mode_config.mutex); |
| 1555 | return 0; | ||
| 1539 | } | 1556 | } |
| 1540 | DRM_DEBUG_KMS("\n"); | 1557 | DRM_DEBUG_KMS("\n"); |
| 1541 | 1558 | ||
| @@ -1546,6 +1563,7 @@ bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) | |||
| 1546 | count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, | 1563 | count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, |
| 1547 | max_height); | 1564 | max_height); |
| 1548 | drm_setup_crtcs(fb_helper); | 1565 | drm_setup_crtcs(fb_helper); |
| 1566 | mutex_unlock(&dev->mode_config.mutex); | ||
| 1549 | 1567 | ||
| 1550 | return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); | 1568 | return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); |
| 1551 | } | 1569 | } |
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index ade09d7b4271..c99c3d3e7811 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h | |||
| @@ -127,7 +127,7 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, | |||
| 127 | 127 | ||
| 128 | int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info); | 128 | int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info); |
| 129 | 129 | ||
| 130 | bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper); | 130 | int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper); |
| 131 | bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel); | 131 | bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel); |
| 132 | int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper); | 132 | int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper); |
| 133 | int drm_fb_helper_debug_enter(struct fb_info *info); | 133 | int drm_fb_helper_debug_enter(struct fb_info *info); |
