aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/printk.c
diff options
context:
space:
mode:
authorKay Sievers <kay@vrfy.org>2012-06-28 03:38:53 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-06-29 11:39:42 -0400
commit084681d14e429cb6192262ac7437f00e2c02f26a (patch)
tree60ef064def42a8b077cf1caaf465bc4029bbf360 /kernel/printk.c
parent116e90b23f74d303e8d607c7a7d54f60f14ab9f2 (diff)
printk: flush continuation lines immediately to console
Continuation lines are buffered internally, intended to merge the chunked printk()s into a single record, and to isolate potentially racy continuation users from usual terminated line users. This though, has the effect that partial lines are not printed to the console in the moment they are emitted. In case the kernel crashes in the meantime, the potentially interesting printed information would never reach the consoles. Here we share the continuation buffer with the console copy logic, and partial lines are always immediately flushed to the available consoles. They are still buffered internally to improve the readability and integrity of the messages and minimize the amount of needed record headers to store. Signed-off-by: Kay Sievers <kay@vrfy.org> Tested-by: Steven Rostedt <rostedt@goodmis.org> Acked-by: Steven Rostedt <rostedt@goodmis.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'kernel/printk.c')
-rw-r--r--kernel/printk.c244
1 files changed, 176 insertions, 68 deletions
diff --git a/kernel/printk.c b/kernel/printk.c
index cdfba44fedf0..fbf4d0b22a1d 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -193,12 +193,19 @@ static int console_may_schedule;
193 * separated by ',', and find the message after the ';' character. 193 * separated by ',', and find the message after the ';' character.
194 */ 194 */
195 195
196enum log_flags {
197 LOG_DEFAULT = 0,
198 LOG_NOCONS = 1, /* already flushed, do not print to console */
199};
200
196struct log { 201struct log {
197 u64 ts_nsec; /* timestamp in nanoseconds */ 202 u64 ts_nsec; /* timestamp in nanoseconds */
198 u16 len; /* length of entire record */ 203 u16 len; /* length of entire record */
199 u16 text_len; /* length of text buffer */ 204 u16 text_len; /* length of text buffer */
200 u16 dict_len; /* length of dictionary buffer */ 205 u16 dict_len; /* length of dictionary buffer */
201 u16 level; /* syslog level + facility */ 206 u8 facility; /* syslog facility */
207 u8 flags:5; /* internal record flags */
208 u8 level:3; /* syslog level */
202}; 209};
203 210
204/* 211/*
@@ -286,6 +293,7 @@ static u32 log_next(u32 idx)
286 293
287/* insert record into the buffer, discard old ones, update heads */ 294/* insert record into the buffer, discard old ones, update heads */
288static void log_store(int facility, int level, 295static void log_store(int facility, int level,
296 enum log_flags flags, u64 ts_nsec,
289 const char *dict, u16 dict_len, 297 const char *dict, u16 dict_len,
290 const char *text, u16 text_len) 298 const char *text, u16 text_len)
291{ 299{
@@ -329,8 +337,13 @@ static void log_store(int facility, int level,
329 msg->text_len = text_len; 337 msg->text_len = text_len;
330 memcpy(log_dict(msg), dict, dict_len); 338 memcpy(log_dict(msg), dict, dict_len);
331 msg->dict_len = dict_len; 339 msg->dict_len = dict_len;
332 msg->level = (facility << 3) | (level & 7); 340 msg->facility = facility;
333 msg->ts_nsec = local_clock(); 341 msg->level = level & 7;
342 msg->flags = flags & 0x1f;
343 if (ts_nsec > 0)
344 msg->ts_nsec = ts_nsec;
345 else
346 msg->ts_nsec = local_clock();
334 memset(log_dict(msg) + dict_len, 0, pad_len); 347 memset(log_dict(msg) + dict_len, 0, pad_len);
335 msg->len = sizeof(struct log) + text_len + dict_len + pad_len; 348 msg->len = sizeof(struct log) + text_len + dict_len + pad_len;
336 349
@@ -446,7 +459,7 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
446 ts_usec = msg->ts_nsec; 459 ts_usec = msg->ts_nsec;
447 do_div(ts_usec, 1000); 460 do_div(ts_usec, 1000);
448 len = sprintf(user->buf, "%u,%llu,%llu;", 461 len = sprintf(user->buf, "%u,%llu,%llu;",
449 msg->level, user->seq, ts_usec); 462 (msg->facility << 3) | msg->level, user->seq, ts_usec);
450 463
451 /* escape non-printable characters */ 464 /* escape non-printable characters */
452 for (i = 0; i < msg->text_len; i++) { 465 for (i = 0; i < msg->text_len; i++) {
@@ -787,6 +800,21 @@ static bool printk_time;
787#endif 800#endif
788module_param_named(time, printk_time, bool, S_IRUGO | S_IWUSR); 801module_param_named(time, printk_time, bool, S_IRUGO | S_IWUSR);
789 802
803static size_t print_time(u64 ts, char *buf)
804{
805 unsigned long rem_nsec;
806
807 if (!printk_time)
808 return 0;
809
810 if (!buf)
811 return 15;
812
813 rem_nsec = do_div(ts, 1000000000);
814 return sprintf(buf, "[%5lu.%06lu] ",
815 (unsigned long)ts, rem_nsec / 1000);
816}
817
790static size_t print_prefix(const struct log *msg, bool syslog, char *buf) 818static size_t print_prefix(const struct log *msg, bool syslog, char *buf)
791{ 819{
792 size_t len = 0; 820 size_t len = 0;
@@ -803,18 +831,7 @@ static size_t print_prefix(const struct log *msg, bool syslog, char *buf)
803 } 831 }
804 } 832 }
805 833
806 if (printk_time) { 834 len += print_time(msg->ts_nsec, buf ? buf + len : NULL);
807 if (buf) {
808 unsigned long long ts = msg->ts_nsec;
809 unsigned long rem_nsec = do_div(ts, 1000000000);
810
811 len += sprintf(buf + len, "[%5lu.%06lu] ",
812 (unsigned long) ts, rem_nsec / 1000);
813 } else {
814 len += 15;
815 }
816 }
817
818 return len; 835 return len;
819} 836}
820 837
@@ -1294,15 +1311,92 @@ static inline void printk_delay(void)
1294 } 1311 }
1295} 1312}
1296 1313
1314/*
1315 * Continuation lines are buffered, and not committed to the record buffer
1316 * until the line is complete, or a race forces it. The line fragments
1317 * though, are printed immediately to the consoles to ensure everything has
1318 * reached the console in case of a kernel crash.
1319 */
1320static struct cont {
1321 char buf[LOG_LINE_MAX];
1322 size_t len; /* length == 0 means unused buffer */
1323 size_t cons; /* bytes written to console */
1324 struct task_struct *owner; /* task of first print*/
1325 u64 ts_nsec; /* time of first print */
1326 u8 level; /* log level of first message */
1327 u8 facility; /* log level of first message */
1328 bool flushed:1; /* buffer sealed and committed */
1329} cont;
1330
1331static void cont_flush(void)
1332{
1333 if (cont.flushed)
1334 return;
1335 if (cont.len == 0)
1336 return;
1337
1338 log_store(cont.facility, cont.level, LOG_NOCONS, cont.ts_nsec,
1339 NULL, 0, cont.buf, cont.len);
1340
1341 cont.flushed = true;
1342}
1343
1344static bool cont_add(int facility, int level, const char *text, size_t len)
1345{
1346 if (cont.len && cont.flushed)
1347 return false;
1348
1349 if (cont.len + len > sizeof(cont.buf)) {
1350 cont_flush();
1351 return false;
1352 }
1353
1354 if (!cont.len) {
1355 cont.facility = facility;
1356 cont.level = level;
1357 cont.owner = current;
1358 cont.ts_nsec = local_clock();
1359 cont.cons = 0;
1360 cont.flushed = false;
1361 }
1362
1363 memcpy(cont.buf + cont.len, text, len);
1364 cont.len += len;
1365 return true;
1366}
1367
1368static size_t cont_print_text(char *text, size_t size)
1369{
1370 size_t textlen = 0;
1371 size_t len;
1372
1373 if (cont.cons == 0) {
1374 textlen += print_time(cont.ts_nsec, text);
1375 size -= textlen;
1376 }
1377
1378 len = cont.len - cont.cons;
1379 if (len > 0) {
1380 if (len+1 > size)
1381 len = size-1;
1382 memcpy(text + textlen, cont.buf + cont.cons, len);
1383 textlen += len;
1384 cont.cons = cont.len;
1385 }
1386
1387 if (cont.flushed) {
1388 text[textlen++] = '\n';
1389 /* got everything, release buffer */
1390 cont.len = 0;
1391 }
1392 return textlen;
1393}
1394
1297asmlinkage int vprintk_emit(int facility, int level, 1395asmlinkage int vprintk_emit(int facility, int level,
1298 const char *dict, size_t dictlen, 1396 const char *dict, size_t dictlen,
1299 const char *fmt, va_list args) 1397 const char *fmt, va_list args)
1300{ 1398{
1301 static int recursion_bug; 1399 static int recursion_bug;
1302 static char cont_buf[LOG_LINE_MAX];
1303 static size_t cont_len;
1304 static int cont_level;
1305 static struct task_struct *cont_task;
1306 static char textbuf[LOG_LINE_MAX]; 1400 static char textbuf[LOG_LINE_MAX];
1307 char *text = textbuf; 1401 char *text = textbuf;
1308 size_t text_len; 1402 size_t text_len;
@@ -1348,7 +1442,8 @@ asmlinkage int vprintk_emit(int facility, int level,
1348 recursion_bug = 0; 1442 recursion_bug = 0;
1349 printed_len += strlen(recursion_msg); 1443 printed_len += strlen(recursion_msg);
1350 /* emit KERN_CRIT message */ 1444 /* emit KERN_CRIT message */
1351 log_store(0, 2, NULL, 0, recursion_msg, printed_len); 1445 log_store(0, 2, LOG_DEFAULT, 0,
1446 NULL, 0, recursion_msg, printed_len);
1352 } 1447 }
1353 1448
1354 /* 1449 /*
@@ -1386,55 +1481,38 @@ asmlinkage int vprintk_emit(int facility, int level,
1386 } 1481 }
1387 1482
1388 if (!newline) { 1483 if (!newline) {
1389 if (cont_len && (prefix || cont_task != current)) { 1484 /*
1390 /* 1485 * Flush the conflicting buffer. An earlier newline was missing,
1391 * Flush earlier buffer, which is either from a 1486 * or another task also prints continuation lines.
1392 * different thread, or when we got a new prefix. 1487 */
1393 */ 1488 if (cont.len && (prefix || cont.owner != current))
1394 log_store(facility, cont_level, NULL, 0, cont_buf, cont_len); 1489 cont_flush();
1395 cont_len = 0;
1396 }
1397
1398 if (!cont_len) {
1399 cont_level = level;
1400 cont_task = current;
1401 }
1402 1490
1403 /* buffer or append to earlier buffer from the same thread */ 1491 /* buffer line if possible, otherwise store it right away */
1404 if (cont_len + text_len > sizeof(cont_buf)) 1492 if (!cont_add(facility, level, text, text_len))
1405 text_len = sizeof(cont_buf) - cont_len; 1493 log_store(facility, level, LOG_DEFAULT, 0,
1406 memcpy(cont_buf + cont_len, text, text_len); 1494 dict, dictlen, text, text_len);
1407 cont_len += text_len;
1408 } else { 1495 } else {
1409 if (cont_len && cont_task == current) { 1496 bool stored = false;
1410 if (prefix) {
1411 /*
1412 * New prefix from the same thread; flush. We
1413 * either got no earlier newline, or we race
1414 * with an interrupt.
1415 */
1416 log_store(facility, cont_level,
1417 NULL, 0, cont_buf, cont_len);
1418 cont_len = 0;
1419 }
1420 1497
1421 /* append to the earlier buffer and flush */ 1498 /*
1422 if (cont_len + text_len > sizeof(cont_buf)) 1499 * Flush the conflicting buffer. An earlier newline was missing,
1423 text_len = sizeof(cont_buf) - cont_len; 1500 * or we race with a continuation line from an interrupt.
1424 memcpy(cont_buf + cont_len, text, text_len); 1501 */
1425 cont_len += text_len; 1502 if (cont.len && prefix && cont.owner == current)
1426 log_store(facility, cont_level, 1503 cont_flush();
1427 NULL, 0, cont_buf, cont_len); 1504
1428 cont_len = 0; 1505 /* Merge with our buffer if possible; flush it in any case */
1429 cont_task = NULL; 1506 if (cont.len && cont.owner == current) {
1430 printed_len = cont_len; 1507 stored = cont_add(facility, level, text, text_len);
1431 } else { 1508 cont_flush();
1432 /* ordinary single and terminated line */
1433 log_store(facility, level,
1434 dict, dictlen, text, text_len);
1435 printed_len = text_len;
1436 } 1509 }
1510
1511 if (!stored)
1512 log_store(facility, level, LOG_DEFAULT, 0,
1513 dict, dictlen, text, text_len);
1437 } 1514 }
1515 printed_len += text_len;
1438 1516
1439 /* 1517 /*
1440 * Try to acquire and then immediately release the console semaphore. 1518 * Try to acquire and then immediately release the console semaphore.
@@ -1521,11 +1599,18 @@ EXPORT_SYMBOL(printk);
1521#else 1599#else
1522 1600
1523#define LOG_LINE_MAX 0 1601#define LOG_LINE_MAX 0
1602static struct cont {
1603 size_t len;
1604 size_t cons;
1605 u8 level;
1606 bool flushed:1;
1607} cont;
1524static struct log *log_from_idx(u32 idx) { return NULL; } 1608static struct log *log_from_idx(u32 idx) { return NULL; }
1525static u32 log_next(u32 idx) { return 0; } 1609static u32 log_next(u32 idx) { return 0; }
1526static void call_console_drivers(int level, const char *text, size_t len) {} 1610static void call_console_drivers(int level, const char *text, size_t len) {}
1527static size_t msg_print_text(const struct log *msg, bool syslog, 1611static size_t msg_print_text(const struct log *msg, bool syslog,
1528 char *buf, size_t size) { return 0; } 1612 char *buf, size_t size) { return 0; }
1613static size_t cont_print_text(char *text, size_t size) { return 0; }
1529 1614
1530#endif /* CONFIG_PRINTK */ 1615#endif /* CONFIG_PRINTK */
1531 1616
@@ -1817,6 +1902,7 @@ static u32 console_idx;
1817 */ 1902 */
1818void console_unlock(void) 1903void console_unlock(void)
1819{ 1904{
1905 static char text[LOG_LINE_MAX];
1820 static u64 seen_seq; 1906 static u64 seen_seq;
1821 unsigned long flags; 1907 unsigned long flags;
1822 bool wake_klogd = false; 1908 bool wake_klogd = false;
@@ -1829,10 +1915,23 @@ void console_unlock(void)
1829 1915
1830 console_may_schedule = 0; 1916 console_may_schedule = 0;
1831 1917
1918 /* flush buffered message fragment immediately to console */
1919 raw_spin_lock_irqsave(&logbuf_lock, flags);
1920 if (cont.len && (cont.cons < cont.len || cont.flushed)) {
1921 size_t len;
1922
1923 len = cont_print_text(text, sizeof(text));
1924 raw_spin_unlock(&logbuf_lock);
1925 stop_critical_timings();
1926 call_console_drivers(cont.level, text, len);
1927 start_critical_timings();
1928 local_irq_restore(flags);
1929 } else
1930 raw_spin_unlock_irqrestore(&logbuf_lock, flags);
1931
1832again: 1932again:
1833 for (;;) { 1933 for (;;) {
1834 struct log *msg; 1934 struct log *msg;
1835 static char text[LOG_LINE_MAX];
1836 size_t len; 1935 size_t len;
1837 int level; 1936 int level;
1838 1937
@@ -1847,13 +1946,22 @@ again:
1847 console_seq = log_first_seq; 1946 console_seq = log_first_seq;
1848 console_idx = log_first_idx; 1947 console_idx = log_first_idx;
1849 } 1948 }
1850 1949skip:
1851 if (console_seq == log_next_seq) 1950 if (console_seq == log_next_seq)
1852 break; 1951 break;
1853 1952
1854 msg = log_from_idx(console_idx); 1953 msg = log_from_idx(console_idx);
1855 level = msg->level & 7; 1954 if (msg->flags & LOG_NOCONS) {
1955 /*
1956 * Skip record we have buffered and already printed
1957 * directly to the console when we received it.
1958 */
1959 console_idx = log_next(console_idx);
1960 console_seq++;
1961 goto skip;
1962 }
1856 1963
1964 level = msg->level;
1857 len = msg_print_text(msg, false, text, sizeof(text)); 1965 len = msg_print_text(msg, false, text, sizeof(text));
1858 1966
1859 console_idx = log_next(console_idx); 1967 console_idx = log_next(console_idx);