aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/vt
diff options
context:
space:
mode:
authorImre Deak <imre.deak@intel.com>2015-04-01 14:06:16 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-05-10 13:29:51 -0400
commitd364b5c3e446bbf9892edbb000bf4c358f86bd8b (patch)
tree191b00942c308a7809e1b0d7222feead25b95882 /drivers/tty/vt
parent80463501fe3a6cfdfb0aca92b1f2abbda61092a1 (diff)
vt: fix console lock vs. kernfs s_active lock order
Currently there is a lock order problem between the console lock and the kernfs s_active lock of the console driver's bind sysfs entry. When writing to the sysfs entry the lock order is first s_active then console lock, when unregistering the console driver via do_unregister_con_driver() the order is the opposite. See the below bugzilla reference for one instance of a lockdep backtrace. Fix this by unregistering the console driver from a deferred work, where we can safely drop the console lock while unregistering the device and corresponding sysfs entries (which in turn acquire s_active). Note that we have to keep the console driver slot in the registered_con_driver array reserved for the driver that's being unregistered until it's fully removed. Otherwise a concurrent call to do_register_con_driver could try to reuse the same slot and fail when registering the corresponding device with a minor index that's still in use. Note that the referenced bug report contains two dmesg logs with two distinct lockdep reports: [1] is about a locking scenario involving s_active, console_lock and the fb_notifier list lock, while [2] is about a locking scenario involving only s_active and console_lock. In [1] locking fb_notifier triggers the lockdep warning only because of its dependence on console_lock, otherwise case [1] is the same s_active<->console_lock dependency problem fixed by this patch. Before this change we have the following locking scenarios involving the 3 locks: a) via do_unregister_framebuffer()->...->do_unregister_con_driver(): 1. console lock 2. fb_notifier lock 3. s_active lock b) for example via give_up_console()->do_unregister_con_driver(): 1. console lock 2. s_active lock c) via vt_bind()/vt_unbind(): 1. s_active lock 2. console lock Since c) is the console bind sysfs entry's write code path we can't change the locking order there. We can only fix this issue by removing s_active's dependence on the other two locks in a) and b). We can do this only in the vt code which owns the corresponding sysfs entry, so that after the change we have: a) 1. console lock 2. fb_notifier lock b) 1. console lock c) 1. s_active lock 2. console lock d) in the new con_driver_unregister_callback(): 1. s_active lock [1] https://bugs.freedesktop.org/attachment.cgi?id=87716 [2] https://bugs.freedesktop.org/attachment.cgi?id=107602 v2: - get console_lock earlier in con_driver_unregister_callback(), so we protect the following console driver field assignments there - add code coment explaining the reason for deferring the sysfs entry removal - add a third paragraph to the commit message explaining why there are two distinct lockdep reports and that this issue is independent of fb/fbcon. (Peter Hurley) v3: - clarify further the third paragraph v4: - rebased on v4 of patch 1/3 Signed-off-by: Imre Deak <imre.deak@intel.com> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/vt')
-rw-r--r--drivers/tty/vt/vt.c61
1 files changed, 51 insertions, 10 deletions
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index b075489f314e..08e303c4208c 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -108,6 +108,7 @@
108#define CON_DRIVER_FLAG_MODULE 1 108#define CON_DRIVER_FLAG_MODULE 1
109#define CON_DRIVER_FLAG_INIT 2 109#define CON_DRIVER_FLAG_INIT 2
110#define CON_DRIVER_FLAG_ATTR 4 110#define CON_DRIVER_FLAG_ATTR 4
111#define CON_DRIVER_FLAG_ZOMBIE 8
111 112
112struct con_driver { 113struct con_driver {
113 const struct consw *con; 114 const struct consw *con;
@@ -154,6 +155,7 @@ static int set_vesa_blanking(char __user *p);
154static void set_cursor(struct vc_data *vc); 155static void set_cursor(struct vc_data *vc);
155static void hide_cursor(struct vc_data *vc); 156static void hide_cursor(struct vc_data *vc);
156static void console_callback(struct work_struct *ignored); 157static void console_callback(struct work_struct *ignored);
158static void con_driver_unregister_callback(struct work_struct *ignored);
157static void blank_screen_t(unsigned long dummy); 159static void blank_screen_t(unsigned long dummy);
158static void set_palette(struct vc_data *vc); 160static void set_palette(struct vc_data *vc);
159 161
@@ -183,6 +185,7 @@ static int blankinterval = 10*60;
183core_param(consoleblank, blankinterval, int, 0444); 185core_param(consoleblank, blankinterval, int, 0444);
184 186
185static DECLARE_WORK(console_work, console_callback); 187static DECLARE_WORK(console_work, console_callback);
188static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback);
186 189
187/* 190/*
188 * fg_console is the current virtual console, 191 * fg_console is the current virtual console,
@@ -3605,7 +3608,8 @@ static int do_register_con_driver(const struct consw *csw, int first, int last)
3605 for (i = 0; i < MAX_NR_CON_DRIVER; i++) { 3608 for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
3606 con_driver = &registered_con_driver[i]; 3609 con_driver = &registered_con_driver[i];
3607 3610
3608 if (con_driver->con == NULL) { 3611 if (con_driver->con == NULL &&
3612 !(con_driver->flag & CON_DRIVER_FLAG_ZOMBIE)) {
3609 con_driver->con = csw; 3613 con_driver->con = csw;
3610 con_driver->desc = desc; 3614 con_driver->desc = desc;
3611 con_driver->node = i; 3615 con_driver->node = i;
@@ -3667,16 +3671,20 @@ int do_unregister_con_driver(const struct consw *csw)
3667 struct con_driver *con_driver = &registered_con_driver[i]; 3671 struct con_driver *con_driver = &registered_con_driver[i];
3668 3672
3669 if (con_driver->con == csw) { 3673 if (con_driver->con == csw) {
3670 vtconsole_deinit_device(con_driver); 3674 /*
3671 device_destroy(vtconsole_class, 3675 * Defer the removal of the sysfs entries since that
3672 MKDEV(0, con_driver->node)); 3676 * will acquire the kernfs s_active lock and we can't
3677 * acquire this lock while holding the console lock:
3678 * the unbind sysfs entry imposes already the opposite
3679 * order. Reset con already here to prevent any later
3680 * lookup to succeed and mark this slot as zombie, so
3681 * it won't get reused until we complete the removal
3682 * in the deferred work.
3683 */
3673 con_driver->con = NULL; 3684 con_driver->con = NULL;
3674 con_driver->desc = NULL; 3685 con_driver->flag = CON_DRIVER_FLAG_ZOMBIE;
3675 con_driver->dev = NULL; 3686 schedule_work(&con_driver_unregister_work);
3676 con_driver->node = 0; 3687
3677 con_driver->flag = 0;
3678 con_driver->first = 0;
3679 con_driver->last = 0;
3680 return 0; 3688 return 0;
3681 } 3689 }
3682 } 3690 }
@@ -3685,6 +3693,39 @@ int do_unregister_con_driver(const struct consw *csw)
3685} 3693}
3686EXPORT_SYMBOL_GPL(do_unregister_con_driver); 3694EXPORT_SYMBOL_GPL(do_unregister_con_driver);
3687 3695
3696static void con_driver_unregister_callback(struct work_struct *ignored)
3697{
3698 int i;
3699
3700 console_lock();
3701
3702 for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
3703 struct con_driver *con_driver = &registered_con_driver[i];
3704
3705 if (!(con_driver->flag & CON_DRIVER_FLAG_ZOMBIE))
3706 continue;
3707
3708 console_unlock();
3709
3710 vtconsole_deinit_device(con_driver);
3711 device_destroy(vtconsole_class, MKDEV(0, con_driver->node));
3712
3713 console_lock();
3714
3715 if (WARN_ON_ONCE(con_driver->con))
3716 con_driver->con = NULL;
3717 con_driver->desc = NULL;
3718 con_driver->dev = NULL;
3719 con_driver->node = 0;
3720 WARN_ON_ONCE(con_driver->flag != CON_DRIVER_FLAG_ZOMBIE);
3721 con_driver->flag = 0;
3722 con_driver->first = 0;
3723 con_driver->last = 0;
3724 }
3725
3726 console_unlock();
3727}
3728
3688/* 3729/*
3689 * If we support more console drivers, this function is used 3730 * If we support more console drivers, this function is used
3690 * when a driver wants to take over some existing consoles 3731 * when a driver wants to take over some existing consoles