diff options
author | Alan Cox <alan@linux.intel.com> | 2013-01-24 19:28:15 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2013-02-07 21:02:40 -0500 |
commit | 50e244cc793d511b86adea24972f3a7264cae114 (patch) | |
tree | de59e5f228a256ad09521524639e1b5b6e49b8cc /drivers/tty | |
parent | 84b603abd23300df8ee5fb1c23c83634c2aaedea (diff) |
fb: rework locking to fix lock ordering on takeover
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>
Cc: stable <stable@vger.kernel.org>
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>
Diffstat (limited to 'drivers/tty')
-rw-r--r-- | drivers/tty/vt/vt.c | 93 |
1 files changed, 70 insertions, 23 deletions
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 8fd89687d068..c076af0b300b 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c | |||
@@ -2987,7 +2987,7 @@ int __init vty_init(const struct file_operations *console_fops) | |||
2987 | 2987 | ||
2988 | static struct class *vtconsole_class; | 2988 | static struct class *vtconsole_class; |
2989 | 2989 | ||
2990 | static int bind_con_driver(const struct consw *csw, int first, int last, | 2990 | static int do_bind_con_driver(const struct consw *csw, int first, int last, |
2991 | int deflt) | 2991 | int deflt) |
2992 | { | 2992 | { |
2993 | struct module *owner = csw->owner; | 2993 | struct module *owner = csw->owner; |
@@ -2998,7 +2998,7 @@ static int bind_con_driver(const struct consw *csw, int first, int last, | |||
2998 | if (!try_module_get(owner)) | 2998 | if (!try_module_get(owner)) |
2999 | return -ENODEV; | 2999 | return -ENODEV; |
3000 | 3000 | ||
3001 | console_lock(); | 3001 | WARN_CONSOLE_UNLOCKED(); |
3002 | 3002 | ||
3003 | /* check if driver is registered */ | 3003 | /* check if driver is registered */ |
3004 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | 3004 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { |
@@ -3083,11 +3083,22 @@ static int bind_con_driver(const struct consw *csw, int first, int last, | |||
3083 | 3083 | ||
3084 | retval = 0; | 3084 | retval = 0; |
3085 | err: | 3085 | err: |
3086 | console_unlock(); | ||
3087 | module_put(owner); | 3086 | module_put(owner); |
3088 | return retval; | 3087 | return retval; |
3089 | }; | 3088 | }; |
3090 | 3089 | ||
3090 | |||
3091 | static int bind_con_driver(const struct consw *csw, int first, int last, | ||
3092 | int deflt) | ||
3093 | { | ||
3094 | int ret; | ||
3095 | |||
3096 | console_lock(); | ||
3097 | ret = do_bind_con_driver(csw, first, last, deflt); | ||
3098 | console_unlock(); | ||
3099 | return ret; | ||
3100 | } | ||
3101 | |||
3091 | #ifdef CONFIG_VT_HW_CONSOLE_BINDING | 3102 | #ifdef CONFIG_VT_HW_CONSOLE_BINDING |
3092 | static int con_is_graphics(const struct consw *csw, int first, int last) | 3103 | static int con_is_graphics(const struct consw *csw, int first, int last) |
3093 | { | 3104 | { |
@@ -3199,9 +3210,9 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) | |||
3199 | if (!con_is_bound(csw)) | 3210 | if (!con_is_bound(csw)) |
3200 | con_driver->flag &= ~CON_DRIVER_FLAG_INIT; | 3211 | con_driver->flag &= ~CON_DRIVER_FLAG_INIT; |
3201 | 3212 | ||
3202 | console_unlock(); | ||
3203 | /* ignore return value, binding should not fail */ | 3213 | /* ignore return value, binding should not fail */ |
3204 | bind_con_driver(defcsw, first, last, deflt); | 3214 | do_bind_con_driver(defcsw, first, last, deflt); |
3215 | console_unlock(); | ||
3205 | err: | 3216 | err: |
3206 | module_put(owner); | 3217 | module_put(owner); |
3207 | return retval; | 3218 | return retval; |
@@ -3492,28 +3503,18 @@ int con_debug_leave(void) | |||
3492 | } | 3503 | } |
3493 | EXPORT_SYMBOL_GPL(con_debug_leave); | 3504 | EXPORT_SYMBOL_GPL(con_debug_leave); |
3494 | 3505 | ||
3495 | /** | 3506 | static int do_register_con_driver(const struct consw *csw, int first, int last) |
3496 | * register_con_driver - register console driver to console layer | ||
3497 | * @csw: console driver | ||
3498 | * @first: the first console to take over, minimum value is 0 | ||
3499 | * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1 | ||
3500 | * | ||
3501 | * DESCRIPTION: This function registers a console driver which can later | ||
3502 | * bind to a range of consoles specified by @first and @last. It will | ||
3503 | * also initialize the console driver by calling con_startup(). | ||
3504 | */ | ||
3505 | int register_con_driver(const struct consw *csw, int first, int last) | ||
3506 | { | 3507 | { |
3507 | struct module *owner = csw->owner; | 3508 | struct module *owner = csw->owner; |
3508 | struct con_driver *con_driver; | 3509 | struct con_driver *con_driver; |
3509 | const char *desc; | 3510 | const char *desc; |
3510 | int i, retval = 0; | 3511 | int i, retval = 0; |
3511 | 3512 | ||
3513 | WARN_CONSOLE_UNLOCKED(); | ||
3514 | |||
3512 | if (!try_module_get(owner)) | 3515 | if (!try_module_get(owner)) |
3513 | return -ENODEV; | 3516 | return -ENODEV; |
3514 | 3517 | ||
3515 | console_lock(); | ||
3516 | |||
3517 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { | 3518 | for (i = 0; i < MAX_NR_CON_DRIVER; i++) { |
3518 | con_driver = ®istered_con_driver[i]; | 3519 | con_driver = ®istered_con_driver[i]; |
3519 | 3520 | ||
@@ -3566,10 +3567,29 @@ int register_con_driver(const struct consw *csw, int first, int last) | |||
3566 | } | 3567 | } |
3567 | 3568 | ||
3568 | err: | 3569 | err: |
3569 | console_unlock(); | ||
3570 | module_put(owner); | 3570 | module_put(owner); |
3571 | return retval; | 3571 | return retval; |
3572 | } | 3572 | } |
3573 | |||
3574 | /** | ||
3575 | * register_con_driver - register console driver to console layer | ||
3576 | * @csw: console driver | ||
3577 | * @first: the first console to take over, minimum value is 0 | ||
3578 | * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1 | ||
3579 | * | ||
3580 | * DESCRIPTION: This function registers a console driver which can later | ||
3581 | * bind to a range of consoles specified by @first and @last. It will | ||
3582 | * also initialize the console driver by calling con_startup(). | ||
3583 | */ | ||
3584 | int register_con_driver(const struct consw *csw, int first, int last) | ||
3585 | { | ||
3586 | int retval; | ||
3587 | |||
3588 | console_lock(); | ||
3589 | retval = do_register_con_driver(csw, first, last); | ||
3590 | console_unlock(); | ||
3591 | return retval; | ||
3592 | } | ||
3573 | EXPORT_SYMBOL(register_con_driver); | 3593 | EXPORT_SYMBOL(register_con_driver); |
3574 | 3594 | ||
3575 | /** | 3595 | /** |
@@ -3623,17 +3643,44 @@ EXPORT_SYMBOL(unregister_con_driver); | |||
3623 | * when a driver wants to take over some existing consoles | 3643 | * when a driver wants to take over some existing consoles |
3624 | * and become default driver for newly opened ones. | 3644 | * and become default driver for newly opened ones. |
3625 | * | 3645 | * |
3626 | * take_over_console is basically a register followed by unbind | 3646 | * take_over_console is basically a register followed by unbind |
3647 | */ | ||
3648 | int do_take_over_console(const struct consw *csw, int first, int last, int deflt) | ||
3649 | { | ||
3650 | int err; | ||
3651 | |||
3652 | err = do_register_con_driver(csw, first, last); | ||
3653 | /* | ||
3654 | * If we get an busy error we still want to bind the console driver | ||
3655 | * and return success, as we may have unbound the console driver | ||
3656 | * but not unregistered it. | ||
3657 | */ | ||
3658 | if (err == -EBUSY) | ||
3659 | err = 0; | ||
3660 | if (!err) | ||
3661 | do_bind_con_driver(csw, first, last, deflt); | ||
3662 | |||
3663 | return err; | ||
3664 | } | ||
3665 | EXPORT_SYMBOL_GPL(do_take_over_console); | ||
3666 | |||
3667 | /* | ||
3668 | * If we support more console drivers, this function is used | ||
3669 | * when a driver wants to take over some existing consoles | ||
3670 | * and become default driver for newly opened ones. | ||
3671 | * | ||
3672 | * take_over_console is basically a register followed by unbind | ||
3627 | */ | 3673 | */ |
3628 | int take_over_console(const struct consw *csw, int first, int last, int deflt) | 3674 | int take_over_console(const struct consw *csw, int first, int last, int deflt) |
3629 | { | 3675 | { |
3630 | int err; | 3676 | int err; |
3631 | 3677 | ||
3632 | err = register_con_driver(csw, first, last); | 3678 | err = register_con_driver(csw, first, last); |
3633 | /* if we get an busy error we still want to bind the console driver | 3679 | /* |
3680 | * If we get an busy error we still want to bind the console driver | ||
3634 | * and return success, as we may have unbound the console driver | 3681 | * and return success, as we may have unbound the console driver |
3635 | * but not unregistered it. | 3682 | * but not unregistered it. |
3636 | */ | 3683 | */ |
3637 | if (err == -EBUSY) | 3684 | if (err == -EBUSY) |
3638 | err = 0; | 3685 | err = 0; |
3639 | if (!err) | 3686 | if (!err) |