summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Mladek <pmladek@suse.com>2016-12-12 19:45:44 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2016-12-12 21:55:09 -0500
commit22c2c7b2ef7864389b1b75f9fd604da14b21e2c2 (patch)
treea1ed957aac7b3c1916d7a6380a5903226d9a121b
parent4a998e322abc935e95efc1a8108e6102be636a43 (diff)
printk/NMI: handle continuous lines and missing newline
Commit 4bcc595ccd80 ("printk: reinstate KERN_CONT for printing continuation lines") added back KERN_CONT message header. As a result it might appear in the middle of the line when the parts are squashed via the temporary NMI buffer. A reasonable solution seems to be to split the text in the NNI temporary not only by newlines but also by the message headers. Another solution would be to filter out KERN_CONT when writing to the temporary buffer. But this would complicate the lockless handling. Also it would not solve problems with a missing newline that was there even before the KERN_CONT stuff. This patch moves the temporary buffer handling into separate function. I played with it and it seems that using the char pointers make the code easier to read. Also it prints the final newline as a continuous line. Finally, it moves handling of the s->len overflow into the paranoid check. And allows to recover from the disaster. Link: http://lkml.kernel.org/r/1478695291-12169-2-git-send-email-pmladek@suse.com Signed-off-by: Petr Mladek <pmladek@suse.com> Reviewed-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com> Cc: Joe Perches <joe@perches.com> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Jason Wessel <jason.wessel@windriver.com> Cc: Jaroslav Kysela <perex@perex.cz> Cc: Takashi Iwai <tiwai@suse.com> Cc: Chris Mason <clm@fb.com> Cc: Josef Bacik <jbacik@fb.com> Cc: David Sterba <dsterba@suse.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--kernel/printk/nmi.c78
1 files changed, 50 insertions, 28 deletions
diff --git a/kernel/printk/nmi.c b/kernel/printk/nmi.c
index 152533edc56f..f011aaef583c 100644
--- a/kernel/printk/nmi.c
+++ b/kernel/printk/nmi.c
@@ -114,16 +114,51 @@ static void printk_nmi_flush_line(const char *text, int len)
114 114
115} 115}
116 116
117/* 117/* printk part of the temporary buffer line by line */
118 * printk one line from the temporary buffer from @start index until 118static int printk_nmi_flush_buffer(const char *start, size_t len)
119 * and including the @end index.
120 */
121static void printk_nmi_flush_seq_line(struct nmi_seq_buf *s,
122 int start, int end)
123{ 119{
124 const char *buf = s->buffer + start; 120 const char *c, *end;
121 bool header;
122
123 c = start;
124 end = start + len;
125 header = true;
126
127 /* Print line by line. */
128 while (c < end) {
129 if (*c == '\n') {
130 printk_nmi_flush_line(start, c - start + 1);
131 start = ++c;
132 header = true;
133 continue;
134 }
135
136 /* Handle continuous lines or missing new line. */
137 if ((c + 1 < end) && printk_get_level(c)) {
138 if (header) {
139 c = printk_skip_level(c);
140 continue;
141 }
142
143 printk_nmi_flush_line(start, c - start);
144 start = c++;
145 header = true;
146 continue;
147 }
148
149 header = false;
150 c++;
151 }
125 152
126 printk_nmi_flush_line(buf, (end - start) + 1); 153 /* Check if there was a partial line. Ignore pure header. */
154 if (start < end && !header) {
155 static const char newline[] = KERN_CONT "\n";
156
157 printk_nmi_flush_line(start, end - start);
158 printk_nmi_flush_line(newline, strlen(newline));
159 }
160
161 return len;
127} 162}
128 163
129/* 164/*
@@ -136,8 +171,8 @@ static void __printk_nmi_flush(struct irq_work *work)
136 __RAW_SPIN_LOCK_INITIALIZER(read_lock); 171 __RAW_SPIN_LOCK_INITIALIZER(read_lock);
137 struct nmi_seq_buf *s = container_of(work, struct nmi_seq_buf, work); 172 struct nmi_seq_buf *s = container_of(work, struct nmi_seq_buf, work);
138 unsigned long flags; 173 unsigned long flags;
139 size_t len, size; 174 size_t len;
140 int i, last_i; 175 int i;
141 176
142 /* 177 /*
143 * The lock has two functions. First, one reader has to flush all 178 * The lock has two functions. First, one reader has to flush all
@@ -155,12 +190,14 @@ more:
155 /* 190 /*
156 * This is just a paranoid check that nobody has manipulated 191 * This is just a paranoid check that nobody has manipulated
157 * the buffer an unexpected way. If we printed something then 192 * the buffer an unexpected way. If we printed something then
158 * @len must only increase. 193 * @len must only increase. Also it should never overflow the
194 * buffer size.
159 */ 195 */
160 if (i && i >= len) { 196 if ((i && i >= len) || len > sizeof(s->buffer)) {
161 const char *msg = "printk_nmi_flush: internal error\n"; 197 const char *msg = "printk_nmi_flush: internal error\n";
162 198
163 printk_nmi_flush_line(msg, strlen(msg)); 199 printk_nmi_flush_line(msg, strlen(msg));
200 len = 0;
164 } 201 }
165 202
166 if (!len) 203 if (!len)
@@ -168,22 +205,7 @@ more:
168 205
169 /* Make sure that data has been written up to the @len */ 206 /* Make sure that data has been written up to the @len */
170 smp_rmb(); 207 smp_rmb();
171 208 i += printk_nmi_flush_buffer(s->buffer + i, len - i);
172 size = min(len, sizeof(s->buffer));
173 last_i = i;
174
175 /* Print line by line. */
176 for (; i < size; i++) {
177 if (s->buffer[i] == '\n') {
178 printk_nmi_flush_seq_line(s, last_i, i);
179 last_i = i + 1;
180 }
181 }
182 /* Check if there was a partial line. */
183 if (last_i < size) {
184 printk_nmi_flush_seq_line(s, last_i, size - 1);
185 printk_nmi_flush_line("\n", strlen("\n"));
186 }
187 209
188 /* 210 /*
189 * Check that nothing has got added in the meantime and truncate 211 * Check that nothing has got added in the meantime and truncate