semaphores: remove console_sem and make semaphore.c optional

This is incomplete.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
diff --git a/kernel/Makefile b/kernel/Makefile
index e898c5b..ab7dd786 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -8,7 +8,7 @@
 	    signal.o sys.o kmod.o workqueue.o pid.o \
 	    rcupdate.o extable.o params.o posix-timers.o \
 	    kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
-	    hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
+	    hrtimer.o rwsem.o nsproxy.o srcu.o \
 	    notifier.o ksysfs.o sched_clock.o cred.o \
 	    async.o range.o
 obj-y += groups.o
@@ -29,6 +29,7 @@
 obj-$(CONFIG_SYSCTL_SYSCALL_CHECK) += sysctl_check.o
 obj-$(CONFIG_STACKTRACE) += stacktrace.o
 obj-y += time/
+obj-$(CONFIG_LEGACY_SEMAPHORE) += semaphore.o
 obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o
 obj-$(CONFIG_LOCKDEP) += lockdep.o
 ifeq ($(CONFIG_PROC_FS),y)
diff --git a/kernel/printk.c b/kernel/printk.c
index 7982a0a..953a9a5b 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -77,25 +77,31 @@
 EXPORT_SYMBOL(oops_in_progress);
 
 /*
- * console_sem protects the console_drivers list, and also
+ * The console lock protects the console_drivers list, and also
  * provides serialisation for access to the entire console
  * driver system.
+ * The lock can be in one of four states, depending on whether
+ * the console is locked and/or suspended. In suspended state,
+ * it is not possible to acquire the lock for output to the
+ * console, but it can still be locked for other purposes.
  */
-static DEFINE_SEMAPHORE(console_sem);
+enum {
+	CONSOLE_UNLOCKED	 = 0,
+	CONSOLE_LOCKED		 = 1,
+	CONSOLE_SUSPENDED	 = 2,
+	CONSOLE_LOCKED_SUSPENDED = 3,
+};
+static atomic_t console_state = ATOMIC_INIT(CONSOLE_UNLOCKED);
+static bool console_set_state(unsigned int old, unsigned int new)
+{
+	return atomic_cmpxchg(&console_state, old, new) == old;
+}
+static DECLARE_WAIT_QUEUE_HEAD(console_wq);
+
 struct console *console_drivers;
 EXPORT_SYMBOL_GPL(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, console_suspended;
-
-/*
  * logbuf_lock protects log_buf, log_start, log_end, con_start and logged_chars
  * It is also used in interesting ways to provide interlocking in
  * console_unlock();.
@@ -691,7 +697,7 @@
 	/* If a crash is occurring, make sure we can't deadlock */
 	raw_spin_lock_init(&logbuf_lock);
 	/* And make sure that we print immediately */
-	sema_init(&console_sem, 1);
+	atomic_set(&console_state, CONSOLE_UNLOCKED);
 }
 
 #if defined(CONFIG_PRINTK_TIME)
@@ -796,15 +802,14 @@
 		 * in order to do this test safely.
 		 */
 		if (!can_use_console(cpu)) {
-			console_locked = 0;
+			atomic_set(&console_state, CONSOLE_UNLOCKED);
 			wake = 1;
 			retval = 0;
 		}
 	}
 	printk_cpu = UINT_MAX;
 	if (wake)
-		up(&console_sem);
-	raw_spin_unlock(&logbuf_lock);
+		wake_up(&console_wq);
 	return retval;
 }
 static const char recursion_bug_msg [] =
@@ -1123,17 +1128,16 @@
 	if (!console_suspend_enabled)
 		return;
 	printk("Suspending console(s) (use no_console_suspend to debug)\n");
