printk() - do not merge continuation lines of different threads
authorKay Sievers <kay@vrfy.org>
Thu, 10 May 2012 02:32:53 +0000 (04:32 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 10 May 2012 03:29:59 +0000 (20:29 -0700)
This prevents the merging of printk() continuation lines of different
threads, in the case they race against each other.

It should properly isolate "atomic" single-line printk() users from
continuation users, to make sure the single-line users will never be
merged with the racy continuation ones.

Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Sasha Levin <levinsasha928@gmail.com>
Signed-off-by: Kay Sievers <kay@vrfy.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
kernel/printk.c

index 7b432951f91e74666d3abe8c7b6f2cca09ce10a1..301fb0f09fbf11ee3f4f5b375691299af951da54 100644 (file)
@@ -1230,12 +1230,13 @@ asmlinkage int vprintk_emit(int facility, int level,
        static size_t buflen;
        static int buflevel;
        static char textbuf[LOG_LINE_MAX];
+       static struct task_struct *cont;
        char *text = textbuf;
        size_t textlen;
        unsigned long flags;
        int this_cpu;
        bool newline = false;
-       bool cont = false;
+       bool prefix = false;
        int printed_len = 0;
 
        boot_delay_msec();
@@ -1295,20 +1296,16 @@ asmlinkage int vprintk_emit(int facility, int level,
                case '0' ... '7':
                        if (level == -1)
                                level = text[1] - '0';
-                       text += 3;
-                       textlen -= 3;
-                       break;
-               case 'c':       /* KERN_CONT */
-                       cont = true;
                case 'd':       /* KERN_DEFAULT */
+                       prefix = true;
+               case 'c':       /* KERN_CONT */
                        text += 3;
                        textlen -= 3;
-                       break;
                }
        }
 
-       if (buflen && (!cont || dict)) {
-               /* no continuation; flush existing buffer */
+       if (buflen && (prefix || dict || cont != current)) {
+               /* flush existing buffer */
                log_store(facility, buflevel, NULL, 0, buf, buflen);
                printed_len += buflen;
                buflen = 0;
@@ -1342,6 +1339,10 @@ asmlinkage int vprintk_emit(int facility, int level,
                                  dict, dictlen, text, textlen);
                        printed_len += textlen;
                }
+               cont = NULL;
+       } else {
+               /* remember thread which filled the buffer */
+               cont = current;
        }
 
        /*