From de4c13f0938fb46c21b55b328bd22132207f860b Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 14 Jan 2014 19:01:44 +0200 Subject: [PATCH 1/2] tty/vt: fix lock dependency during console unregistering Signed-off-by: Imre Deak --- drivers/tty/vt/vt.c | 52 ++++++++++++++++++++++++++++++------------- drivers/video/console/fbcon.c | 1 + drivers/video/fbmem.c | 2 ++ include/linux/console.h | 1 + 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 61b1137..44e9f9e 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -108,6 +108,7 @@ #define CON_DRIVER_FLAG_MODULE 1 #define CON_DRIVER_FLAG_INIT 2 #define CON_DRIVER_FLAG_ATTR 4 +#define CON_DRIVER_FLAG_ZOMBIE 8 struct con_driver { const struct consw *con; @@ -3234,11 +3235,8 @@ static int vt_bind(struct con_driver *con) if (first == 0 && last == MAX_NR_CONSOLES -1) deflt = 1; - if (first != -1) { - console_lock(); + if (first != -1) do_bind_con_driver(csw, first, last, deflt); - console_unlock(); - } first = -1; last = -1; @@ -3276,11 +3274,8 @@ static int vt_unbind(struct con_driver *con) if (first == 0 && last == MAX_NR_CONSOLES -1) deflt = 1; - if (first != -1) { - console_lock(); + if (first != -1) do_unbind_con_driver(csw, first, last, deflt); - console_unlock(); - } first = -1; last = -1; @@ -3307,11 +3302,15 @@ static ssize_t store_bind(struct device *dev, struct device_attribute *attr, struct con_driver *con = dev_get_drvdata(dev); int bind = simple_strtoul(buf, NULL, 0); + console_lock(); + if (bind) vt_bind(con); else vt_unbind(con); + console_unlock(); + return count; } @@ -3517,7 +3516,8 @@ static int do_register_con_driver(const struct consw *csw, int first, int last) for (i = 0; i < MAX_NR_CON_DRIVER; i++) { con_driver = ®istered_con_driver[i]; - if (con_driver->con == NULL) { + if (con_driver->con == NULL && + !(con_driver->flag & CON_DRIVER_FLAG_ZOMBIE)) { con_driver->con = csw; con_driver->desc = desc; con_driver->node = i; @@ -3577,24 +3577,43 @@ int do_unregister_con_driver(const struct consw *csw) if (con_driver->con == csw && con_driver->flag & CON_DRIVER_FLAG_MODULE) { + con_driver->con = NULL; + con_driver->flag = CON_DRIVER_FLAG_ZOMBIE; + retval = 0; + break; + } + } +err: + return retval; +} +EXPORT_SYMBOL_GPL(do_unregister_con_driver); + +void prune_zombie_con_drivers(void) +{ + int i; + + console_lock(); + for (i = 0; i < MAX_NR_CON_DRIVER; i++) { + struct con_driver *con_driver = ®istered_con_driver[i]; + + if (con_driver->flag & CON_DRIVER_FLAG_ZOMBIE) { + console_unlock(); vtconsole_deinit_device(con_driver); device_destroy(vtconsole_class, MKDEV(0, con_driver->node)); - con_driver->con = NULL; + console_lock(); con_driver->desc = NULL; + con_driver->flag &= ~CON_DRIVER_FLAG_ZOMBIE; + WARN_ON(con_driver->flag); con_driver->dev = NULL; con_driver->node = 0; - con_driver->flag = 0; con_driver->first = 0; con_driver->last = 0; - retval = 0; - break; } } -err: - return retval; + console_unlock(); } -EXPORT_SYMBOL_GPL(do_unregister_con_driver); +EXPORT_SYMBOL_GPL(prune_zombie_con_drivers); /* * If we support more console drivers, this function is used @@ -3632,6 +3651,7 @@ void give_up_console(const struct consw *csw) console_lock(); do_unregister_con_driver(csw); console_unlock(); + prune_zombie_con_drivers(); } static int __init vtconsole_class_init(void) diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index cd8a802..26d180c 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -3623,6 +3623,7 @@ static void __exit fb_console_exit(void) fbcon_exit(); do_unregister_con_driver(&fb_con); console_unlock(); + prune_zombie_con_drivers(); } module_exit(fb_console_exit); diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index e296967..1a7cf9e 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1725,6 +1725,8 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); console_unlock(); + prune_zombie_con_drivers(); + /* this may free fb info */ put_fb_info(fb_info); return 0; diff --git a/include/linux/console.h b/include/linux/console.h index 7571a16..b7bf097 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -76,6 +76,7 @@ extern const struct consw prom_con; /* SPARC PROM console */ int con_is_bound(const struct consw *csw); int do_unregister_con_driver(const struct consw *csw); +void prune_zombie_con_drivers(void); int do_take_over_console(const struct consw *sw, int first, int last, int deflt); void give_up_console(const struct consw *sw); #ifdef CONFIG_HW_CONSOLE -- 1.8.4