-	console_lock();
-	console_suspended = 1;
-	up(&console_sem);
+	wait_event(console_wq,
+		   console_set_state(CONSOLE_UNLOCKED, CONSOLE_SUSPENDED));
 }
 
 void resume_console(void)
 {
 	if (!console_suspend_enabled)
 		return;
-	down(&console_sem);
-	console_suspended = 0;
+	wait_event(console_wq,
+		   console_set_state(CONSOLE_SUSPENDED, CONSOLE_LOCKED));
 	console_unlock();
 }
 
@@ -1173,12 +1177,17 @@
  */
 void console_lock(void)
 {
+	unsigned int state;
 	BUG_ON(in_interrupt());
-	down(&console_sem);
-	if (console_suspended)
-		return;
-	console_locked = 1;
-	console_may_schedule = 1;
+
+	/* wait until we transition from unlocked to locked state */
+	state = CONSOLE_UNLOCKED;
+	wait_event(console_wq, !(state = atomic_cmpxchg(&console_state,
+				  state & ~CONSOLE_LOCKED,
+				  state | CONSOLE_LOCKED)) & CONSOLE_LOCKED);
+	smp_rmb(); /* XXX does atomic_cmpxchg always imply an smp_rmb? */
+	if (state != CONSOLE_SUSPENDED)
+		console_may_schedule = 1;
 }
 EXPORT_SYMBOL(console_lock);
 
@@ -1192,21 +1201,26 @@
  */
 int console_trylock(void)
 {
-	if (down_trylock(&console_sem))
+	if (!console_set_state(CONSOLE_UNLOCKED, CONSOLE_LOCKED))
 		return 0;
-	if (console_suspended) {
-		up(&console_sem);
-		return 0;
-	}
-	console_locked = 1;
+	smp_rmb();
+
 	console_may_schedule = 0;
 	return 1;
 }
 EXPORT_SYMBOL(console_trylock);
 
+/*
+ * 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
+ */
 int is_console_locked(void)
 {
-	return console_locked;
+	return atomic_read(&console_state) != CONSOLE_UNLOCKED;
 }
 
 static DEFINE_PER_CPU(int, printk_pending);
@@ -1235,7 +1249,7 @@
 /**
  * console_unlock - unlock the console system
  *
- * Releases the console_lock which the caller holds on the console system
+ * Releases the console lock which the caller holds on the console system
  * and the console driver list.
  *
  * While the console_lock was held, console output may have been buffered
@@ -1252,8 +1266,9 @@
 	unsigned _con_start, _log_end;
 	unsigned wake_klogd = 0, retry = 0;
 
-	if (console_suspended) {
-		up(&console_sem);
+	if (atomic_read(&console_state) == CONSOLE_LOCKED_SUSPENDED) {
+		atomic_set(&console_state, CONSOLE_SUSPENDED);
+		wake_up(&console_wq);
 		return;
 	}
 
@@ -1274,15 +1289,16 @@
 		start_critical_timings();
 		local_irq_restore(flags);
 	}
-	console_locked = 0;
 
 	/* Release the exclusive_console once it is used */
 	if (unlikely(exclusive_console))
 		exclusive_console = NULL;
 
+	atomic_set(&console_state, CONSOLE_UNLOCKED);
+
 	raw_spin_unlock(&logbuf_lock);
 
-	up(&console_sem);
+	wake_up(&console_wq);
 
 	/*
 	 * Someone could have filled up the buffer again, so re-check if there's
@@ -1328,12 +1344,13 @@
 	 * oops_in_progress is set to 1..
 	 */
 	if (oops_in_progress) {
-		if (down_trylock(&console_sem) != 0)
+		if (!console_set_state(CONSOLE_UNLOCKED, CONSOLE_LOCKED) &&
+		    !console_set_state(CONSOLE_SUSPENDED, CONSOLE_LOCKED_SUSPENDED))
 			return;
+		smp_rmb();
 	} else
 		console_lock();
 
-	console_locked = 1;
 	console_may_schedule = 0;
 	for_each_console(c)
 		if ((c->flags & CON_ENABLED) && c->unblank)