Browse Source

Add support for suspending and resuming the whole console subsystem

Trying to suspend/resume with console messages flying all around is
doomed to failure, when the devices that the messages are trying to
go to are being shut down.

Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Linus Torvalds 19 years ago
parent
commit
557240b48e
3 changed files with 33 additions and 1 deletions
  1. 4 0
      include/linux/console.h
  2. 2 0
      kernel/power/main.c
  3. 27 1
      kernel/printk.c

+ 4 - 0
include/linux/console.h

@@ -117,6 +117,10 @@ extern void console_stop(struct console *);
 extern void console_start(struct console *);
 extern void console_start(struct console *);
 extern int is_console_locked(void);
 extern int is_console_locked(void);
 
 
+/* Suspend and resume console messages over PM events */
+extern void suspend_console(void);
+extern void resume_console(void);
+
 /* Some debug stub to catch some of the obvious races in the VT code */
 /* Some debug stub to catch some of the obvious races in the VT code */
 #if 1
 #if 1
 #define WARN_CONSOLE_UNLOCKED()	WARN_ON(!is_console_locked() && !oops_in_progress)
 #define WARN_CONSOLE_UNLOCKED()	WARN_ON(!is_console_locked() && !oops_in_progress)

+ 2 - 0
kernel/power/main.c

@@ -86,6 +86,7 @@ static int suspend_prepare(suspend_state_t state)
 			goto Thaw;
 			goto Thaw;
 	}
 	}
 
 
+	suspend_console();
 	if ((error = device_suspend(PMSG_SUSPEND))) {
 	if ((error = device_suspend(PMSG_SUSPEND))) {
 		printk(KERN_ERR "Some devices failed to suspend\n");
 		printk(KERN_ERR "Some devices failed to suspend\n");
 		goto Finish;
 		goto Finish;
@@ -133,6 +134,7 @@ int suspend_enter(suspend_state_t state)
 static void suspend_finish(suspend_state_t state)
 static void suspend_finish(suspend_state_t state)
 {
 {
 	device_resume();
 	device_resume();
+	resume_console();
 	thaw_processes();
 	thaw_processes();
 	enable_nonboot_cpus();
 	enable_nonboot_cpus();
 	if (pm_ops && pm_ops->finish)
 	if (pm_ops && pm_ops->finish)

+ 27 - 1
kernel/printk.c

@@ -67,6 +67,7 @@ EXPORT_SYMBOL(oops_in_progress);
  * driver system.
  * driver system.
  */
  */
 static DECLARE_MUTEX(console_sem);
 static DECLARE_MUTEX(console_sem);
+static DECLARE_MUTEX(secondary_console_sem);
 struct console *console_drivers;
 struct console *console_drivers;
 /*
 /*
  * This is used for debugging the mess that is the VT code by
  * This is used for debugging the mess that is the VT code by
@@ -76,7 +77,7 @@ struct console *console_drivers;
  * path in the console code where we end up in places I want
  * path in the console code where we end up in places I want
  * locked without the console sempahore held
  * locked without the console sempahore held
  */
  */
-static int console_locked;
+static int console_locked, console_suspended;
 
 
 /*
 /*
  * logbuf_lock protects log_buf, log_start, log_end, con_start and logged_chars
  * logbuf_lock protects log_buf, log_start, log_end, con_start and logged_chars
@@ -697,6 +698,23 @@ int __init add_preferred_console(char *name, int idx, char *options)
 	return 0;
 	return 0;
 }
 }
 
 
+/**
+ * suspend_console - suspend the console subsystem
+ *
+ * This disables printk() while we go into suspend states
+ */
+void suspend_console(void)
+{
+	acquire_console_sem();
+	console_suspended = 1;
+}
+
+void resume_console(void)
+{
+	console_suspended = 0;
+	release_console_sem();
+}
+
 /**
 /**
  * acquire_console_sem - lock the console system for exclusive use.
  * acquire_console_sem - lock the console system for exclusive use.
  *
  *
@@ -708,6 +726,10 @@ int __init add_preferred_console(char *name, int idx, char *options)
 void acquire_console_sem(void)
 void acquire_console_sem(void)
 {
 {
 	BUG_ON(in_interrupt());
 	BUG_ON(in_interrupt());
+	if (console_suspended) {
+		down(&secondary_console_sem);
+		return;
+	}
 	down(&console_sem);
 	down(&console_sem);
 	console_locked = 1;
 	console_locked = 1;
 	console_may_schedule = 1;
 	console_may_schedule = 1;
@@ -750,6 +772,10 @@ void release_console_sem(void)
 	unsigned long _con_start, _log_end;
 	unsigned long _con_start, _log_end;
 	unsigned long wake_klogd = 0;
 	unsigned long wake_klogd = 0;
 
 
+	if (console_suspended) {
+		up(&secondary_console_sem);
+		return;
+	}
 	for ( ; ; ) {
 	for ( ; ; ) {
 		spin_lock_irqsave(&logbuf_lock, flags);
 		spin_lock_irqsave(&logbuf_lock, flags);
 		wake_klogd |= log_start - log_end;
 		wake_klogd |= log_start - log_end;