Skip to contentSkip to conversation

VT locking

PATCH In review Benjamin Herrenschmidt 4 participants 6 messages Jan 6, 2004

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 VT locking

Hi !

The VT code is currently, it seems, full of races, it basically doesn't do any locking... This patch is definitely not fixing everything, but at least fixes some of the most obvious ones by putting things under the umbrella of the console semaphore. For debugging purposes, I added an is_console_locked() call to kernel/printk.c along with a bunch of WARN_ON in low level VT functions that I think should be protected.

I suppose we should merge this at least into -mm. Getting the semaphore around vc_resize, at least, seems necessary to avoid nasty problems with fbdev's especally when those try to output some debug printk's in the mode setting code.

I also moved the blanking code down to the console work queue instead of calling the lower level blank right at interrupt time. Unblank is still possibly called at interrupt time by printk/panic, but then, the console sem will be held. The idea here is to properly protect calls to the low level driver with the console semaphore. Without this, quite bad things can happen, for example if blank() happens to preempt a mode switch initiated by userland.

Note that the current console code definitely takes too much time in keventd. It would probably be wise to consider turning these work queues abuses into a console thread... I keep that for later.

Ben.

drivers/char/selection.c +4 -0
diff -urN linux-2.5/drivers/char/selection.c linuxppc-2.5-benh/drivers/char/selection.c
@@ -24,6 +24,7 @@
 #include <linux/consolemap.h>
 #include <linux/selection.h>
 #include <linux/tiocl.h>
+#include <linux/console.h>
 
 #ifndef MIN
 #define MIN(a,b)	((a) < (b) ? (a) : (b))
@@ -290,7 +291,10 @@
 	int	pasted = 0, count;
 	DECLARE_WAITQUEUE(wait, current);
 
+	acquire_console_sem();
 	poke_blanked_console();
+	release_console_sem();
+
 	add_wait_queue(&vt->paste_wait, &wait);
 	while (sel_buffer && sel_buffer_lth > pasted) {
 		set_current_state(TASK_INTERRUPTIBLE);
drivers/char/tty_io.c +6 -1
diff -urN linux-2.5/drivers/char/tty_io.c linuxppc-2.5-benh/drivers/char/tty_io.c
@@ -1484,7 +1484,12 @@
 #ifdef CONFIG_VT
 	if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) {
 		unsigned int currcons = tty->index;
-		if (vc_resize(currcons, tmp_ws.ws_col, tmp_ws.ws_row))
+		int rc;
+
+		acquire_console_sem();	       
+		rc = vc_resize(currcons, tmp_ws.ws_col, tmp_ws.ws_row);
+		release_console_sem();
+		if (rc)
 			return -ENXIO;
 	}
 #endif
drivers/char/vt.c +85 -40

307 lines not on this page · raw message

drivers/char/vt_ioctl.c +19 -4
diff -urN linux-2.5/drivers/char/vt_ioctl.c linuxppc-2.5-benh/drivers/char/vt_ioctl.c
@@ -470,6 +470,9 @@
 		 * currently, setting the mode from KD_TEXT to KD_GRAPHICS
 		 * doesn't do a whole lot. i'm not sure if it should do any
 		 * restoration of modes or what...
+		 *
+		 * XXX It should at least call into the driver, fbdev's definitely
+		 * need to restore their engine state. --BenH
 		 */
 		if (!perm)
 			return -EPERM;
@@ -492,10 +495,12 @@
 		/*
 		 * explicitly blank/unblank the screen if switching modes
 		 */
+		acquire_console_sem();
 		if (arg == KD_TEXT)
 			unblank_screen();
 		else
 			do_blank_screen(1);
+		release_console_sem();
 		return 0;
 
 	case KDGETMODE:
@@ -718,7 +723,9 @@
 		if (arg == 0 || arg > MAX_NR_CONSOLES)
 			return -ENXIO;
 		arg--;
+		acquire_console_sem();
 		i = vc_allocate(arg);
+		release_console_sem();
 		if (i)
 			return i;
 		set_console(arg);
@@ -768,17 +775,20 @@
 				 * The current vt has been released, so
 				 * complete the switch.
 				 */
-				int newvt = vt_cons[console]->vt_newvt;
+				int newvt;
+				acquire_console_sem();
+				newvt = vt_cons[console]->vt_newvt;
 				vt_cons[console]->vt_newvt = -1;
 				i = vc_allocate(newvt);
-				if (i)
+				if (i) {
+					release_console_sem();
 					return i;
+				}
 				/*
 				 * When we actually do the console switch,
 				 * make sure we are atomic with respect to
 				 * other console switches..
 				 */
-				acquire_console_sem();
 				complete_change_console(newvt);
 				release_console_sem();
 			}
@@ -828,8 +838,11 @@
 		if (get_user(ll, &vtsizes->v_rows) ||
 		    get_user(cc, &vtsizes->v_cols))
 			return -EFAULT;
