Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'printk-cleanups'

Merge my system logging cleanups, triggered by the broken '\n' patches.

The line continuation handling has been broken basically forever, and
the code to handle the system log records was both confusing and
dubious. And it would do entirely the wrong thing unless you always had
a terminating newline, partly because it couldn't actually see whether a
message was marked KERN_CONT or not (but partly because the LOG_CONT
handling in the recording code was rather confusing too).

This re-introduces a real semantically meaningful KERN_CONT, and fixes
the few places I noticed where it was missing. There are probably more
missing cases, since KERN_CONT hasn't actually had any semantic meaning
for at least four years (other than the checkpatch meaning of "no log
level necessary, this is a continuation line").

This also allows the combination of KERN_CONT and a log level. In that
case the log level will be ignored if the merging with a previous line
is successful, but if a new record is needed, that new record will now
get the right log level.

That also means that you can at least in theory combine KERN_CONT with
the "pr_info()" style helpers, although any use of pr_fmt() prefixing
would make that just result in a mess, of course (the prefix would end
up in the middle of a continuing line).

* printk-cleanups:
printk: make reading the kernel log flush pending lines
printk: re-organize log_output() to be more legible
printk: split out core logging code into helper function
printk: reinstate KERN_CONT for printing continuation lines

+68 -69
+3 -3
drivers/tty/vt/vt.c
··· 3187 3187 3188 3188 pr_info("Console: switching "); 3189 3189 if (!deflt) 3190 - printk("consoles %d-%d ", first+1, last+1); 3190 + printk(KERN_CONT "consoles %d-%d ", first+1, last+1); 3191 3191 if (j >= 0) { 3192 3192 struct vc_data *vc = vc_cons[j].d; 3193 3193 3194 - printk("to %s %s %dx%d\n", 3194 + printk(KERN_CONT "to %s %s %dx%d\n", 3195 3195 vc->vc_can_do_color ? "colour" : "mono", 3196 3196 desc, vc->vc_cols, vc->vc_rows); 3197 3197 ··· 3200 3200 update_screen(vc); 3201 3201 } 3202 3202 } else 3203 - printk("to %s\n", desc); 3203 + printk(KERN_CONT "to %s\n", desc); 3204 3204 3205 3205 retval = 0; 3206 3206 err:
+1 -1
include/linux/kern_levels.h
··· 20 20 * line that had no enclosing \n). Only to be used by core/arch code 21 21 * during early bootup (a continued line is not SMP-safe otherwise). 22 22 */ 23 - #define KERN_CONT "" 23 + #define KERN_CONT KERN_SOH "c" 24 24 25 25 /* integer equivalents of KERN_<LEVEL> */ 26 26 #define LOGLEVEL_SCHED -2 /* Deferred messages from sched code
+1
include/linux/printk.h
··· 16 16 switch (buffer[1]) { 17 17 case '0' ... '7': 18 18 case 'd': /* KERN_DEFAULT */ 19 + case 'c': /* KERN_CONT */ 19 20 return buffer[1]; 20 21 } 21 22 }
+61 -63
kernel/printk/printk.c
··· 666 666 * better readable output. 'c' in the record flags mark the first 667 667 * fragment of a line, '+' the following. 668 668 */ 669 - if (msg->flags & LOG_CONT && !(prev_flags & LOG_CONT)) 670 - cont = 'c'; 671 - else if ((msg->flags & LOG_CONT) || 672 - ((prev_flags & LOG_CONT) && !(msg->flags & LOG_PREFIX))) 673 - cont = '+'; 669 + if (msg->flags & LOG_CONT) 670 + cont = (prev_flags & LOG_CONT) ? '+' : 'c'; 674 671 675 672 return scnprintf(buf, size, "%u,%llu,%llu,%c;", 676 673 (msg->facility << 3) | msg->level, seq, ts_usec, cont); ··· 794 797 return ret; 795 798 } 796 799 800 + static void cont_flush(void); 801 + 797 802 static ssize_t devkmsg_read(struct file *file, char __user *buf, 798 803 size_t count, loff_t *ppos) 799 804 { ··· 811 812 if (ret) 812 813 return ret; 813 814 raw_spin_lock_irq(&logbuf_lock); 815 + cont_flush(); 814 816 while (user->seq == log_next_seq) { 815 817 if (file->f_flags & O_NONBLOCK) { 816 818 ret = -EAGAIN; ··· 874 874 return -ESPIPE; 875 875 876 876 raw_spin_lock_irq(&logbuf_lock); 877 + cont_flush(); 877 878 switch (whence) { 878 879 case SEEK_SET: 879 880 /* the first record */ ··· 913 912 poll_wait(file, &log_wait, wait); 914 913 915 914 raw_spin_lock_irq(&logbuf_lock); 915 + cont_flush(); 916 916 if (user->seq < log_next_seq) { 917 917 /* return error when data has vanished underneath us */ 918 918 if (user->seq < log_first_seq) ··· 1300 1298 size_t skip; 1301 1299 1302 1300 raw_spin_lock_irq(&logbuf_lock); 1301 + cont_flush(); 1303 1302 if (syslog_seq < log_first_seq) { 1304 1303 /* messages are gone, move to first one */ 1305 1304 syslog_seq = log_first_seq; ··· 1360 1357 return -ENOMEM; 1361 1358 1362 1359 raw_spin_lock_irq(&logbuf_lock); 1360 + cont_flush(); 1363 1361 if (buf) { 1364 1362 u64 next_seq; 1365 1363 u64 seq; ··· 1522 1518 /* Number of chars in the log buffer */ 1523 1519 case SYSLOG_ACTION_SIZE_UNREAD: 1524 1520 raw_spin_lock_irq(&logbuf_lock); 1521 + cont_flush(); 1525 1522 if (syslog_seq < log_first_seq) { 1526 1523 /* messages are gone, move to first one */ 1527 1524 syslog_seq = log_first_seq; ··· 1659 1654 bool flushed:1; /* buffer sealed and committed */ 1660 1655 } cont; 1661 1656 1662 - static void cont_flush(enum log_flags flags) 1657 + static void cont_flush(void) 1663 1658 { 1664 1659 if (cont.flushed) 1665 1660 return; 1666 1661 if (cont.len == 0) 1667 1662 return; 1668 - 1669 1663 if (cont.cons) { 1670 1664 /* 1671 1665 * If a fragment of this line was directly flushed to the 1672 1666 * console; wait for the console to pick up the rest of the 1673 1667 * line. LOG_NOCONS suppresses a duplicated output. 1674 1668 */ 1675 - log_store(cont.facility, cont.level, flags | LOG_NOCONS, 1669 + log_store(cont.facility, cont.level, cont.flags | LOG_NOCONS, 1676 1670 cont.ts_nsec, NULL, 0, cont.buf, cont.len); 1677 - cont.flags = flags; 1678 1671 cont.flushed = true; 1679 1672 } else { 1680 1673 /* 1681 1674 * If no fragment of this line ever reached the console, 1682 1675 * just submit it to the store and free the buffer. 1683 1676 */ 1684 - log_store(cont.facility, cont.level, flags, 0, 1677 + log_store(cont.facility, cont.level, cont.flags, 0, 1685 1678 NULL, 0, cont.buf, cont.len); 1686 1679 cont.len = 0; 1687 1680 } 1688 1681 } 1689 1682 1690 - static bool cont_add(int facility, int level, const char *text, size_t len) 1683 + static bool cont_add(int facility, int level, enum log_flags flags, const char *text, size_t len) 1691 1684 { 1692 1685 if (cont.len && cont.flushed) 1693 1686 return false; ··· 1696 1693 * the line gets too long, split it up in separate records. 1697 1694 */ 1698 1695 if (nr_ext_console_drivers || cont.len + len > sizeof(cont.buf)) { 1699 - cont_flush(LOG_CONT); 1696 + cont_flush(); 1700 1697 return false; 1701 1698 } 1702 1699 ··· 1705 1702 cont.level = level; 1706 1703 cont.owner = current; 1707 1704 cont.ts_nsec = local_clock(); 1708 - cont.flags = 0; 1705 + cont.flags = flags; 1709 1706 cont.cons = 0; 1710 1707 cont.flushed = false; 1711 1708 } ··· 1713 1710 memcpy(cont.buf + cont.len, text, len); 1714 1711 cont.len += len; 1715 1712 1713 + // The original flags come from the first line, 1714 + // but later continuations can add a newline. 1715 + if (flags & LOG_NEWLINE) { 1716 + cont.flags |= LOG_NEWLINE; 1717 + cont_flush(); 1718 + } 1719 + 1716 1720 if (cont.len > (sizeof(cont.buf) * 80) / 100) 1717 - cont_flush(LOG_CONT); 1721 + cont_flush(); 1718 1722 1719 1723 return true; 1720 1724 } ··· 1752 1742 cont.len = 0; 1753 1743 } 1754 1744 return textlen; 1745 + } 1746 + 1747 + static size_t log_output(int facility, int level, enum log_flags lflags, const char *dict, size_t dictlen, char *text, size_t text_len) 1748 + { 1749 + /* 1750 + * If an earlier line was buffered, and we're a continuation 1751 + * write from the same process, try to add it to the buffer. 1752 + */ 1753 + if (cont.len) { 1754 + if (cont.owner == current && (lflags & LOG_CONT)) { 1755 + if (cont_add(facility, level, lflags, text, text_len)) 1756 + return text_len; 1757 + } 1758 + /* Otherwise, make sure it's flushed */ 1759 + cont_flush(); 1760 + } 1761 + 1762 + /* If it doesn't end in a newline, try to buffer the current line */ 1763 + if (!(lflags & LOG_NEWLINE)) { 1764 + if (cont_add(facility, level, lflags, text, text_len)) 1765 + return text_len; 1766 + } 1767 + 1768 + /* Store it in the record log */ 1769 + return log_store(facility, level, lflags, 0, dict, dictlen, text, text_len); 1755 1770 } 1756 1771 1757 1772 asmlinkage int vprintk_emit(int facility, int level, ··· 1865 1830 1866 1831 /* strip kernel syslog prefix and extract log level or control flags */ 1867 1832 if (facility == 0) { 1868 - int kern_level = printk_get_level(text); 1833 + int kern_level; 1869 1834 1870 - if (kern_level) { 1871 - const char *end_of_header = printk_skip_level(text); 1835 + while ((kern_level = printk_get_level(text)) != 0) { 1872 1836 switch (kern_level) { 1873 1837 case '0' ... '7': 1874 1838 if (level == LOGLEVEL_DEFAULT) ··· 1875 1841 /* fallthrough */ 1876 1842 case 'd': /* KERN_DEFAULT */ 1877 1843 lflags |= LOG_PREFIX; 1844 + break; 1845 + case 'c': /* KERN_CONT */ 1846 + lflags |= LOG_CONT; 1878 1847 } 1879 - /* 1880 - * No need to check length here because vscnprintf 1881 - * put '\0' at the end of the string. Only valid and 1882 - * newly printed level is detected. 1883 - */ 1884 - text_len -= end_of_header - text; 1885 - text = (char *)end_of_header; 1848 + 1849 + text_len -= 2; 1850 + text += 2; 1886 1851 } 1887 1852 } 1888 1853 ··· 1891 1858 if (dict) 1892 1859 lflags |= LOG_PREFIX|LOG_NEWLINE; 1893 1860 1894 - if (!(lflags & LOG_NEWLINE)) { 1895 - /* 1896 - * Flush the conflicting buffer. An earlier newline was missing, 1897 - * or another task also prints continuation lines. 1898 - */ 1899 - if (cont.len && (lflags & LOG_PREFIX || cont.owner != current)) 1900 - cont_flush(LOG_NEWLINE); 1901 - 1902 - /* buffer line if possible, otherwise store it right away */ 1903 - if (cont_add(facility, level, text, text_len)) 1904 - printed_len += text_len; 1905 - else 1906 - printed_len += log_store(facility, level, 1907 - lflags | LOG_CONT, 0, 1908 - dict, dictlen, text, text_len); 1909 - } else { 1910 - bool stored = false; 1911 - 1912 - /* 1913 - * If an earlier newline was missing and it was the same task, 1914 - * either merge it with the current buffer and flush, or if 1915 - * there was a race with interrupts (prefix == true) then just 1916 - * flush it out and store this line separately. 1917 - * If the preceding printk was from a different task and missed 1918 - * a newline, flush and append the newline. 1919 - */ 1920 - if (cont.len) { 1921 - if (cont.owner == current && !(lflags & LOG_PREFIX)) 1922 - stored = cont_add(facility, level, text, 1923 - text_len); 1924 - cont_flush(LOG_NEWLINE); 1925 - } 1926 - 1927 - if (stored) 1928 - printed_len += text_len; 1929 - else 1930 - printed_len += log_store(facility, level, lflags, 0, 1931 - dict, dictlen, text, text_len); 1932 - } 1861 + printed_len += log_output(facility, level, lflags, dict, dictlen, text, text_len); 1933 1862 1934 1863 logbuf_cpu = UINT_MAX; 1935 1864 raw_spin_unlock(&logbuf_lock); ··· 3035 3040 dumper->active = true; 3036 3041 3037 3042 raw_spin_lock_irqsave(&logbuf_lock, flags); 3043 + cont_flush(); 3038 3044 dumper->cur_seq = clear_seq; 3039 3045 dumper->cur_idx = clear_idx; 3040 3046 dumper->next_seq = log_next_seq; ··· 3126 3130 bool ret; 3127 3131 3128 3132 raw_spin_lock_irqsave(&logbuf_lock, flags); 3133 + cont_flush(); 3129 3134 ret = kmsg_dump_get_line_nolock(dumper, syslog, line, size, len); 3130 3135 raw_spin_unlock_irqrestore(&logbuf_lock, flags); 3131 3136 ··· 3169 3172 goto out; 3170 3173 3171 3174 raw_spin_lock_irqsave(&logbuf_lock, flags); 3175 + cont_flush(); 3172 3176 if (dumper->cur_seq < log_first_seq) { 3173 3177 /* messages are gone, move to first available one */ 3174 3178 dumper->cur_seq = log_first_seq;
+2 -2
security/selinux/ss/policydb.c
··· 527 527 printk(KERN_DEBUG "SELinux: %d users, %d roles, %d types, %d bools", 528 528 p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim, p->p_bools.nprim); 529 529 if (p->mls_enabled) 530 - printk(", %d sens, %d cats", p->p_levels.nprim, 530 + printk(KERN_CONT ", %d sens, %d cats", p->p_levels.nprim, 531 531 p->p_cats.nprim); 532 - printk("\n"); 532 + printk(KERN_CONT "\n"); 533 533 534 534 printk(KERN_DEBUG "SELinux: %d classes, %d rules\n", 535 535 p->p_classes.nprim, p->te_avtab.nel);