aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/printk.c
diff options
context:
space:
mode:
authorKay Sievers <kay@vrfy.org>2012-05-14 14:46:27 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-05-14 15:36:45 -0400
commitc313af145b9bc4fb8e8e0c83b8cfc10e1b894a50 (patch)
tree29abdf4b648d64068e424ed49ed379995a955d13 /kernel/printk.c
parent356c05d58af05d582e634b54b40050c73609617b (diff)
printk() - isolate KERN_CONT users from ordinary complete lines
Arrange the continuation printk() buffering to be fully separated from the ordinary full line users. Limit the exposure to races and wrong printk() line merges to users of continuation only. Ordinary full line users racing against continuation users will no longer affect each other. Multiple continuation users from different threads, racing against each other will not wrongly be merged into a single line, but printed as separate lines. Test output of a kernel module which starts two separate threads which race against each other, one of them printing a single full terminated line: printk("(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA)\n"); The other one printing the line, every character separate in a continuation loop: printk("(C"); for (i = 0; i < 58; i++) printk(KERN_CONT "C"); printk(KERN_CONT "C)\n"); Behavior of single and non-thread-aware printk() buffer: # modprobe printk-race printk test init (CC(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) C(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) CC(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) C(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) CC(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) C(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) C(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) CC(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) C(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) C(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC) (CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC) New behavior with separate and thread-aware continuation buffer: # modprobe printk-race printk test init (AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) (AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) (AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) (CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC) (AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) (AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) (AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) (AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) (CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC) (CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC) Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Joe Perches <joe@perches.com> Cc: Ted Ts'o <tytso@mit.edu> 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>
Diffstat (limited to 'kernel/printk.c')
-rw-r--r--kernel/printk.c105
1 files changed, 61 insertions, 44 deletions
diff --git a/kernel/printk.c b/kernel/printk.c
index 915a8be10b5f..32462d2b364a 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -1264,13 +1264,13 @@ asmlinkage int vprintk_emit(int facility, int level,
1264 const char *fmt, va_list args) 1264 const char *fmt, va_list args)
1265{ 1265{
1266 static int recursion_bug; 1266 static int recursion_bug;
1267 static char buf[LOG_LINE_MAX]; 1267 static char cont_buf[LOG_LINE_MAX];
1268 static size_t buflen; 1268 static size_t cont_len;
1269 static int buflevel; 1269 static int cont_level;
1270 static struct task_struct *cont_task;
1270 static char textbuf[LOG_LINE_MAX]; 1271 static char textbuf[LOG_LINE_MAX];
1271 static struct task_struct *cont;
1272 char *text = textbuf; 1272 char *text = textbuf;
1273 size_t textlen; 1273 size_t text_len;
1274 unsigned long flags; 1274 unsigned long flags;
1275 int this_cpu; 1275 int this_cpu;
1276 bool newline = false; 1276 bool newline = false;
@@ -1320,15 +1320,15 @@ asmlinkage int vprintk_emit(int facility, int level,
1320 * The printf needs to come first; we need the syslog 1320 * The printf needs to come first; we need the syslog
1321 * prefix which might be passed-in as a parameter. 1321 * prefix which might be passed-in as a parameter.
1322 */ 1322 */
1323 textlen = vscnprintf(text, sizeof(textbuf), fmt, args); 1323 text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
1324 1324
1325 /* mark and strip a trailing newline */ 1325 /* mark and strip a trailing newline */
1326 if (textlen && text[textlen-1] == '\n') { 1326 if (text_len && text[text_len-1] == '\n') {
1327 textlen--; 1327 text_len--;
1328 newline = true; 1328 newline = true;
1329 } 1329 }
1330 1330
1331 /* strip syslog prefix and extract log level or flags */ 1331 /* strip syslog prefix and extract log level or control flags */
1332 if (text[0] == '<' && text[1] && text[2] == '>') { 1332 if (text[0] == '<' && text[1] && text[2] == '>') {
1333 switch (text[1]) { 1333 switch (text[1]) {
1334 case '0' ... '7': 1334 case '0' ... '7':
@@ -1338,49 +1338,67 @@ asmlinkage int vprintk_emit(int facility, int level,
1338 prefix = true; 1338 prefix = true;
1339 case 'c': /* KERN_CONT */ 1339 case 'c': /* KERN_CONT */
1340 text += 3; 1340 text += 3;
1341 textlen -= 3; 1341 text_len -= 3;
1342 } 1342 }
1343 } 1343 }
1344 1344
1345 if (buflen && (prefix || dict || cont != current)) { 1345 if (level == -1)
1346 /* flush existing buffer */ 1346 level = default_message_loglevel;
1347 log_store(facility, buflevel, NULL, 0, buf, buflen);
1348 printed_len += buflen;
1349 buflen = 0;
1350 }
1351 1347
1352 if (buflen == 0) { 1348 if (dict) {
1353 /* remember level for first message in the buffer */ 1349 prefix = true;
1354 if (level == -1) 1350 newline = true;
1355 buflevel = default_message_loglevel;
1356 else
1357 buflevel = level;
1358 } 1351 }
1359 1352
1360 if (buflen || !newline) { 1353 if (!newline) {
1361 /* append to existing buffer, or buffer until next message */ 1354 if (cont_len && (prefix || cont_task != current)) {
1362 if (buflen + textlen > sizeof(buf)) 1355 /*
1363 textlen = sizeof(buf) - buflen; 1356 * Flush earlier buffer, which is either from a
1364 memcpy(buf + buflen, text, textlen); 1357 * different thread, or when we got a new prefix.
1365 buflen += textlen; 1358 */
1366 } 1359 log_store(facility, cont_level, NULL, 0, cont_buf, cont_len);
1360 cont_len = 0;
1361 }
1367 1362
1368 if (newline) { 1363 if (!cont_len) {
1369 /* end of line; flush buffer */ 1364 cont_level = level;
1370 if (buflen) { 1365 cont_task = current;
1371 log_store(facility, buflevel,
1372 dict, dictlen, buf, buflen);
1373 printed_len += buflen;
1374 buflen = 0;
1375 } else {
1376 log_store(facility, buflevel,
1377 dict, dictlen, text, textlen);
1378 printed_len += textlen;
1379 } 1366 }
1380 cont = NULL; 1367
1368 /* buffer or append to earlier buffer from the same thread */
1369 if (cont_len + text_len > sizeof(cont_buf))
1370 text_len = sizeof(cont_buf) - cont_len;
1371 memcpy(cont_buf + cont_len, text, text_len);
1372 cont_len += text_len;
1381 } else { 1373 } else {
1382 /* remember thread which filled the buffer */ 1374 if (cont_len && cont_task == current) {
1383 cont = current; 1375 if (prefix) {
1376 /*
1377 * New prefix from the same thread; flush. We
1378 * either got no earlier newline, or we race
1379 * with an interrupt.
1380 */
1381 log_store(facility, cont_level,
1382 NULL, 0, cont_buf, cont_len);
1383 cont_len = 0;
1384 }
1385
1386 /* append to the earlier buffer and flush */
1387 if (cont_len + text_len > sizeof(cont_buf))
1388 text_len = sizeof(cont_buf) - cont_len;
1389 memcpy(cont_buf + cont_len, text, text_len);
1390 cont_len += text_len;
1391 log_store(facility, cont_level,
1392 NULL, 0, cont_buf, cont_len);
1393 cont_len = 0;
1394 cont_task = NULL;
1395 printed_len = cont_len;
1396 } else {
1397 /* ordinary single and terminated line */
1398 log_store(facility, level,
1399 dict, dictlen, text, text_len);
1400 printed_len = text_len;
1401 }
1384 } 1402 }
1385 1403
1386 /* 1404 /*
@@ -1470,7 +1488,6 @@ EXPORT_SYMBOL(printk);
1470#define LOG_LINE_MAX 0 1488#define LOG_LINE_MAX 0
1471static struct log *log_from_idx(u32 idx) { return NULL; } 1489static struct log *log_from_idx(u32 idx) { return NULL; }
1472static u32 log_next(u32 idx) { return 0; } 1490static u32 log_next(u32 idx) { return 0; }
1473static char *log_text(const struct log *msg) { return NULL; }
1474static void call_console_drivers(int level, const char *text, size_t len) {} 1491static void call_console_drivers(int level, const char *text, size_t len) {}
1475static size_t msg_print_text(const struct log *msg, bool syslog, 1492static size_t msg_print_text(const struct log *msg, bool syslog,
1476 char *buf, size_t size) { return 0; } 1493 char *buf, size_t size) { return 0; }