]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/pmladek...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 4 May 2017 01:29:28 +0000 (18:29 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 4 May 2017 01:29:28 +0000 (18:29 -0700)
Pull printk updates from Petr Mladek:

 - There is a situation when early console is not deregistered because
   the preferred one matches a wrong entry. It caused messages to appear
   twice.

   This is the 2nd attempt to fix it. The first one was wrong, see the
   commit c6c7d83b9c9e ('Revert "console: don't prefer first registered
   if DT specifies stdout-path"').

   The fix is coupled with some small code clean up. Well, the console
   registration code would deserve a big one. We need to think about it.

 - Do not lose information about the preemtive context when the console
   semaphore is re-taken.

 - Do not block CPU hotplug when someone else is already pushing
   messages to the console.

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/pmladek/printk:
  printk: fix double printing with earlycon
  printk: rename selected_console -> preferred_console
  printk: fix name/type/scope of preferred_console var
  printk: Correctly handle preemption in console_unlock()
  printk: use console_trylock() in console_cpu_notify()

kernel/printk/printk.c

index 2984fb0f0257420de4a7aa2595a52c34f0d33a0e..779479ac9f57bc4feb4276f14d677b88e1b06969 100644 (file)
@@ -269,8 +269,8 @@ static struct console *exclusive_console;
 #define MAX_CMDLINECONSOLES 8
 
 static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];
+static int console_cmdline_cnt;
 
-static int selected_console = -1;
 static int preferred_console = -1;
 int console_set_on_cmdline;
 EXPORT_SYMBOL(console_set_on_cmdline);
@@ -1906,24 +1906,38 @@ static int __add_preferred_console(char *name, int idx, char *options,
         *      See if this tty is not yet registered, and
         *      if we have a slot free.
         */
-       for (i = 0, c = console_cmdline;
-            i < MAX_CMDLINECONSOLES && c->name[0];
-            i++, c++) {
+       for (i = 0, c = console_cmdline; i < console_cmdline_cnt; i++, c++) {
                if (strcmp(c->name, name) == 0 && c->index == idx) {
-                       if (!brl_options)
-                               selected_console = i;
+                       if (brl_options)
+                               return 0;
+
+                       /*
+                        * Maintain an invariant that will help to find if
+                        * the matching console is preferred, see
+                        * register_console():
+                        *
+                        * The last non-braille console is always
+                        * the preferred one.
+                        */
+                       if (i != console_cmdline_cnt - 1)
+                               swap(console_cmdline[i],
+                                    console_cmdline[console_cmdline_cnt - 1]);
+
+                       preferred_console = console_cmdline_cnt - 1;
+
                        return 0;
                }
        }
        if (i == MAX_CMDLINECONSOLES)
                return -E2BIG;
        if (!brl_options)
-               selected_console = i;
+               preferred_console = i;
        strlcpy(c->name, name, sizeof(c->name));
        c->options = options;
        braille_set_options(c, brl_options);
 
        c->index = idx;
+       console_cmdline_cnt++;
        return 0;
 }
 /*
@@ -2031,15 +2045,16 @@ void resume_console(void)
  * @cpu: unused
  *
  * If printk() is called from a CPU that is not online yet, the messages
- * will be spooled but will not show up on the console.  This function is
- * called when a new CPU comes online (or fails to come up), and ensures
- * that any such output gets printed.
+ * will be printed on the console only if there are CON_ANYTIME consoles.
+ * This function is called when a new CPU comes online (or fails to come
+ * up) or goes offline.
  */
 static int console_cpu_notify(unsigned int cpu)
 {
        if (!cpuhp_tasks_frozen) {
-               console_lock();
-               console_unlock();
+               /* If trylock fails, someone else is doing the printing */
+               if (console_trylock())
+                       console_unlock();
        }
        return 0;
 }
@@ -2161,7 +2176,7 @@ void console_unlock(void)
        }
 
        /*
-        * Console drivers are called under logbuf_lock, so
+        * Console drivers are called with interrupts disabled, so
         * @console_may_schedule should be cleared before; however, we may
         * end up dumping a lot of lines, for example, if called from
         * console registration path, and should invoke cond_resched()
@@ -2169,11 +2184,15 @@ void console_unlock(void)
         * scheduling stall on a slow console leading to RCU stall and
         * softlockup warnings which exacerbate the issue with more
         * messages practically incapacitating the system.
+        *
+        * console_trylock() is not able to detect the preemptive
+        * context reliably. Therefore the value must be stored before
+        * and cleared after the the "again" goto label.
         */
        do_cond_resched = console_may_schedule;
+again:
        console_may_schedule = 0;
 
-again:
        /*
         * We released the console_sem lock, so we need to recheck if
         * cpu is online and (if not) is there at least one CON_ANYTIME
@@ -2409,6 +2428,7 @@ void register_console(struct console *newcon)
        unsigned long flags;
        struct console *bcon = NULL;
        struct console_cmdline *c;
+       static bool has_preferred;
 
        if (console_drivers)
                for_each_console(bcon)
@@ -2435,15 +2455,15 @@ void register_console(struct console *newcon)
        if (console_drivers && console_drivers->flags & CON_BOOT)
                bcon = console_drivers;
 
-       if (preferred_console < 0 || bcon || !console_drivers)
-               preferred_console = selected_console;
+       if (!has_preferred || bcon || !console_drivers)
+               has_preferred = preferred_console >= 0;
 
        /*
         *      See if we want to use this console driver. If we
         *      didn't select a console we take the first one
         *      that registers here.
         */
-       if (preferred_console < 0) {
+       if (!has_preferred) {
                if (newcon->index < 0)
                        newcon->index = 0;
                if (newcon->setup == NULL ||
@@ -2451,18 +2471,29 @@ void register_console(struct console *newcon)
                        newcon->flags |= CON_ENABLED;
                        if (newcon->device) {
                                newcon->flags |= CON_CONSDEV;
-                               preferred_console = 0;
+                               has_preferred = true;
                        }
                }
        }
 
        /*
-        *      See if this console matches one we selected on
-        *      the command line.
+        * See if this console matches one we selected on the command line.
+        *
+        * There may be several entries in the console_cmdline array matching
+        * with the same console, one with newcon->match(), another by
+        * name/index:
+        *
+        *      pl011,mmio,0x87e024000000,115200 -- added from SPCR
+        *      ttyAMA0 -- added from command line
+        *
+        * Traverse the console_cmdline array in reverse order to be
+        * sure that if this console is preferred then it will be the first
+        * matching entry.  We use the invariant that is maintained in
+        * __add_preferred_console().
         */
-       for (i = 0, c = console_cmdline;
-            i < MAX_CMDLINECONSOLES && c->name[0];
-            i++, c++) {
+       for (i = console_cmdline_cnt - 1; i >= 0; i--) {
+               c = console_cmdline + i;
+
                if (!newcon->match ||
                    newcon->match(newcon, c->name, c->index, c->options) != 0) {
                        /* default matching */
@@ -2484,9 +2515,9 @@ void register_console(struct console *newcon)
                }
 
                newcon->flags |= CON_ENABLED;
-               if (i == selected_console) {
+               if (i == preferred_console) {
                        newcon->flags |= CON_CONSDEV;
-                       preferred_console = selected_console;
+                       has_preferred = true;
                }
                break;
        }