From 6d4e751e108b3a2333412b603911f8243e60a86e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Apr 2015 11:22:06 +0200 Subject: tty: consolemap.c: move assignment out of if () block We should not be doing assignments within an if () block so fix up the code to not do this. change was created using Coccinelle. CC: Jiri Slaby CC: Takashi Iwai CC: Imre Deak Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/consolemap.c | 60 ++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 23 deletions(-) (limited to 'drivers/tty/vt') diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 59b25e039968..c8c91f0476a2 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -261,19 +261,22 @@ u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode) int m; if (glyph < 0 || glyph >= MAX_GLYPH) return 0; - else if (!(p = *conp->vc_uni_pagedir_loc)) - return glyph; - else if (use_unicode) { - if (!p->inverse_trans_unicode) + else { + p = *conp->vc_uni_pagedir_loc; + if (!p) return glyph; - else - return p->inverse_trans_unicode[glyph]; - } else { - m = inv_translate[conp->vc_num]; - if (!p->inverse_translations[m]) - return glyph; - else - return p->inverse_translations[m][glyph]; + else if (use_unicode) { + if (!p->inverse_trans_unicode) + return glyph; + else + return p->inverse_trans_unicode[glyph]; + } else { + m = inv_translate[conp->vc_num]; + if (!p->inverse_translations[m]) + return glyph; + else + return p->inverse_translations[m][glyph]; + } } } EXPORT_SYMBOL_GPL(inverse_translate); @@ -397,7 +400,8 @@ static void con_release_unimap(struct uni_pagedir *p) if (p == dflt) dflt = NULL; for (i = 0; i < 32; i++) { - if ((p1 = p->uni_pgdir[i]) != NULL) { + p1 = p->uni_pgdir[i]; + if (p1 != NULL) { for (j = 0; j < 32; j++) kfree(p1[j]); kfree(p1); @@ -473,14 +477,16 @@ con_insert_unipair(struct uni_pagedir *p, u_short unicode, u_short fontpos) int i, n; u16 **p1, *p2; - if (!(p1 = p->uni_pgdir[n = unicode >> 11])) { + p1 = p->uni_pgdir[n = unicode >> 11]; + if (!p1) { p1 = p->uni_pgdir[n] = kmalloc(32*sizeof(u16 *), GFP_KERNEL); if (!p1) return -ENOMEM; for (i = 0; i < 32; i++) p1[i] = NULL; } - if (!(p2 = p1[n = (unicode >> 6) & 0x1f])) { + p2 = p1[n = (unicode >> 6) & 0x1f]; + if (!p2) { p2 = p1[n] = kmalloc(64*sizeof(u16), GFP_KERNEL); if (!p2) return -ENOMEM; memset(p2, 0xff, 64*sizeof(u16)); /* No glyphs for the characters (yet) */ @@ -569,10 +575,12 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) * entries from "p" (old) to "q" (new). */ l = 0; /* unicode value */ - for (i = 0; i < 32; i++) - if ((p1 = p->uni_pgdir[i])) - for (j = 0; j < 32; j++) - if ((p2 = p1[j])) { + for (i = 0; i < 32; i++) { + p1 = p->uni_pgdir[i]; + if (p1) + for (j = 0; j < 32; j++) { + p2 = p1[j]; + if (p2) { for (k = 0; k < 64; k++, l++) if (p2[k] != 0xffff) { /* @@ -593,9 +601,11 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) /* Account for row of 64 empty entries */ l += 64; } + } else /* Account for empty table */ l += 32 * 64; + } /* * Finished copying font table, set vc_uni_pagedir to new table @@ -735,10 +745,12 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni ect = 0; if (*vc->vc_uni_pagedir_loc) { p = *vc->vc_uni_pagedir_loc; - for (i = 0; i < 32; i++) - if ((p1 = p->uni_pgdir[i])) - for (j = 0; j < 32; j++) - if ((p2 = *(p1++))) + for (i = 0; i < 32; i++) { + p1 = p->uni_pgdir[i]; + if (p1) + for (j = 0; j < 32; j++) { + p2 = *(p1++); + if (p2) for (k = 0; k < 64; k++) { if (*p2 < MAX_GLYPH && ect++ < ct) { __put_user((u_short)((i<<11)+(j<<6)+k), @@ -749,6 +761,8 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni } p2++; } + } + } } __put_user(ect, uct); console_unlock(); -- cgit v1.2.1 From bd63364caa8df38bad2b25b11b2a1b849475cce5 Mon Sep 17 00:00:00 2001 From: Scot Doyle Date: Thu, 26 Mar 2015 13:54:39 +0000 Subject: vt: add cursor blink interval escape sequence Add an escape sequence to specify the current console's cursor blink interval. The interval is specified as a number of milliseconds until the next cursor display state toggle, from 50 to 65535. /proc/loadavg did not show a difference with a one msec interval, but the lower bound is set to 50 msecs since slower hardware wasn't tested. Store the interval in the vc_data structure for later access by fbcon, initializing the value to fbcon's current hardcoded value of 200 msecs. Signed-off-by: Scot Doyle Acked-by: Pavel Machek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/tty/vt') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 4a24eb2b0ede..b075489f314e 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -135,6 +135,7 @@ const struct consw *conswitchp; */ #define DEFAULT_BELL_PITCH 750 #define DEFAULT_BELL_DURATION (HZ/8) +#define DEFAULT_CURSOR_BLINK_MS 200 struct vc vc_cons [MAX_NR_CONSOLES]; @@ -1590,6 +1591,13 @@ static void setterm_command(struct vc_data *vc) case 15: /* activate the previous console */ set_console(last_console); break; + case 16: /* set cursor blink duration in msec */ + if (vc->vc_npar >= 1 && vc->vc_par[1] >= 50 && + vc->vc_par[1] <= USHRT_MAX) + vc->vc_cur_blink_ms = vc->vc_par[1]; + else + vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS; + break; } } @@ -1717,6 +1725,7 @@ static void reset_terminal(struct vc_data *vc, int do_clear) vc->vc_bell_pitch = DEFAULT_BELL_PITCH; vc->vc_bell_duration = DEFAULT_BELL_DURATION; + vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS; gotoxy(vc, 0, 0); save_cur(vc); -- cgit v1.2.1 From d364b5c3e446bbf9892edbb000bf4c358f86bd8b Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 1 Apr 2015 21:06:16 +0300 Subject: 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 Reviewed-by: Daniel Vetter Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 61 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 10 deletions(-) (limited to 'drivers/tty/vt') 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 @@ #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; @@ -154,6 +155,7 @@ static int set_vesa_blanking(char __user *p); static void set_cursor(struct vc_data *vc); static void hide_cursor(struct vc_data *vc); static void console_callback(struct work_struct *ignored); +static void con_driver_unregister_callback(struct work_struct *ignored); static void blank_screen_t(unsigned long dummy); static void set_palette(struct vc_data *vc); @@ -183,6 +185,7 @@ static int blankinterval = 10*60; core_param(consoleblank, blankinterval, int, 0444); static DECLARE_WORK(console_work, console_callback); +static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback); /* * 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) 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; @@ -3667,16 +3671,20 @@ int do_unregister_con_driver(const struct consw *csw) struct con_driver *con_driver = ®istered_con_driver[i]; if (con_driver->con == csw) { - vtconsole_deinit_device(con_driver); - device_destroy(vtconsole_class, - MKDEV(0, con_driver->node)); + /* + * Defer the removal of the sysfs entries since that + * will acquire the kernfs s_active lock and we can't + * acquire this lock while holding the console lock: + * the unbind sysfs entry imposes already the opposite + * order. Reset con already here to prevent any later + * lookup to succeed and mark this slot as zombie, so + * it won't get reused until we complete the removal + * in the deferred work. + */ con_driver->con = NULL; - con_driver->desc = NULL; - con_driver->dev = NULL; - con_driver->node = 0; - con_driver->flag = 0; - con_driver->first = 0; - con_driver->last = 0; + con_driver->flag = CON_DRIVER_FLAG_ZOMBIE; + schedule_work(&con_driver_unregister_work); + return 0; } } @@ -3685,6 +3693,39 @@ int do_unregister_con_driver(const struct consw *csw) } EXPORT_SYMBOL_GPL(do_unregister_con_driver); +static void con_driver_unregister_callback(struct work_struct *ignored) +{ + 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)) + continue; + + console_unlock(); + + vtconsole_deinit_device(con_driver); + device_destroy(vtconsole_class, MKDEV(0, con_driver->node)); + + console_lock(); + + if (WARN_ON_ONCE(con_driver->con)) + con_driver->con = NULL; + con_driver->desc = NULL; + con_driver->dev = NULL; + con_driver->node = 0; + WARN_ON_ONCE(con_driver->flag != CON_DRIVER_FLAG_ZOMBIE); + con_driver->flag = 0; + con_driver->first = 0; + con_driver->last = 0; + } + + console_unlock(); +} + /* * If we support more console drivers, this function is used * when a driver wants to take over some existing consoles -- cgit v1.2.1 From 77232f791e0605521a978cbe7f79a93df24df374 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 13 Apr 2015 11:16:21 +0200 Subject: vt: Don't check KD_GRAPHICS when binding/unbinding This was introduced in commit 6db4063c5b72b46e9793b0f141a7a3984ac6facf Author: Antonino A. Daplas Date: Mon Jun 26 00:27:12 2006 -0700 [PATCH] VT binding: Add sysfs control to the VT layer with the justification "In addition, if any of the consoles are in KD_GRAPHICS mode, binding and unbinding will not succeed. KD_GRAPHICS mode usually indicates that the underlying console hardware is used for other purposes other than displaying text (ie X). This feature should prevent binding/unbinding from interfering with a graphics application using the VT." I think we should lift this artificial restriction though: - KD_GRAPHICS doesn't get cleaned up automatically, which means it's easy to have terminals stuck in KD_GRAPHICS when hacking around on X. - X doesn't really care, especially with drm where kms already blocks fbdev (and hence fbcon) when there's an active compositor. - This is a root-only interface with a separate .config option and it's possible to hang your machine already anyway if you unload/reload drivers and don't know what you're doing. With this patch i915.ko module reloading works again reliably, something in the recent fedora upgrades broke things. Cc: Antonino A. Daplas Cc: Peter Hurley Cc: Imre Deak Signed-off-by: Daniel Vetter Acked-by: David Herrmann Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) (limited to 'drivers/tty/vt') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 08e303c4208c..8fe52989b380 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3204,22 +3204,6 @@ err: #ifdef CONFIG_VT_HW_CONSOLE_BINDING -static int con_is_graphics(const struct consw *csw, int first, int last) -{ - int i, retval = 0; - - for (i = first; i <= last; i++) { - struct vc_data *vc = vc_cons[i].d; - - if (vc && vc->vc_mode == KD_GRAPHICS) { - retval = 1; - break; - } - } - - return retval; -} - /* unlocked version of unbind_con_driver() */ int do_unbind_con_driver(const struct consw *csw, int first, int last, int deflt) { @@ -3305,8 +3289,7 @@ static int vt_bind(struct con_driver *con) const struct consw *defcsw = NULL, *csw = NULL; int i, more = 1, first = -1, last = -1, deflt = 0; - if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) || - con_is_graphics(con->con, con->first, con->last)) + if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE)) goto err; csw = con->con; @@ -3357,8 +3340,7 @@ static int vt_unbind(struct con_driver *con) int i, more = 1, first = -1, last = -1, deflt = 0; int ret; - if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) || - con_is_graphics(con->con, con->first, con->last)) + if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE)) goto err; csw = con->con; -- cgit v1.2.1