-		for (i = 0; i < MAX_NR_CONSOLES; i++)
+		for (i = 0; i < MAX_NR_CONSOLES; i++) {
+			acquire_console_sem();
                         vc_resize(i, cc, ll);
+			release_console_sem();
+		}
 		return 0;
 	}
 
@@ -870,11 +883,13 @@
 		for (i = 0; i < MAX_NR_CONSOLES; i++) {
 			if (!vc_cons[i].d)
 				continue;
+			acquire_console_sem();
 			if (vlin)
 				vc_cons[i].d->vc_scan_lines = vlin;
 			if (clin)
 				vc_cons[i].d->vc_font.height = clin;
 			vc_resize(i, cc, ll);
+			release_console_sem();
 		}
   		return 0;
 	}
kernel/power/console.c +10 -6
diff -urN linux-2.5/kernel/power/console.c linuxppc-2.5-benh/kernel/power/console.c
@@ -6,6 +6,7 @@
 
 #include <linux/vt_kern.h>
 #include <linux/kbd_kern.h>
+#include <linux/console.h>
 #include "power.h"
 
 static int new_loglevel = 10;
@@ -18,14 +19,20 @@
 	console_loglevel = new_loglevel;
 
 #ifdef SUSPEND_CONSOLE
+	acquire_console_sem();
+
 	orig_fgconsole = fg_console;
 
-	if (vc_allocate(SUSPEND_CONSOLE))
+	if (vc_allocate(SUSPEND_CONSOLE)) {
 	  /* we can't have a free VC for now. Too bad,
 	   * we don't want to mess the screen for now. */
+		release_console_sem();
 		return 1;
+	}
 
 	set_console(SUSPEND_CONSOLE);
