diff options
author | Gu Zheng <guz.fnst@cn.fujitsu.com> | 2013-11-05 05:00:57 -0500 |
---|---|---|
committer | Tomi Valkeinen <tomi.valkeinen@ti.com> | 2013-11-11 08:52:59 -0500 |
commit | 3a41c5dbe8bc396a7fb16ca8739e945bb003342e (patch) | |
tree | 637757f9d320f138aa062b959b741e44fcce90ac /drivers/video | |
parent | deccd24f9077ccabc6b34f2e6d2f75c98b528fa1 (diff) |
fb: reorder the lock sequence to fix potential dead lock
Following commits:
50e244cc79 fb: rework locking to fix lock ordering on takeover
e93a9a8687 fb: Yet another band-aid for fixing lockdep mess
054430e773 fbcon: fix locking harder
reworked locking to fix related lock ordering on takeover, and introduced console_lock
into fbmem, but it seems that the new lock sequence(fb_info->lock ---> console_lock)
is against with the one in console_callback(console_lock ---> fb_info->lock), and leads to
a potential dead lock as following:
[ 601.079000] ======================================================
[ 601.079000] [ INFO: possible circular locking dependency detected ]
[ 601.079000] 3.11.0 #189 Not tainted
[ 601.079000] -------------------------------------------------------
[ 601.079000] kworker/0:3/619 is trying to acquire lock:
[ 601.079000] (&fb_info->lock){+.+.+.}, at: [<ffffffff81397566>] lock_fb_info+0x26/0x60
[ 601.079000]
but task is already holding lock:
[ 601.079000] (console_lock){+.+.+.}, at: [<ffffffff8141aae3>] console_callback+0x13/0x160
[ 601.079000]
which lock already depends on the new lock.
[ 601.079000]
the existing dependency chain (in reverse order) is:
[ 601.079000]
-> #1 (console_lock){+.+.+.}:
[ 601.079000] [<ffffffff810dc971>] lock_acquire+0xa1/0x140
[ 601.079000] [<ffffffff810c6267>] console_lock+0x77/0x80
[ 601.079000] [<ffffffff81399448>] register_framebuffer+0x1d8/0x320
[ 601.079000] [<ffffffff81cfb4c8>] efifb_probe+0x408/0x48f
[ 601.079000] [<ffffffff8144a963>] platform_drv_probe+0x43/0x80
[ 601.079000] [<ffffffff8144853b>] driver_probe_device+0x8b/0x390
[ 601.079000] [<ffffffff814488eb>] __driver_attach+0xab/0xb0
[ 601.079000] [<ffffffff814463bd>] bus_for_each_dev+0x5d/0xa0
[ 601.079000] [<ffffffff81447e6e>] driver_attach+0x1e/0x20
[ 601.079000] [<ffffffff81447a07>] bus_add_driver+0x117/0x290
[ 601.079000] [<ffffffff81448fea>] driver_register+0x7a/0x170
[ 601.079000] [<ffffffff8144a10a>] __platform_driver_register+0x4a/0x50
[ 601.079000] [<ffffffff8144a12d>] platform_driver_probe+0x1d/0xb0
[ 601.079000] [<ffffffff81cfb0a1>] efifb_init+0x273/0x292
[ 601.079000] [<ffffffff81002132>] do_one_initcall+0x102/0x1c0
[ 601.079000] [<ffffffff81cb80a6>] kernel_init_freeable+0x15d/0x1ef
[ 601.079000] [<ffffffff8166d2de>] kernel_init+0xe/0xf0
[ 601.079000] [<ffffffff816914ec>] ret_from_fork+0x7c/0xb0
[ 601.079000]
-> #0 (&fb_info->lock){+.+.+.}:
[ 601.079000] [<ffffffff810dc1d8>] __lock_acquire+0x1e18/0x1f10
[ 601.079000] [<ffffffff810dc971>] lock_acquire+0xa1/0x140
[ 601.079000] [<ffffffff816835ca>] mutex_lock_nested+0x7a/0x3b0
[ 601.079000] [<ffffffff81397566>] lock_fb_info+0x26/0x60
[ 601.079000] [<ffffffff813a4aeb>] fbcon_blank+0x29b/0x2e0
[ 601.079000] [<ffffffff81418658>] do_blank_screen+0x1d8/0x280
[ 601.079000] [<ffffffff8141ab34>] console_callback+0x64/0x160
[ 601.079000] [<ffffffff8108d855>] process_one_work+0x1f5/0x540
[ 601.079000] [<ffffffff8108e04c>] worker_thread+0x11c/0x370
[ 601.079000] [<ffffffff81095fbd>] kthread+0xed/0x100
[ 601.079000] [<ffffffff816914ec>] ret_from_fork+0x7c/0xb0
[ 601.079000]
other info that might help us debug this:
[ 601.079000] Possible unsafe locking scenario:
[ 601.079000] CPU0 CPU1
[ 601.079000] ---- ----
[ 601.079000] lock(console_lock);
[ 601.079000] lock(&fb_info->lock);
[ 601.079000] lock(console_lock);
[ 601.079000] lock(&fb_info->lock);
[ 601.079000]
*** DEADLOCK ***
so we reorder the lock sequence the same as it in console_callback() to
avoid this issue. And following Tomi's suggestion, fix these similar
issues all in fb subsystem.
Signed-off-by: Gu Zheng <guz.fnst@cn.fujitsu.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/fbmem.c | 50 | ||||
-rw-r--r-- | drivers/video/fbsysfs.c | 19 | ||||
-rw-r--r-- | drivers/video/sh_mobile_lcdcfb.c | 10 |
3 files changed, 51 insertions, 28 deletions
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index dacaf74256a3..010d19105ebc 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c | |||
@@ -1108,14 +1108,16 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, | |||
1108 | case FBIOPUT_VSCREENINFO: | 1108 | case FBIOPUT_VSCREENINFO: |
1109 | if (copy_from_user(&var, argp, sizeof(var))) | 1109 | if (copy_from_user(&var, argp, sizeof(var))) |
1110 | return -EFAULT; | 1110 | return -EFAULT; |
1111 | if (!lock_fb_info(info)) | ||
1112 | return -ENODEV; | ||
1113 | console_lock(); | 1111 | console_lock(); |
1112 | if (!lock_fb_info(info)) { | ||
1113 | console_unlock(); | ||
1114 | return -ENODEV; | ||
1115 | } | ||
1114 | info->flags |= FBINFO_MISC_USEREVENT; | 1116 | info->flags |= FBINFO_MISC_USEREVENT; |
1115 | ret = fb_set_var(info, &var); | 1117 | ret = fb_set_var(info, &var); |
1116 | info->flags &= ~FBINFO_MISC_USEREVENT; | 1118 | info->flags &= ~FBINFO_MISC_USEREVENT; |
1117 | console_unlock(); | ||
1118 | unlock_fb_info(info); | 1119 | unlock_fb_info(info); |
1120 | console_unlock(); | ||
1119 | if (!ret && copy_to_user(argp, &var, sizeof(var))) | 1121 | if (!ret && copy_to_user(argp, &var, sizeof(var))) |
1120 | ret = -EFAULT; | 1122 | ret = -EFAULT; |
1121 | break; | 1123 | break; |
@@ -1144,12 +1146,14 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, | |||
1144 | case FBIOPAN_DISPLAY: | 1146 | case FBIOPAN_DISPLAY: |
1145 | if (copy_from_user(&var, argp, sizeof(var))) | 1147 | if (copy_from_user(&var, argp, sizeof(var))) |
1146 | return -EFAULT; | 1148 | return -EFAULT; |
1147 | if (!lock_fb_info(info)) | ||
1148 | return -ENODEV; | ||
1149 | console_lock(); | 1149 | console_lock(); |
1150 | if (!lock_fb_info(info)) { | ||
1151 | console_unlock(); | ||
1152 | return -ENODEV; | ||
1153 | } | ||
1150 | ret = fb_pan_display(info, &var); | 1154 | ret = fb_pan_display(info, &var); |
1151 | console_unlock(); | ||
1152 | unlock_fb_info(info); | 1155 | unlock_fb_info(info); |
1156 | console_unlock(); | ||
1153 | if (ret == 0 && copy_to_user(argp, &var, sizeof(var))) | 1157 | if (ret == 0 && copy_to_user(argp, &var, sizeof(var))) |
1154 | return -EFAULT; | 1158 | return -EFAULT; |
1155 | break; | 1159 | break; |
@@ -1184,23 +1188,27 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, | |||
1184 | break; | 1188 | break; |
1185 | } | 1189 | } |
1186 | event.data = &con2fb; | 1190 | event.data = &con2fb; |
1187 | if (!lock_fb_info(info)) | ||
1188 | return -ENODEV; | ||
1189 | console_lock(); | 1191 | console_lock(); |
1192 | if (!lock_fb_info(info)) { | ||
1193 | console_unlock(); | ||
1194 | return -ENODEV; | ||
1195 | } | ||
1190 | event.info = info; | 1196 | event.info = info; |
1191 | ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event); | 1197 | ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event); |
1192 | console_unlock(); | ||
1193 | unlock_fb_info(info); | 1198 | unlock_fb_info(info); |
1199 | console_unlock(); | ||
1194 | break; | 1200 | break; |
1195 | case FBIOBLANK: | 1201 | case FBIOBLANK: |
1196 | if (!lock_fb_info(info)) | ||
1197 | return -ENODEV; | ||
1198 | console_lock(); | 1202 | console_lock(); |
1203 | if (!lock_fb_info(info)) { | ||
1204 | console_unlock(); | ||
1205 | return -ENODEV; | ||
1206 | } | ||
1199 | info->flags |= FBINFO_MISC_USEREVENT; | 1207 | info->flags |= FBINFO_MISC_USEREVENT; |
1200 | ret = fb_blank(info, arg); | 1208 | ret = fb_blank(info, arg); |
1201 | info->flags &= ~FBINFO_MISC_USEREVENT; | 1209 | info->flags &= ~FBINFO_MISC_USEREVENT; |
1202 | console_unlock(); | ||
1203 | unlock_fb_info(info); | 1210 | unlock_fb_info(info); |
1211 | console_unlock(); | ||
1204 | break; | 1212 | break; |
1205 | default: | 1213 | default: |
1206 | if (!lock_fb_info(info)) | 1214 | if (!lock_fb_info(info)) |
@@ -1660,12 +1668,15 @@ static int do_register_framebuffer(struct fb_info *fb_info) | |||
1660 | registered_fb[i] = fb_info; | 1668 | registered_fb[i] = fb_info; |
1661 | 1669 | ||
1662 | event.info = fb_info; | 1670 | event.info = fb_info; |
1663 | if (!lock_fb_info(fb_info)) | ||
1664 | return -ENODEV; | ||
1665 | console_lock(); | 1671 | console_lock(); |
1672 | if (!lock_fb_info(fb_info)) { | ||
1673 | console_unlock(); | ||
1674 | return -ENODEV; | ||
1675 | } | ||
1676 | |||
1666 | fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event); | 1677 | fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event); |
1667 | console_unlock(); | ||
1668 | unlock_fb_info(fb_info); | 1678 | unlock_fb_info(fb_info); |
1679 | console_unlock(); | ||
1669 | return 0; | 1680 | return 0; |
1670 | } | 1681 | } |
1671 | 1682 | ||
@@ -1678,13 +1689,16 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) | |||
1678 | if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info) | 1689 | if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info) |
1679 | return -EINVAL; | 1690 | return -EINVAL; |
1680 | 1691 | ||
1681 | if (!lock_fb_info(fb_info)) | ||
1682 | return -ENODEV; | ||
1683 | console_lock(); | 1692 | console_lock(); |
1693 | if (!lock_fb_info(fb_info)) { | ||
1694 | console_unlock(); | ||
1695 | return -ENODEV; | ||
1696 | } | ||
1697 | |||
1684 | event.info = fb_info; | 1698 | event.info = fb_info; |
1685 | ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event); | 1699 | ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event); |
1686 | console_unlock(); | ||
1687 | unlock_fb_info(fb_info); | 1700 | unlock_fb_info(fb_info); |
1701 | console_unlock(); | ||
1688 | 1702 | ||
1689 | if (ret) | 1703 | if (ret) |
1690 | return -EINVAL; | 1704 | return -EINVAL; |
diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index ef476b02fbe5..53444ac19fe0 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c | |||
@@ -177,9 +177,12 @@ static ssize_t store_modes(struct device *device, | |||
177 | if (i * sizeof(struct fb_videomode) != count) | 177 | if (i * sizeof(struct fb_videomode) != count) |
178 | return -EINVAL; | 178 | return -EINVAL; |
179 | 179 | ||
180 | if (!lock_fb_info(fb_info)) | ||
181 | return -ENODEV; | ||
182 | console_lock(); | 180 | console_lock(); |
181 | if (!lock_fb_info(fb_info)) { | ||
182 | console_unlock(); | ||
183 | return -ENODEV; | ||
184 | } | ||
185 | |||
183 | list_splice(&fb_info->modelist, &old_list); | 186 | list_splice(&fb_info->modelist, &old_list); |
184 | fb_videomode_to_modelist((const struct fb_videomode *)buf, i, | 187 | fb_videomode_to_modelist((const struct fb_videomode *)buf, i, |
185 | &fb_info->modelist); | 188 | &fb_info->modelist); |
@@ -189,8 +192,8 @@ static ssize_t store_modes(struct device *device, | |||
189 | } else | 192 | } else |
190 | fb_destroy_modelist(&old_list); | 193 | fb_destroy_modelist(&old_list); |
191 | 194 | ||
192 | console_unlock(); | ||
193 | unlock_fb_info(fb_info); | 195 | unlock_fb_info(fb_info); |
196 | console_unlock(); | ||
194 | 197 | ||
195 | return 0; | 198 | return 0; |
196 | } | 199 | } |
@@ -404,12 +407,16 @@ static ssize_t store_fbstate(struct device *device, | |||
404 | 407 | ||
405 | state = simple_strtoul(buf, &last, 0); | 408 | state = simple_strtoul(buf, &last, 0); |
406 | 409 | ||
407 | if (!lock_fb_info(fb_info)) | ||
408 | return -ENODEV; | ||
409 | console_lock(); | 410 | console_lock(); |
411 | if (!lock_fb_info(fb_info)) { | ||
412 | console_unlock(); | ||
413 | return -ENODEV; | ||
414 | } | ||
415 | |||
410 | fb_set_suspend(fb_info, (int)state); | 416 | fb_set_suspend(fb_info, (int)state); |
411 | console_unlock(); | 417 | |
412 | unlock_fb_info(fb_info); | 418 | unlock_fb_info(fb_info); |
419 | console_unlock(); | ||
413 | 420 | ||
414 | return count; | 421 | return count; |
415 | } | 422 | } |
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index eaeae0fd0962..ab85ad6c25ec 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c | |||
@@ -574,8 +574,9 @@ static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch, | |||
574 | switch (event) { | 574 | switch (event) { |
575 | case SH_MOBILE_LCDC_EVENT_DISPLAY_CONNECT: | 575 | case SH_MOBILE_LCDC_EVENT_DISPLAY_CONNECT: |
576 | /* HDMI plug in */ | 576 | /* HDMI plug in */ |
577 | console_lock(); | ||
577 | if (lock_fb_info(info)) { | 578 | if (lock_fb_info(info)) { |
578 | console_lock(); | 579 | |
579 | 580 | ||
580 | ch->display.width = monspec->max_x * 10; | 581 | ch->display.width = monspec->max_x * 10; |
581 | ch->display.height = monspec->max_y * 10; | 582 | ch->display.height = monspec->max_y * 10; |
@@ -594,19 +595,20 @@ static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch, | |||
594 | fb_set_suspend(info, 0); | 595 | fb_set_suspend(info, 0); |
595 | } | 596 | } |
596 | 597 | ||
597 | console_unlock(); | 598 | |
598 | unlock_fb_info(info); | 599 | unlock_fb_info(info); |
599 | } | 600 | } |
601 | console_unlock(); | ||
600 | break; | 602 | break; |
601 | 603 | ||
602 | case SH_MOBILE_LCDC_EVENT_DISPLAY_DISCONNECT: | 604 | case SH_MOBILE_LCDC_EVENT_DISPLAY_DISCONNECT: |
603 | /* HDMI disconnect */ | 605 | /* HDMI disconnect */ |
606 | console_lock(); | ||
604 | if (lock_fb_info(info)) { | 607 | if (lock_fb_info(info)) { |
605 | console_lock(); | ||
606 | fb_set_suspend(info, 1); | 608 | fb_set_suspend(info, 1); |
607 | console_unlock(); | ||
608 | unlock_fb_info(info); | 609 | unlock_fb_info(info); |
609 | } | 610 | } |
611 | console_unlock(); | ||
610 | break; | 612 | break; |
611 | 613 | ||
612 | case SH_MOBILE_LCDC_EVENT_DISPLAY_MODE: | 614 | case SH_MOBILE_LCDC_EVENT_DISPLAY_MODE: |