aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Cox2013-01-24 18:28:15 -0600
committerGreg Kroah-Hartman2013-02-28 08:32:27 -0600
commitabd9120040d5f427b950561277f2846b0a80be44 (patch)
tree15daed2e300f2df9852692ac84f5ffb999575683
parent90523879cdf0051a208a660a6ce475ed7ac51e99 (diff)
downloadkernel-common-abd9120040d5f427b950561277f2846b0a80be44.tar.gz
kernel-common-abd9120040d5f427b950561277f2846b0a80be44.tar.xz
kernel-common-abd9120040d5f427b950561277f2846b0a80be44.zip
fb: rework locking to fix lock ordering on takeover
commit 50e244cc793d511b86adea24972f3a7264cae114 upstream. Adjust the console layer to allow a take over call where the caller already holds the locks. Make the fb layer lock in order. This is partly a band aid, the fb layer is terminally confused about the locking rules it uses for its notifiers it seems. [akpm@linux-foundation.org: remove stray non-ascii char, tidy comment] [akpm@linux-foundation.org: export do_take_over_console()] [airlied: cleanup another non-ascii char] Signed-off-by: Alan Cox <alan@linux.intel.com> Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de> Cc: Stephen Rothwell <sfr@canb.auug.org.au> Cc: Jiri Kosina <jkosina@suse.cz> Tested-by: Sedat Dilek <sedat.dilek@gmail.com> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Dave Airlie <airlied@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/tty/vt/vt.c93
-rw-r--r--drivers/video/console/fbcon.c29
-rw-r--r--drivers/video/fbmem.c5
-rw-r--r--drivers/video/fbsysfs.c3
-rw-r--r--include/linux/console.h1
5 files changed, 104 insertions, 27 deletions
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index b3915b7ad3e..eb80f01dcbd 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -3016,7 +3016,7 @@ int __init vty_init(const struct file_operations *console_fops)
3016 3016
3017static struct class *vtconsole_class; 3017static struct class *vtconsole_class;
3018 3018
3019static int bind_con_driver(const struct consw *csw, int first, int last, 3019static int do_bind_con_driver(const struct consw *csw, int first, int last,
3020 int deflt) 3020 int deflt)
3021{ 3021{
3022 struct module *owner = csw->owner; 3022 struct module *owner = csw->owner;
@@ -3027,7 +3027,7 @@ static int bind_con_driver(const struct consw *csw, int first, int last,
3027 if (!try_module_get(owner)) 3027 if (!try_module_get(owner))
3028 return -ENODEV; 3028 return -ENODEV;
3029 3029
3030 console_lock(); 3030 WARN_CONSOLE_UNLOCKED();
3031 3031
3032 /* check if driver is registered */ 3032 /* check if driver is registered */
3033 for (i = 0; i < MAX_NR_CON_DRIVER; i++) { 3033 for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
@@ -3112,11 +3112,22 @@ static int bind_con_driver(const struct consw *csw, int first, int last,
3112 3112
3113 retval = 0; 3113 retval = 0;
3114err: 3114err:
3115 console_unlock();
3116 module_put(owner); 3115 module_put(owner);
3117 return retval; 3116 return retval;
3118}; 3117};
3119 3118
3119
3120static int bind_con_driver(const struct consw *csw, int first, int last,
3121 int deflt)
3122{
3123 int ret;
3124
3125 console_lock();
3126 ret = do_bind_con_driver(csw, first, last, deflt);
3127 console_unlock();
3128 return ret;
3129}
3130
3120#ifdef CONFIG_VT_HW_CONSOLE_BINDING 3131#ifdef CONFIG_VT_HW_CONSOLE_BINDING
3121static int con_is_graphics(const struct consw *csw, int first, int last) 3132static int con_is_graphics(const struct consw *csw, int first, int last)
3122{ 3133{
@@ -3228,9 +3239,9 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt)
3228 if (!con_is_bound(csw)) 3239 if (!con_is_bound(csw))
3229 con_driver->flag &= ~CON_DRIVER_FLAG_INIT; 3240 con_driver->flag &= ~CON_DRIVER_FLAG_INIT;
3230 3241
3231 console_unlock();
3232 /* ignore return value, binding should not fail */ 3242 /* ignore return value, binding should not fail */
3233 bind_con_driver(defcsw, first, last, deflt); 3243 do_bind_con_driver(defcsw, first, last, deflt);
3244 console_unlock();
3234err: 3245err:
3235 module_put(owner); 3246 module_put(owner);
3236 return retval; 3247 return retval;
@@ -3508,28 +3519,18 @@ int con_debug_leave(void)
3508} 3519}
3509EXPORT_SYMBOL_GPL(con_debug_leave); 3520EXPORT_SYMBOL_GPL(con_debug_leave);
3510 3521
3511/** 3522static int do_register_con_driver(const struct consw *csw, int first, int last)
3512 * register_con_driver - register console driver to console layer
3513 * @csw: console driver
3514 * @first: the first console to take over, minimum value is 0
3515 * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1
3516 *
3517 * DESCRIPTION: This function registers a console driver which can later
3518 * bind to a range of consoles specified by @first and @last. It will
3519 * also initialize the console driver by calling con_startup().
3520 */
3521int register_con_driver(const struct consw *csw, int first, int last)
3522{ 3523{
3523 struct module *owner = csw->owner; 3524 struct module *owner = csw->owner;
3524 struct con_driver *con_driver; 3525 struct con_driver *con_driver;
3525 const char *desc; 3526 const char *desc;
3526 int i, retval = 0; 3527 int i, retval = 0;
3527 3528
3529 WARN_CONSOLE_UNLOCKED();
3530
3528 if (!try_module_get(owner)) 3531 if (!try_module_get(owner))
3529 return -ENODEV; 3532 return -ENODEV;
3530 3533
3531 console_lock();
3532
3533 for (i = 0; i < MAX_NR_CON_DRIVER; i++) { 3534 for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
3534 con_driver = &registered_con_driver[i]; 3535 con_driver = &registered_con_driver[i];
3535 3536
@@ -3582,10 +3583,29 @@ int register_con_driver(const struct consw *csw, int first, int last)
3582 } 3583 }
3583 3584
3584err: 3585err:
3585 console_unlock();
3586 module_put(owner); 3586 module_put(owner);
3587 return retval; 3587 return retval;
3588} 3588}
3589
3590/**
3591 * register_con_driver - register console driver to console layer
3592 * @csw: console driver
3593 * @first: the first console to take over, minimum value is 0
3594 * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1
3595 *
3596 * DESCRIPTION: This function registers a console driver which can later
3597 * bind to a range of consoles specified by @first and @last. It will
3598 * also initialize the console driver by calling con_startup().
3599 */
3600int register_con_driver(const struct consw *csw, int first, int last)
3601{
3602 int retval;
3603
3604 console_lock();
3605 retval = do_register_con_driver(csw, first, last);
3606 console_unlock();
3607 return retval;
3608}
3589EXPORT_SYMBOL(register_con_driver); 3609EXPORT_SYMBOL(register_con_driver);
3590 3610
3591/** 3611/**
@@ -3639,17 +3659,44 @@ EXPORT_SYMBOL(unregister_con_driver);
3639 * when a driver wants to take over some existing consoles 3659 * when a driver wants to take over some existing consoles
3640 * and become default driver for newly opened ones. 3660 * and become default driver for newly opened ones.
3641 * 3661 *
3642 * take_over_console is basically a register followed by unbind 3662 * take_over_console is basically a register followed by unbind
3663 */
3664int do_take_over_console(const struct consw *csw, int first, int last, int deflt)
3665{
3666 int err;
3667
3668 err = do_register_con_driver(csw, first, last);
3669 /*
3670 * If we get an busy error we still want to bind the console driver
3671 * and return success, as we may have unbound the console driver
3672 * but not unregistered it.
3673 */
3674 if (err == -EBUSY)
3675 err = 0;
3676 if (!err)
3677 do_bind_con_driver(csw, first, last, deflt);
3678
3679 return err;
3680}
3681EXPORT_SYMBOL_GPL(do_take_over_console);
3682
3683/*
3684 * If we support more console drivers, this function is used
3685 * when a driver wants to take over some existing consoles
3686 * and become default driver for newly opened ones.
3687 *
3688 * take_over_console is basically a register followed by unbind
3643 */ 3689 */
3644int take_over_console(const struct consw *csw, int first, int last, int deflt) 3690int take_over_console(const struct consw *csw, int first, int last, int deflt)
3645{ 3691{
3646 int err; 3692 int err;
3647 3693
3648 err = register_con_driver(csw, first, last); 3694 err = register_con_driver(csw, first, last);
3649 /* if we get an busy error we still want to bind the console driver 3695 /*
3696 * If we get an busy error we still want to bind the console driver
3650 * and return success, as we may have unbound the console driver 3697 * and return success, as we may have unbound the console driver
3651  * but not unregistered it. 3698 * but not unregistered it.
3652 */ 3699 */
3653 if (err == -EBUSY) 3700 if (err == -EBUSY)
3654 err = 0; 3701 err = 0;
3655 if (!err) 3702 if (!err)
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index b27a91c9880..18d02c6af70 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -530,6 +530,33 @@ static int search_for_mapped_con(void)
530 return retval; 530 return retval;
531} 531}
532 532
533static int do_fbcon_takeover(int show_logo)
534{
535 int err, i;
536
537 if (!num_registered_fb)
538 return -ENODEV;
539
540 if (!show_logo)
541 logo_shown = FBCON_LOGO_DONTSHOW;
542
543 for (i = first_fb_vc; i <= last_fb_vc; i++)
544 con2fb_map[i] = info_idx;
545
546 err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc,
547 fbcon_is_default);
548
549 if (err) {
550 for (i = first_fb_vc; i <= last_fb_vc; i++)
551 con2fb_map[i] = -1;
552 info_idx = -1;
553 } else {
554 fbcon_has_console_bind = 1;
555 }
556
557 return err;
558}
559
533static int fbcon_takeover(int show_logo) 560static int fbcon_takeover(int show_logo)
534{ 561{
535 int err, i; 562 int err, i;
@@ -3122,7 +3149,7 @@ static int fbcon_fb_registered(struct fb_info *info)
3122 } 3149 }
3123 3150
3124 if (info_idx != -1) 3151 if (info_idx != -1)
3125 ret = fbcon_takeover(1); 3152 ret = do_fbcon_takeover(1);
3126 } else { 3153 } else {
3127 for (i = first_fb_vc; i <= last_fb_vc; i++) { 3154 for (i = first_fb_vc; i <= last_fb_vc; i++) {
3128 if (con2fb_map_boot[i] == idx) 3155 if (con2fb_map_boot[i] == idx)
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 7a41220aebd..a94edc3f58b 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -1628,7 +1628,9 @@ static int do_register_framebuffer(struct fb_info *fb_info)
1628 event.info = fb_info; 1628 event.info = fb_info;
1629 if (!lock_fb_info(fb_info)) 1629 if (!lock_fb_info(fb_info))
1630 return -ENODEV; 1630 return -ENODEV;
1631 console_lock();
1631 fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event); 1632 fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
1633 console_unlock();
1632 unlock_fb_info(fb_info); 1634 unlock_fb_info(fb_info);
1633 return 0; 1635 return 0;
1634} 1636}
@@ -1831,11 +1833,8 @@ int fb_new_modelist(struct fb_info *info)
1831 err = 1; 1833 err = 1;
1832 1834
1833 if (!list_empty(&info->modelist)) { 1835 if (!list_empty(&info->modelist)) {
1834 if (!lock_fb_info(info))
1835 return -ENODEV;
1836 event.info = info; 1836 event.info = info;
1837 err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event); 1837 err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
1838 unlock_fb_info(info);
1839 } 1838 }
1840 1839
1841 return err; 1840 return err;
diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c
index 67afa9c2289..303fb9f35b2 100644
--- a/drivers/video/fbsysfs.c
+++ b/drivers/video/fbsysfs.c
@@ -175,6 +175,8 @@ static ssize_t store_modes(struct device *device,
175 if (i * sizeof(struct fb_videomode) != count) 175 if (i * sizeof(struct fb_videomode) != count)
176 return -EINVAL; 176 return -EINVAL;
177 177
178 if (!lock_fb_info(fb_info))
179 return -ENODEV;
178 console_lock(); 180 console_lock();
179 list_splice(&fb_info->modelist, &old_list); 181 list_splice(&fb_info->modelist, &old_list);
180 fb_videomode_to_modelist((const struct fb_videomode *)buf, i, 182 fb_videomode_to_modelist((const struct fb_videomode *)buf, i,
@@ -186,6 +188,7 @@ static ssize_t store_modes(struct device *device,
186 fb_destroy_modelist(&old_list); 188 fb_destroy_modelist(&old_list);
187 189
188 console_unlock(); 190 console_unlock();
191 unlock_fb_info(fb_info);
189 192
190 return 0; 193 return 0;
191} 194}
diff --git a/include/linux/console.h b/include/linux/console.h
index 7453cfd593c..49b1061f3a0 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -78,6 +78,7 @@ int con_is_bound(const struct consw *csw);
78int register_con_driver(const struct consw *csw, int first, int last); 78int register_con_driver(const struct consw *csw, int first, int last);
79int unregister_con_driver(const struct consw *csw); 79int unregister_con_driver(const struct consw *csw);
80int take_over_console(const struct consw *sw, int first, int last, int deflt); 80int take_over_console(const struct consw *sw, int first, int last, int deflt);
81int do_take_over_console(const struct consw *sw, int first, int last, int deflt);
81void give_up_console(const struct consw *sw); 82void give_up_console(const struct consw *sw);
82#ifdef CONFIG_HW_CONSOLE 83#ifdef CONFIG_HW_CONSOLE
83int con_debug_enter(struct vc_data *vc); 84int con_debug_enter(struct vc_data *vc);