+	release_console_sem();
+
 	if (vt_waitactive(SUSPEND_CONSOLE)) {
 		pr_debug("Suspend: Can't switch VCs.");
 		return 1;
@@ -40,12 +47,9 @@
 {
 	console_loglevel = orig_loglevel;
 #ifdef SUSPEND_CONSOLE
+	acquire_console_sem();
 	set_console(orig_fgconsole);
-
-	/* FIXME: 
-	 * This following part is left over from swsusp. Is it really needed?
-	 */
-	update_screen(fg_console);
+	release_console_sem();
 #endif
 	return;
 }
kernel/printk.c +32 -0
diff -urN linux-2.5/kernel/printk.c linuxppc-2.5-benh/kernel/printk.c
@@ -33,6 +33,11 @@
 
 #include <asm/uaccess.h>
 
+#ifdef CONFIG_BOOTX_TEXT
+#include <asm/btext.h>
+#endif
+
+
 #define __LOG_BUF_LEN	(1 << CONFIG_LOG_BUF_SHIFT)
 
 /* printk's without a loglevel use this.. */
@@ -62,6 +67,15 @@
  */
 static DECLARE_MUTEX(console_sem);
 struct console *console_drivers;
+/*
+ * This is used for debugging the mess that is the VT code by
+ * keeping track if we have the console semaphore held. It's
+ * definitely not the perfect debug tool (we don't know if _WE_
+ * hold it are racing, but it helps tracking those weird code
+ * path in the console code where we end up in places I want
+ * locked without the console sempahore held
+ */
+static int console_locked;
 
 /*
  * logbuf_lock protects log_buf, log_start, log_end, con_start and logged_chars
@@ -479,6 +493,9 @@
 	char *p;
 	static char printk_buf[1024];
 	static int log_level_unknown = 1;
+#ifdef CONFIG_BOOTX_TEXT
+	extern int force_printk_to_btext;
+#endif
 
 	if (oops_in_progress) {
 		/* If a crash is occurring, make sure we can't deadlock */
@@ -494,6 +511,10 @@
 	va_start(args, fmt);
 	printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
 	va_end(args);
+#ifdef CONFIG_BOOTX_TEXT
+	if (force_printk_to_btext)
+		btext_drawstring(printk_buf);
+#endif /* CONFIG_BOOTX_TEXT */
 
 	/*
 	 * Copy the output into log_buf.  If the caller didn't provide
@@ -524,6 +545,7 @@
 		goto out;
 	}
 	if (!down_trylock(&console_sem)) {
+		console_locked = 1;
 		/*
 		 * We own the drivers.  We can drop the spinlock and let
 		 * release_console_sem() print the text
@@ -557,10 +579,17 @@
 	if (in_interrupt())
 		BUG();
 	down(&console_sem);
+	console_locked = 1;
 	console_may_schedule = 1;
 }
 EXPORT_SYMBOL(acquire_console_sem);
 
+int is_console_locked(void)
+{
+	return console_locked;
+}
+EXPORT_SYMBOL(is_console_locked);
+
 /**
  * release_console_sem - unlock the console system
  *
@@ -592,12 +621,14 @@
 		spin_unlock_irqrestore(&logbuf_lock, flags);
 		call_console_drivers(_con_start, _log_end);
 	}
+	console_locked = 0;
 	console_may_schedule = 0;
 	up(&console_sem);
 	spin_unlock_irqrestore(&logbuf_lock, flags);
 	if (wake_klogd && !oops_in_progress && waitqueue_active(&log_wait))
 		wake_up_interruptible(&log_wait);
 }
+EXPORT_SYMBOL(release_console_sem);
 
 /** console_conditional_schedule - yield the CPU if required
  *
@@ -633,6 +664,7 @@
 	 */
 	if (down_trylock(&console_sem) != 0)
 		return;
+	console_locked = 1;
 	console_may_schedule = 0;
 	for (c = console_drivers; c != NULL; c = c->next)
 		if ((c->flags & CON_ENABLED) && c->unblank)
Signature · 9 lines
Benjamin Herrenschmidt <[email protected]>



-------------------------------------------------------
This SF.net email is sponsored by: IBM Linux Tutorials.
Become an expert in LINUX or just sharpen your skills.  Sign up for IBM's
Free Linux Tutorials.  Learn everything from the bash shell to sys admin.
Click now! http://ads.osdn.com/?ad_id=1278&alloc_id=3371&op=click
#1 lore raw

Hi!

Quoted 3 lines

The VT code is currently, it seems, full of races, it basically doesn't do any locking... This patch is definitely not fixing everything, but at

And the races bite, BTW. For years I was seeing weird stuff like console output on blanked console if scroll happened at approximately same time as blank. It was hard to reproduce reliably, through.
Pavel

Signature · 2 lines
When do you have a heart between your knees?
[Johanka's followup: and *two* hearts?]
#2 lore raw
In reply to Benjamin Herrenschmidt
Quoted 13 lines · Benjamin Herrenschmidt
kernel/printk.c +3 -0
@@ -479,6 +493,9 @@
 	char *p;
 	static char printk_buf[1024];
 	static int log_level_unknown = 1;
+#ifdef CONFIG_BOOTX_TEXT
+	extern int force_printk_to_btext;
+#endif
 
 	if (oops_in_progress) {
 		/* If a crash is occurring, make sure we can't deadlock */

kernel/printk.c line 517

Quoted 7 lines
kernel/printk.c +4 -0
@@ -494,6 +511,10 @@
 	va_start(args, fmt);
 	printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
 	va_end(args);
+#ifdef CONFIG_BOOTX_TEXT
+	if (force_printk_to_btext)
+		btext_drawstring(printk_buf);
+#endif /* CONFIG_BOOTX_TEXT */

Looks like a nice opportunity to introduce an arch-specific printk() stub:

    void *arch_printk(const char *args);

    if (arch_printk)
	arch_printk(printk_buf);

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]

In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds

-------------------------------------------------------
This SF.net email is sponsored by: IBM Linux Tutorials. Become an expert in LINUX or just sharpen your skills. Sign up for IBM's Free Linux Tutorials. Learn everything from the bash shell to sys admin. Click now! http://ads.osdn.com/?ad_id=1278&alloc_id=3371&op=click

#3 lore raw
Quoted 8 lines

Looks like a nice opportunity to introduce an arch-specific printk() stub:

    void *arch_printk(const char *args);

    if (arch_printk)
	arch_printk(printk_buf);

Right... I admit I didn't even notice the printk_btext stuff leaked with this patch, this is debug stuff that wasn't really supposed to get out of my tree, oh well... I shouldn't do patches when I'm sick with +40° fever...

Andrew, the only bits of the kernel/printk.c that are supposed to get to your tree are related to is_console_locked() at this point. The force_printk_to_btext is a debug tool that allow to route all printk's to some early-boot output mecanism, though it would eventually be acceptable upstream with Geert's idea of arch_printk...

Cheers,
Ben.

-------------------------------------------------------
This SF.net email is sponsored by: IBM Linux Tutorials. Become an expert in LINUX or just sharpen your skills. Sign up for IBM's Free Linux Tutorials. Learn everything from the bash shell to sys admin. Click now! http://ads.osdn.com/?ad_id=1278&alloc_id=3371&op=click

#4 lore raw
Quoted 6 lines · Benjamin Herrenschmidt

Andrew, the only bits of the kernel/printk.c that are supposed to get to your tree are related to is_console_locked() at this point. The force_printk_to_btext is a debug tool that allow to route all printk's to some early-boot output mecanism, though it would eventually be acceptable upstream with Geert's idea of arch_printk...

That's OK, it's just in there for a soak-test at present - this stuff is quite a long way away, yes?

It's probably just easiest for you to work against current -bk, send me a single rolled up patch whenever you have new things for people to test. When we're happy with it all we can do a bk merge of everything.

#5 lore raw
Quoted 3 lines

That's OK, it's just in there for a soak-test at present - this stuff is quite a long way away, yes?

The VT locking on the debug crude hack ? :)

Well it depends :) If nobody complains, it could get in pretty fast :) In any case, please report me any trigger of the WARN_ON... I didn't chield everything yet with the console sem, so there may still be some of them coming from the low level code.

Quoted 3 lines

It's probably just easiest for you to work against current -bk, send me a single rolled up patch whenever you have new things for people to test. When we're happy with it all we can do a bk merge of everything.

Ok.