Loading include/linux/console.h +73 −27 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #define _LINUX_CONSOLE_H_ 1 #include <linux/atomic.h> #include <linux/bits.h> #include <linux/rculist.h> #include <linux/types.h> Loading Loading @@ -125,28 +126,73 @@ static inline int con_debug_leave(void) /* * The interface for a console, or any other device that wants to capture * console messages (printer driver?) * * If a console driver is marked CON_BOOT then it will be auto-unregistered * when the first real console is registered. This is for early-printk drivers. */ #define CON_PRINTBUFFER (1) #define CON_CONSDEV (2) /* Preferred console, /dev/console */ #define CON_ENABLED (4) #define CON_BOOT (8) #define CON_ANYTIME (16) /* Safe to call when cpu is offline */ #define CON_BRL (32) /* Used for a braille device */ #define CON_EXTENDED (64) /* Use the extended output format a la /dev/kmsg */ /** * cons_flags - General console flags * @CON_PRINTBUFFER: Used by newly registered consoles to avoid duplicate * output of messages that were already shown by boot * consoles or read by userspace via syslog() syscall. * @CON_CONSDEV: Indicates that the console driver is backing * /dev/console. * @CON_ENABLED: Indicates if a console is allowed to print records. If * false, the console also will not advance to later * records. * @CON_BOOT: Marks the console driver as early console driver which * is used during boot before the real driver becomes * available. It will be automatically unregistered * when the real console driver is registered unless * "keep_bootcon" parameter is used. * @CON_ANYTIME: A misnomed historical flag which tells the core code * that the legacy @console::write callback can be invoked * on a CPU which is marked OFFLINE. That is misleading as * it suggests that there is no contextual limit for * invoking the callback. The original motivation was * readiness of the per-CPU areas. * @CON_BRL: Indicates a braille device which is exempt from * receiving the printk spam for obvious reasons. * @CON_EXTENDED: The console supports the extended output format of * /dev/kmesg which requires a larger output buffer. */ enum cons_flags { CON_PRINTBUFFER = BIT(0), CON_CONSDEV = BIT(1), CON_ENABLED = BIT(2), CON_BOOT = BIT(3), CON_ANYTIME = BIT(4), CON_BRL = BIT(5), CON_EXTENDED = BIT(6), }; /** * struct console - The console descriptor structure * @name: The name of the console driver * @write: Write callback to output messages (Optional) * @read: Read callback for console input (Optional) * @device: The underlying TTY device driver (Optional) * @unblank: Callback to unblank the console (Optional) * @setup: Callback for initializing the console (Optional) * @exit: Callback for teardown of the console (Optional) * @match: Callback for matching a console (Optional) * @flags: Console flags. See enum cons_flags * @index: Console index, e.g. port number * @cflag: TTY control mode flags * @ispeed: TTY input speed * @ospeed: TTY output speed * @seq: Sequence number of the next ringbuffer record to print * @dropped: Number of unreported dropped ringbuffer records * @data: Driver private data * @node: hlist node for the console list */ struct console { char name[16]; void (*write)(struct console *, const char *, unsigned); int (*read)(struct console *, char *, unsigned); struct tty_driver *(*device)(struct console *, int *); void (*write)(struct console *co, const char *s, unsigned int count); int (*read)(struct console *co, char *s, unsigned int count); struct tty_driver *(*device)(struct console *co, int *index); void (*unblank)(void); int (*setup)(struct console *, char *); int (*exit)(struct console *); int (*match)(struct console *, char *name, int idx, char *options); int (*setup)(struct console *co, char *options); int (*exit)(struct console *co); int (*match)(struct console *co, char *name, int idx, char *options); short flags; short index; int cflag; Loading include/linux/printk.h +0 −2 Original line number Diff line number Diff line Loading @@ -44,8 +44,6 @@ static inline const char *printk_skip_headers(const char *buffer) return buffer; } #define CONSOLE_EXT_LOG_MAX 8192 /* printk's without a loglevel use this.. */ #define MESSAGE_LOGLEVEL_DEFAULT CONFIG_MESSAGE_LOGLEVEL_DEFAULT Loading kernel/printk/internal.h +45 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,21 @@ int devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write, #ifdef CONFIG_PRINTK #ifdef CONFIG_PRINTK_CALLER #define PRINTK_PREFIX_MAX 48 #else #define PRINTK_PREFIX_MAX 32 #endif /* * the maximum size of a formatted record (i.e. with prefix added * per line and dropped messages or in extended message format) */ #define PRINTK_MESSAGE_MAX 2048 /* the maximum size allowed to be reserved for a record */ #define PRINTKRB_RECORD_MAX 1024 /* Flags for a single printk record. */ enum printk_info_flags { LOG_NEWLINE = 2, /* text ended with a newline */ Loading Loading @@ -48,6 +63,10 @@ u16 printk_parse_prefix(const char *text, int *level, enum printk_info_flags *flags); #else #define PRINTK_PREFIX_MAX 0 #define PRINTK_MESSAGE_MAX 0 #define PRINTKRB_RECORD_MAX 0 /* * In !PRINTK builds we still export console_sem * semaphore and some of console functions (console_unlock()/etc.), so Loading @@ -58,3 +77,29 @@ u16 printk_parse_prefix(const char *text, int *level, static inline bool printk_percpu_data_ready(void) { return false; } #endif /* CONFIG_PRINTK */ /** * struct printk_buffers - Buffers to read/format/output printk messages. * @outbuf: After formatting, contains text to output. * @scratchbuf: Used as temporary ringbuffer reading and string-print space. */ struct printk_buffers { char outbuf[PRINTK_MESSAGE_MAX]; char scratchbuf[PRINTKRB_RECORD_MAX]; }; /** * struct printk_message - Container for a prepared printk message. * @pbufs: printk buffers used to prepare the message. * @outbuf_len: The length of prepared text in @pbufs->outbuf to output. This * does not count the terminator. A value of 0 means there is * nothing to output and this record should be skipped. * @seq: The sequence number of the record used for @pbufs->outbuf. * @dropped: The number of dropped records from reading @seq. */ struct printk_message { struct printk_buffers *pbufs; unsigned int outbuf_len; u64 seq; unsigned long dropped; }; kernel/printk/printk.c +179 −129 Original line number Diff line number Diff line Loading @@ -465,21 +465,6 @@ static struct latched_seq clear_seq = { .val[1] = 0, }; #ifdef CONFIG_PRINTK_CALLER #define PREFIX_MAX 48 #else #define PREFIX_MAX 32 #endif /* the maximum size of a formatted record (i.e. with prefix added per line) */ #define CONSOLE_LOG_MAX 1024 /* the maximum size for a dropped text message */ #define DROPPED_TEXT_MAX 64 /* the maximum size allowed to be reserved for a record */ #define LOG_LINE_MAX (CONSOLE_LOG_MAX - PREFIX_MAX) #define LOG_LEVEL(v) ((v) & 0x07) #define LOG_FACILITY(v) ((v) >> 3 & 0xff) Loading Loading @@ -710,16 +695,15 @@ static ssize_t msg_print_ext_body(char *buf, size_t size, return len; } static bool printk_get_next_message(struct printk_message *pmsg, u64 seq, bool is_extended, bool may_supress); /* /dev/kmsg - userspace message inject/listen interface */ struct devkmsg_user { atomic64_t seq; struct ratelimit_state rs; struct mutex lock; char buf[CONSOLE_EXT_LOG_MAX]; struct printk_info info; char text_buf[CONSOLE_EXT_LOG_MAX]; struct printk_record record; struct printk_buffers pbufs; }; static __printf(3, 4) __cold Loading @@ -745,7 +729,7 @@ static ssize_t devkmsg_write(struct kiocb *iocb, struct iov_iter *from) size_t len = iov_iter_count(from); ssize_t ret = len; if (!user || len > LOG_LINE_MAX) if (!user || len > PRINTKRB_RECORD_MAX) return -EINVAL; /* Ignore when user logging is disabled. */ Loading Loading @@ -801,8 +785,10 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct devkmsg_user *user = file->private_data; struct printk_record *r = &user->record; size_t len; char *outbuf = &user->pbufs.outbuf[0]; struct printk_message pmsg = { .pbufs = &user->pbufs, }; ssize_t ret; if (!user) Loading @@ -812,7 +798,7 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf, if (ret) return ret; if (!prb_read_valid(prb, atomic64_read(&user->seq), r)) { if (!printk_get_next_message(&pmsg, atomic64_read(&user->seq), true, false)) { if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; goto out; Loading @@ -829,36 +815,31 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf, * This pairs with __wake_up_klogd:A. */ ret = wait_event_interruptible(log_wait, prb_read_valid(prb, atomic64_read(&user->seq), r)); /* LMM(devkmsg_read:A) */ printk_get_next_message(&pmsg, atomic64_read(&user->seq), true, false)); /* LMM(devkmsg_read:A) */ if (ret) goto out; } if (r->info->seq != atomic64_read(&user->seq)) { if (pmsg.dropped) { /* our last seen message is gone, return error and reset */ atomic64_set(&user->seq, r->info->seq); atomic64_set(&user->seq, pmsg.seq); ret = -EPIPE; goto out; } len = info_print_ext_header(user->buf, sizeof(user->buf), r->info); len += msg_print_ext_body(user->buf + len, sizeof(user->buf) - len, &r->text_buf[0], r->info->text_len, &r->info->dev_info); atomic64_set(&user->seq, r->info->seq + 1); atomic64_set(&user->seq, pmsg.seq + 1); if (len > count) { if (pmsg.outbuf_len > count) { ret = -EINVAL; goto out; } if (copy_to_user(buf, user->buf, len)) { if (copy_to_user(buf, outbuf, pmsg.outbuf_len)) { ret = -EFAULT; goto out; } ret = len; ret = pmsg.outbuf_len; out: mutex_unlock(&user->lock); return ret; Loading Loading @@ -952,9 +933,6 @@ static int devkmsg_open(struct inode *inode, struct file *file) mutex_init(&user->lock); prb_rec_init_rd(&user->record, &user->info, &user->text_buf[0], sizeof(user->text_buf)); atomic64_set(&user->seq, prb_first_valid_seq(prb)); file->private_data = user; Loading Loading @@ -1149,7 +1127,7 @@ static unsigned int __init add_to_rb(struct printk_ringbuffer *rb, return prb_record_text_space(&e); } static char setup_text_buf[LOG_LINE_MAX] __initdata; static char setup_text_buf[PRINTKRB_RECORD_MAX] __initdata; void __init setup_log_buf(int early) { Loading Loading @@ -1415,7 +1393,7 @@ static size_t record_print_text(struct printk_record *r, bool syslog, size_t text_len = r->info->text_len; size_t buf_size = r->text_buf_size; char *text = r->text_buf; char prefix[PREFIX_MAX]; char prefix[PRINTK_PREFIX_MAX]; bool truncated = false; size_t prefix_len; size_t line_len; Loading Loading @@ -1514,7 +1492,7 @@ static size_t get_record_print_text_size(struct printk_info *info, unsigned int line_count, bool syslog, bool time) { char prefix[PREFIX_MAX]; char prefix[PRINTK_PREFIX_MAX]; size_t prefix_len; prefix_len = info_print_prefix(info, syslog, time, prefix); Loading Loading @@ -1580,11 +1558,11 @@ static int syslog_print(char __user *buf, int size) int len = 0; u64 seq; text = kmalloc(CONSOLE_LOG_MAX, GFP_KERNEL); text = kmalloc(PRINTK_MESSAGE_MAX, GFP_KERNEL); if (!text) return -ENOMEM; prb_rec_init_rd(&r, &info, text, CONSOLE_LOG_MAX); prb_rec_init_rd(&r, &info, text, PRINTK_MESSAGE_MAX); mutex_lock(&syslog_lock); Loading Loading @@ -1685,7 +1663,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear) u64 seq; bool time; text = kmalloc(CONSOLE_LOG_MAX, GFP_KERNEL); text = kmalloc(PRINTK_MESSAGE_MAX, GFP_KERNEL); if (!text) return -ENOMEM; Loading @@ -1697,7 +1675,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear) seq = find_first_fitting_seq(latched_seq_read_nolock(&clear_seq), -1, size, true, time); prb_rec_init_rd(&r, &info, text, CONSOLE_LOG_MAX); prb_rec_init_rd(&r, &info, text, PRINTK_MESSAGE_MAX); len = 0; prb_for_each_record(seq, prb, seq, &r) { Loading Loading @@ -2010,27 +1988,6 @@ static int console_trylock_spinning(void) return 1; } /* * Call the specified console driver, asking it to write out the specified * text and length. If @dropped_text is non-NULL and any records have been * dropped, a dropped message will be written out first. */ static void call_console_driver(struct console *con, const char *text, size_t len, char *dropped_text) { size_t dropped_len; if (con->dropped && dropped_text) { dropped_len = snprintf(dropped_text, DROPPED_TEXT_MAX, "** %lu printk messages dropped **\n", con->dropped); con->dropped = 0; con->write(con, dropped_text, dropped_len); } con->write(con, text, len); } /* * Recursion is tracked separately on each CPU. If NMIs are supported, an * additional NMI context per CPU is also separately tracked. Until per-CPU Loading Loading @@ -2241,8 +2198,8 @@ int vprintk_store(int facility, int level, reserve_size = vsnprintf(&prefix_buf[0], sizeof(prefix_buf), fmt, args2) + 1; va_end(args2); if (reserve_size > LOG_LINE_MAX) reserve_size = LOG_LINE_MAX; if (reserve_size > PRINTKRB_RECORD_MAX) reserve_size = PRINTKRB_RECORD_MAX; /* Extract log level or control flags. */ if (facility == 0) Loading @@ -2256,7 +2213,7 @@ int vprintk_store(int facility, int level, if (flags & LOG_CONT) { prb_rec_init_wr(&r, reserve_size); if (prb_reserve_in_last(&e, prb, &r, caller_id, LOG_LINE_MAX)) { if (prb_reserve_in_last(&e, prb, &r, caller_id, PRINTKRB_RECORD_MAX)) { text_len = printk_sprint(&r.text_buf[r.info->text_len], reserve_size, facility, &flags, fmt, args); r.info->text_len += text_len; Loading Loading @@ -2387,8 +2344,6 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre #else /* CONFIG_PRINTK */ #define CONSOLE_LOG_MAX 0 #define DROPPED_TEXT_MAX 0 #define printk_time false #define prb_read_valid(rb, seq, r) false Loading @@ -2412,10 +2367,6 @@ static ssize_t msg_print_ext_body(char *buf, size_t size, struct dev_printk_info *dev_info) { return 0; } static void console_lock_spinning_enable(void) { } static int console_lock_spinning_disable_and_check(int cookie) { return 0; } static void call_console_driver(struct console *con, const char *text, size_t len, char *dropped_text) { } static bool suppress_message_printing(int level) { return false; } static bool pr_flush(int timeout_ms, bool reset_on_progress) { return true; } static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progress) { return true; } Loading Loading @@ -2742,16 +2693,136 @@ static void __console_unlock(void) } /* * Print one record for the given console. The record printed is whatever * record is the next available record for the given console. * Prepend the message in @pmsg->pbufs->outbuf with a "dropped message". This * is achieved by shifting the existing message over and inserting the dropped * message. * * @text is a buffer of size CONSOLE_LOG_MAX. * @pmsg is the printk message to prepend. * * If extended messages should be printed, @ext_text is a buffer of size * CONSOLE_EXT_LOG_MAX. Otherwise @ext_text must be NULL. * @dropped is the dropped count to report in the dropped message. * * If the message text in @pmsg->pbufs->outbuf does not have enough space for * the dropped message, the message text will be sufficiently truncated. * * If @pmsg->pbufs->outbuf is modified, @pmsg->outbuf_len is updated. */ #ifdef CONFIG_PRINTK static void console_prepend_dropped(struct printk_message *pmsg, unsigned long dropped) { struct printk_buffers *pbufs = pmsg->pbufs; const size_t scratchbuf_sz = sizeof(pbufs->scratchbuf); const size_t outbuf_sz = sizeof(pbufs->outbuf); char *scratchbuf = &pbufs->scratchbuf[0]; char *outbuf = &pbufs->outbuf[0]; size_t len; len = scnprintf(scratchbuf, scratchbuf_sz, "** %lu printk messages dropped **\n", dropped); /* * Make sure outbuf is sufficiently large before prepending. * Keep at least the prefix when the message must be truncated. * It is a rather theoretical problem when someone tries to * use a minimalist buffer. */ if (WARN_ON_ONCE(len + PRINTK_PREFIX_MAX >= outbuf_sz)) return; if (pmsg->outbuf_len + len >= outbuf_sz) { /* Truncate the message, but keep it terminated. */ pmsg->outbuf_len = outbuf_sz - (len + 1); outbuf[pmsg->outbuf_len] = 0; } memmove(outbuf + len, outbuf, pmsg->outbuf_len + 1); memcpy(outbuf, scratchbuf, len); pmsg->outbuf_len += len; } #else #define console_prepend_dropped(pmsg, dropped) #endif /* CONFIG_PRINTK */ /* * Read and format the specified record (or a later record if the specified * record is not available). * * @pmsg will contain the formatted result. @pmsg->pbufs must point to a * struct printk_buffers. * * @seq is the record to read and format. If it is not available, the next * valid record is read. * * @is_extended specifies if the message should be formatted for extended * console output. * * @may_supress specifies if records may be skipped based on loglevel. * * Returns false if no record is available. Otherwise true and all fields * of @pmsg are valid. (See the documentation of struct printk_message * for information about the @pmsg fields.) */ static bool printk_get_next_message(struct printk_message *pmsg, u64 seq, bool is_extended, bool may_suppress) { static int panic_console_dropped; struct printk_buffers *pbufs = pmsg->pbufs; const size_t scratchbuf_sz = sizeof(pbufs->scratchbuf); const size_t outbuf_sz = sizeof(pbufs->outbuf); char *scratchbuf = &pbufs->scratchbuf[0]; char *outbuf = &pbufs->outbuf[0]; struct printk_info info; struct printk_record r; size_t len = 0; /* * Formatting extended messages requires a separate buffer, so use the * scratch buffer to read in the ringbuffer text. * * If dropped messages should be printed, @dropped_text is a buffer of size * DROPPED_TEXT_MAX. Otherwise @dropped_text must be NULL. * Formatting normal messages is done in-place, so read the ringbuffer * text directly into the output buffer. */ if (is_extended) prb_rec_init_rd(&r, &info, scratchbuf, scratchbuf_sz); else prb_rec_init_rd(&r, &info, outbuf, outbuf_sz); if (!prb_read_valid(prb, seq, &r)) return false; pmsg->seq = r.info->seq; pmsg->dropped = r.info->seq - seq; /* * Check for dropped messages in panic here so that printk * suppression can occur as early as possible if necessary. */ if (pmsg->dropped && panic_in_progress() && panic_console_dropped++ > 10) { suppress_panic_printk = 1; pr_warn_once("Too many dropped messages. Suppress messages on non-panic CPUs to prevent livelock.\n"); } /* Skip record that has level above the console loglevel. */ if (may_suppress && suppress_message_printing(r.info->level)) goto out; if (is_extended) { len = info_print_ext_header(outbuf, outbuf_sz, r.info); len += msg_print_ext_body(outbuf + len, outbuf_sz - len, &r.text_buf[0], r.info->text_len, &r.info->dev_info); } else { len = record_print_text(&r, console_msg_format & MSG_FORMAT_SYSLOG, printk_time); } out: pmsg->outbuf_len = len; return true; } /* * Print one record for the given console. The record printed is whatever * record is the next available record for the given console. * * @handover will be set to true if a printk waiter has taken over the * console_lock, in which case the caller is no longer holding both the Loading @@ -2764,46 +2835,33 @@ static void __console_unlock(void) * * Requires the console_lock and the SRCU read lock. */ static bool console_emit_next_record(struct console *con, char *text, char *ext_text, char *dropped_text, bool *handover, int cookie) static bool console_emit_next_record(struct console *con, bool *handover, int cookie) { static int panic_console_dropped; struct printk_info info; struct printk_record r; unsigned long flags; char *write_text; size_t len; static struct printk_buffers pbufs; prb_rec_init_rd(&r, &info, text, CONSOLE_LOG_MAX); bool is_extended = console_srcu_read_flags(con) & CON_EXTENDED; char *outbuf = &pbufs.outbuf[0]; struct printk_message pmsg = { .pbufs = &pbufs, }; unsigned long flags; *handover = false; if (!prb_read_valid(prb, con->seq, &r)) if (!printk_get_next_message(&pmsg, con->seq, is_extended, true)) return false; if (con->seq != r.info->seq) { con->dropped += r.info->seq - con->seq; con->seq = r.info->seq; if (panic_in_progress() && panic_console_dropped++ > 10) { suppress_panic_printk = 1; pr_warn_once("Too many dropped messages. Suppress messages on non-panic CPUs to prevent livelock.\n"); } } con->dropped += pmsg.dropped; /* Skip record that has level above the console loglevel. */ if (suppress_message_printing(r.info->level)) { con->seq++; /* Skip messages of formatted length 0. */ if (pmsg.outbuf_len == 0) { con->seq = pmsg.seq + 1; goto skip; } if (ext_text) { write_text = ext_text; len = info_print_ext_header(ext_text, CONSOLE_EXT_LOG_MAX, r.info); len += msg_print_ext_body(ext_text + len, CONSOLE_EXT_LOG_MAX - len, &r.text_buf[0], r.info->text_len, &r.info->dev_info); } else { write_text = text; len = record_print_text(&r, console_msg_format & MSG_FORMAT_SYSLOG, printk_time); if (con->dropped && !is_extended) { console_prepend_dropped(&pmsg, con->dropped); con->dropped = 0; } /* Loading @@ -2819,11 +2877,15 @@ static bool console_emit_next_record(struct console *con, char *text, char *ext_ printk_safe_enter_irqsave(flags); console_lock_spinning_enable(); stop_critical_timings(); /* don't trace print latency */ call_console_driver(con, write_text, len, dropped_text); /* Do not trace print latency. */ stop_critical_timings(); /* Write everything out to the hardware. */ con->write(con, outbuf, pmsg.outbuf_len); start_critical_timings(); con->seq++; con->seq = pmsg.seq + 1; *handover = console_lock_spinning_disable_and_check(cookie); printk_safe_exit_irqrestore(flags); Loading Loading @@ -2856,9 +2918,6 @@ static bool console_emit_next_record(struct console *con, char *text, char *ext_ */ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handover) { static char dropped_text[DROPPED_TEXT_MAX]; static char ext_text[CONSOLE_EXT_LOG_MAX]; static char text[CONSOLE_LOG_MAX]; bool any_usable = false; struct console *con; bool any_progress; Loading @@ -2878,16 +2937,7 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove continue; any_usable = true; if (console_srcu_read_flags(con) & CON_EXTENDED) { /* Extended consoles do not print "dropped messages". */ progress = console_emit_next_record(con, &text[0], &ext_text[0], NULL, handover, cookie); } else { progress = console_emit_next_record(con, &text[0], NULL, &dropped_text[0], handover, cookie); } progress = console_emit_next_record(con, handover, cookie); /* * If a handover has occurred, the SRCU read lock Loading Loading
include/linux/console.h +73 −27 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #define _LINUX_CONSOLE_H_ 1 #include <linux/atomic.h> #include <linux/bits.h> #include <linux/rculist.h> #include <linux/types.h> Loading Loading @@ -125,28 +126,73 @@ static inline int con_debug_leave(void) /* * The interface for a console, or any other device that wants to capture * console messages (printer driver?) * * If a console driver is marked CON_BOOT then it will be auto-unregistered * when the first real console is registered. This is for early-printk drivers. */ #define CON_PRINTBUFFER (1) #define CON_CONSDEV (2) /* Preferred console, /dev/console */ #define CON_ENABLED (4) #define CON_BOOT (8) #define CON_ANYTIME (16) /* Safe to call when cpu is offline */ #define CON_BRL (32) /* Used for a braille device */ #define CON_EXTENDED (64) /* Use the extended output format a la /dev/kmsg */ /** * cons_flags - General console flags * @CON_PRINTBUFFER: Used by newly registered consoles to avoid duplicate * output of messages that were already shown by boot * consoles or read by userspace via syslog() syscall. * @CON_CONSDEV: Indicates that the console driver is backing * /dev/console. * @CON_ENABLED: Indicates if a console is allowed to print records. If * false, the console also will not advance to later * records. * @CON_BOOT: Marks the console driver as early console driver which * is used during boot before the real driver becomes * available. It will be automatically unregistered * when the real console driver is registered unless * "keep_bootcon" parameter is used. * @CON_ANYTIME: A misnomed historical flag which tells the core code * that the legacy @console::write callback can be invoked * on a CPU which is marked OFFLINE. That is misleading as * it suggests that there is no contextual limit for * invoking the callback. The original motivation was * readiness of the per-CPU areas. * @CON_BRL: Indicates a braille device which is exempt from * receiving the printk spam for obvious reasons. * @CON_EXTENDED: The console supports the extended output format of * /dev/kmesg which requires a larger output buffer. */ enum cons_flags { CON_PRINTBUFFER = BIT(0), CON_CONSDEV = BIT(1), CON_ENABLED = BIT(2), CON_BOOT = BIT(3), CON_ANYTIME = BIT(4), CON_BRL = BIT(5), CON_EXTENDED = BIT(6), }; /** * struct console - The console descriptor structure * @name: The name of the console driver * @write: Write callback to output messages (Optional) * @read: Read callback for console input (Optional) * @device: The underlying TTY device driver (Optional) * @unblank: Callback to unblank the console (Optional) * @setup: Callback for initializing the console (Optional) * @exit: Callback for teardown of the console (Optional) * @match: Callback for matching a console (Optional) * @flags: Console flags. See enum cons_flags * @index: Console index, e.g. port number * @cflag: TTY control mode flags * @ispeed: TTY input speed * @ospeed: TTY output speed * @seq: Sequence number of the next ringbuffer record to print * @dropped: Number of unreported dropped ringbuffer records * @data: Driver private data * @node: hlist node for the console list */ struct console { char name[16]; void (*write)(struct console *, const char *, unsigned); int (*read)(struct console *, char *, unsigned); struct tty_driver *(*device)(struct console *, int *); void (*write)(struct console *co, const char *s, unsigned int count); int (*read)(struct console *co, char *s, unsigned int count); struct tty_driver *(*device)(struct console *co, int *index); void (*unblank)(void); int (*setup)(struct console *, char *); int (*exit)(struct console *); int (*match)(struct console *, char *name, int idx, char *options); int (*setup)(struct console *co, char *options); int (*exit)(struct console *co); int (*match)(struct console *co, char *name, int idx, char *options); short flags; short index; int cflag; Loading
include/linux/printk.h +0 −2 Original line number Diff line number Diff line Loading @@ -44,8 +44,6 @@ static inline const char *printk_skip_headers(const char *buffer) return buffer; } #define CONSOLE_EXT_LOG_MAX 8192 /* printk's without a loglevel use this.. */ #define MESSAGE_LOGLEVEL_DEFAULT CONFIG_MESSAGE_LOGLEVEL_DEFAULT Loading
kernel/printk/internal.h +45 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,21 @@ int devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write, #ifdef CONFIG_PRINTK #ifdef CONFIG_PRINTK_CALLER #define PRINTK_PREFIX_MAX 48 #else #define PRINTK_PREFIX_MAX 32 #endif /* * the maximum size of a formatted record (i.e. with prefix added * per line and dropped messages or in extended message format) */ #define PRINTK_MESSAGE_MAX 2048 /* the maximum size allowed to be reserved for a record */ #define PRINTKRB_RECORD_MAX 1024 /* Flags for a single printk record. */ enum printk_info_flags { LOG_NEWLINE = 2, /* text ended with a newline */ Loading Loading @@ -48,6 +63,10 @@ u16 printk_parse_prefix(const char *text, int *level, enum printk_info_flags *flags); #else #define PRINTK_PREFIX_MAX 0 #define PRINTK_MESSAGE_MAX 0 #define PRINTKRB_RECORD_MAX 0 /* * In !PRINTK builds we still export console_sem * semaphore and some of console functions (console_unlock()/etc.), so Loading @@ -58,3 +77,29 @@ u16 printk_parse_prefix(const char *text, int *level, static inline bool printk_percpu_data_ready(void) { return false; } #endif /* CONFIG_PRINTK */ /** * struct printk_buffers - Buffers to read/format/output printk messages. * @outbuf: After formatting, contains text to output. * @scratchbuf: Used as temporary ringbuffer reading and string-print space. */ struct printk_buffers { char outbuf[PRINTK_MESSAGE_MAX]; char scratchbuf[PRINTKRB_RECORD_MAX]; }; /** * struct printk_message - Container for a prepared printk message. * @pbufs: printk buffers used to prepare the message. * @outbuf_len: The length of prepared text in @pbufs->outbuf to output. This * does not count the terminator. A value of 0 means there is * nothing to output and this record should be skipped. * @seq: The sequence number of the record used for @pbufs->outbuf. * @dropped: The number of dropped records from reading @seq. */ struct printk_message { struct printk_buffers *pbufs; unsigned int outbuf_len; u64 seq; unsigned long dropped; };
kernel/printk/printk.c +179 −129 Original line number Diff line number Diff line Loading @@ -465,21 +465,6 @@ static struct latched_seq clear_seq = { .val[1] = 0, }; #ifdef CONFIG_PRINTK_CALLER #define PREFIX_MAX 48 #else #define PREFIX_MAX 32 #endif /* the maximum size of a formatted record (i.e. with prefix added per line) */ #define CONSOLE_LOG_MAX 1024 /* the maximum size for a dropped text message */ #define DROPPED_TEXT_MAX 64 /* the maximum size allowed to be reserved for a record */ #define LOG_LINE_MAX (CONSOLE_LOG_MAX - PREFIX_MAX) #define LOG_LEVEL(v) ((v) & 0x07) #define LOG_FACILITY(v) ((v) >> 3 & 0xff) Loading Loading @@ -710,16 +695,15 @@ static ssize_t msg_print_ext_body(char *buf, size_t size, return len; } static bool printk_get_next_message(struct printk_message *pmsg, u64 seq, bool is_extended, bool may_supress); /* /dev/kmsg - userspace message inject/listen interface */ struct devkmsg_user { atomic64_t seq; struct ratelimit_state rs; struct mutex lock; char buf[CONSOLE_EXT_LOG_MAX]; struct printk_info info; char text_buf[CONSOLE_EXT_LOG_MAX]; struct printk_record record; struct printk_buffers pbufs; }; static __printf(3, 4) __cold Loading @@ -745,7 +729,7 @@ static ssize_t devkmsg_write(struct kiocb *iocb, struct iov_iter *from) size_t len = iov_iter_count(from); ssize_t ret = len; if (!user || len > LOG_LINE_MAX) if (!user || len > PRINTKRB_RECORD_MAX) return -EINVAL; /* Ignore when user logging is disabled. */ Loading Loading @@ -801,8 +785,10 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct devkmsg_user *user = file->private_data; struct printk_record *r = &user->record; size_t len; char *outbuf = &user->pbufs.outbuf[0]; struct printk_message pmsg = { .pbufs = &user->pbufs, }; ssize_t ret; if (!user) Loading @@ -812,7 +798,7 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf, if (ret) return ret; if (!prb_read_valid(prb, atomic64_read(&user->seq), r)) { if (!printk_get_next_message(&pmsg, atomic64_read(&user->seq), true, false)) { if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; goto out; Loading @@ -829,36 +815,31 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf, * This pairs with __wake_up_klogd:A. */ ret = wait_event_interruptible(log_wait, prb_read_valid(prb, atomic64_read(&user->seq), r)); /* LMM(devkmsg_read:A) */ printk_get_next_message(&pmsg, atomic64_read(&user->seq), true, false)); /* LMM(devkmsg_read:A) */ if (ret) goto out; } if (r->info->seq != atomic64_read(&user->seq)) { if (pmsg.dropped) { /* our last seen message is gone, return error and reset */ atomic64_set(&user->seq, r->info->seq); atomic64_set(&user->seq, pmsg.seq); ret = -EPIPE; goto out; } len = info_print_ext_header(user->buf, sizeof(user->buf), r->info); len += msg_print_ext_body(user->buf + len, sizeof(user->buf) - len, &r->text_buf[0], r->info->text_len, &r->info->dev_info); atomic64_set(&user->seq, r->info->seq + 1); atomic64_set(&user->seq, pmsg.seq + 1); if (len > count) { if (pmsg.outbuf_len > count) { ret = -EINVAL; goto out; } if (copy_to_user(buf, user->buf, len)) { if (copy_to_user(buf, outbuf, pmsg.outbuf_len)) { ret = -EFAULT; goto out; } ret = len; ret = pmsg.outbuf_len; out: mutex_unlock(&user->lock); return ret; Loading Loading @@ -952,9 +933,6 @@ static int devkmsg_open(struct inode *inode, struct file *file) mutex_init(&user->lock); prb_rec_init_rd(&user->record, &user->info, &user->text_buf[0], sizeof(user->text_buf)); atomic64_set(&user->seq, prb_first_valid_seq(prb)); file->private_data = user; Loading Loading @@ -1149,7 +1127,7 @@ static unsigned int __init add_to_rb(struct printk_ringbuffer *rb, return prb_record_text_space(&e); } static char setup_text_buf[LOG_LINE_MAX] __initdata; static char setup_text_buf[PRINTKRB_RECORD_MAX] __initdata; void __init setup_log_buf(int early) { Loading Loading @@ -1415,7 +1393,7 @@ static size_t record_print_text(struct printk_record *r, bool syslog, size_t text_len = r->info->text_len; size_t buf_size = r->text_buf_size; char *text = r->text_buf; char prefix[PREFIX_MAX]; char prefix[PRINTK_PREFIX_MAX]; bool truncated = false; size_t prefix_len; size_t line_len; Loading Loading @@ -1514,7 +1492,7 @@ static size_t get_record_print_text_size(struct printk_info *info, unsigned int line_count, bool syslog, bool time) { char prefix[PREFIX_MAX]; char prefix[PRINTK_PREFIX_MAX]; size_t prefix_len; prefix_len = info_print_prefix(info, syslog, time, prefix); Loading Loading @@ -1580,11 +1558,11 @@ static int syslog_print(char __user *buf, int size) int len = 0; u64 seq; text = kmalloc(CONSOLE_LOG_MAX, GFP_KERNEL); text = kmalloc(PRINTK_MESSAGE_MAX, GFP_KERNEL); if (!text) return -ENOMEM; prb_rec_init_rd(&r, &info, text, CONSOLE_LOG_MAX); prb_rec_init_rd(&r, &info, text, PRINTK_MESSAGE_MAX); mutex_lock(&syslog_lock); Loading Loading @@ -1685,7 +1663,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear) u64 seq; bool time; text = kmalloc(CONSOLE_LOG_MAX, GFP_KERNEL); text = kmalloc(PRINTK_MESSAGE_MAX, GFP_KERNEL); if (!text) return -ENOMEM; Loading @@ -1697,7 +1675,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear) seq = find_first_fitting_seq(latched_seq_read_nolock(&clear_seq), -1, size, true, time); prb_rec_init_rd(&r, &info, text, CONSOLE_LOG_MAX); prb_rec_init_rd(&r, &info, text, PRINTK_MESSAGE_MAX); len = 0; prb_for_each_record(seq, prb, seq, &r) { Loading Loading @@ -2010,27 +1988,6 @@ static int console_trylock_spinning(void) return 1; } /* * Call the specified console driver, asking it to write out the specified * text and length. If @dropped_text is non-NULL and any records have been * dropped, a dropped message will be written out first. */ static void call_console_driver(struct console *con, const char *text, size_t len, char *dropped_text) { size_t dropped_len; if (con->dropped && dropped_text) { dropped_len = snprintf(dropped_text, DROPPED_TEXT_MAX, "** %lu printk messages dropped **\n", con->dropped); con->dropped = 0; con->write(con, dropped_text, dropped_len); } con->write(con, text, len); } /* * Recursion is tracked separately on each CPU. If NMIs are supported, an * additional NMI context per CPU is also separately tracked. Until per-CPU Loading Loading @@ -2241,8 +2198,8 @@ int vprintk_store(int facility, int level, reserve_size = vsnprintf(&prefix_buf[0], sizeof(prefix_buf), fmt, args2) + 1; va_end(args2); if (reserve_size > LOG_LINE_MAX) reserve_size = LOG_LINE_MAX; if (reserve_size > PRINTKRB_RECORD_MAX) reserve_size = PRINTKRB_RECORD_MAX; /* Extract log level or control flags. */ if (facility == 0) Loading @@ -2256,7 +2213,7 @@ int vprintk_store(int facility, int level, if (flags & LOG_CONT) { prb_rec_init_wr(&r, reserve_size); if (prb_reserve_in_last(&e, prb, &r, caller_id, LOG_LINE_MAX)) { if (prb_reserve_in_last(&e, prb, &r, caller_id, PRINTKRB_RECORD_MAX)) { text_len = printk_sprint(&r.text_buf[r.info->text_len], reserve_size, facility, &flags, fmt, args); r.info->text_len += text_len; Loading Loading @@ -2387,8 +2344,6 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre #else /* CONFIG_PRINTK */ #define CONSOLE_LOG_MAX 0 #define DROPPED_TEXT_MAX 0 #define printk_time false #define prb_read_valid(rb, seq, r) false Loading @@ -2412,10 +2367,6 @@ static ssize_t msg_print_ext_body(char *buf, size_t size, struct dev_printk_info *dev_info) { return 0; } static void console_lock_spinning_enable(void) { } static int console_lock_spinning_disable_and_check(int cookie) { return 0; } static void call_console_driver(struct console *con, const char *text, size_t len, char *dropped_text) { } static bool suppress_message_printing(int level) { return false; } static bool pr_flush(int timeout_ms, bool reset_on_progress) { return true; } static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progress) { return true; } Loading Loading @@ -2742,16 +2693,136 @@ static void __console_unlock(void) } /* * Print one record for the given console. The record printed is whatever * record is the next available record for the given console. * Prepend the message in @pmsg->pbufs->outbuf with a "dropped message". This * is achieved by shifting the existing message over and inserting the dropped * message. * * @text is a buffer of size CONSOLE_LOG_MAX. * @pmsg is the printk message to prepend. * * If extended messages should be printed, @ext_text is a buffer of size * CONSOLE_EXT_LOG_MAX. Otherwise @ext_text must be NULL. * @dropped is the dropped count to report in the dropped message. * * If the message text in @pmsg->pbufs->outbuf does not have enough space for * the dropped message, the message text will be sufficiently truncated. * * If @pmsg->pbufs->outbuf is modified, @pmsg->outbuf_len is updated. */ #ifdef CONFIG_PRINTK static void console_prepend_dropped(struct printk_message *pmsg, unsigned long dropped) { struct printk_buffers *pbufs = pmsg->pbufs; const size_t scratchbuf_sz = sizeof(pbufs->scratchbuf); const size_t outbuf_sz = sizeof(pbufs->outbuf); char *scratchbuf = &pbufs->scratchbuf[0]; char *outbuf = &pbufs->outbuf[0]; size_t len; len = scnprintf(scratchbuf, scratchbuf_sz, "** %lu printk messages dropped **\n", dropped); /* * Make sure outbuf is sufficiently large before prepending. * Keep at least the prefix when the message must be truncated. * It is a rather theoretical problem when someone tries to * use a minimalist buffer. */ if (WARN_ON_ONCE(len + PRINTK_PREFIX_MAX >= outbuf_sz)) return; if (pmsg->outbuf_len + len >= outbuf_sz) { /* Truncate the message, but keep it terminated. */ pmsg->outbuf_len = outbuf_sz - (len + 1); outbuf[pmsg->outbuf_len] = 0; } memmove(outbuf + len, outbuf, pmsg->outbuf_len + 1); memcpy(outbuf, scratchbuf, len); pmsg->outbuf_len += len; } #else #define console_prepend_dropped(pmsg, dropped) #endif /* CONFIG_PRINTK */ /* * Read and format the specified record (or a later record if the specified * record is not available). * * @pmsg will contain the formatted result. @pmsg->pbufs must point to a * struct printk_buffers. * * @seq is the record to read and format. If it is not available, the next * valid record is read. * * @is_extended specifies if the message should be formatted for extended * console output. * * @may_supress specifies if records may be skipped based on loglevel. * * Returns false if no record is available. Otherwise true and all fields * of @pmsg are valid. (See the documentation of struct printk_message * for information about the @pmsg fields.) */ static bool printk_get_next_message(struct printk_message *pmsg, u64 seq, bool is_extended, bool may_suppress) { static int panic_console_dropped; struct printk_buffers *pbufs = pmsg->pbufs; const size_t scratchbuf_sz = sizeof(pbufs->scratchbuf); const size_t outbuf_sz = sizeof(pbufs->outbuf); char *scratchbuf = &pbufs->scratchbuf[0]; char *outbuf = &pbufs->outbuf[0]; struct printk_info info; struct printk_record r; size_t len = 0; /* * Formatting extended messages requires a separate buffer, so use the * scratch buffer to read in the ringbuffer text. * * If dropped messages should be printed, @dropped_text is a buffer of size * DROPPED_TEXT_MAX. Otherwise @dropped_text must be NULL. * Formatting normal messages is done in-place, so read the ringbuffer * text directly into the output buffer. */ if (is_extended) prb_rec_init_rd(&r, &info, scratchbuf, scratchbuf_sz); else prb_rec_init_rd(&r, &info, outbuf, outbuf_sz); if (!prb_read_valid(prb, seq, &r)) return false; pmsg->seq = r.info->seq; pmsg->dropped = r.info->seq - seq; /* * Check for dropped messages in panic here so that printk * suppression can occur as early as possible if necessary. */ if (pmsg->dropped && panic_in_progress() && panic_console_dropped++ > 10) { suppress_panic_printk = 1; pr_warn_once("Too many dropped messages. Suppress messages on non-panic CPUs to prevent livelock.\n"); } /* Skip record that has level above the console loglevel. */ if (may_suppress && suppress_message_printing(r.info->level)) goto out; if (is_extended) { len = info_print_ext_header(outbuf, outbuf_sz, r.info); len += msg_print_ext_body(outbuf + len, outbuf_sz - len, &r.text_buf[0], r.info->text_len, &r.info->dev_info); } else { len = record_print_text(&r, console_msg_format & MSG_FORMAT_SYSLOG, printk_time); } out: pmsg->outbuf_len = len; return true; } /* * Print one record for the given console. The record printed is whatever * record is the next available record for the given console. * * @handover will be set to true if a printk waiter has taken over the * console_lock, in which case the caller is no longer holding both the Loading @@ -2764,46 +2835,33 @@ static void __console_unlock(void) * * Requires the console_lock and the SRCU read lock. */ static bool console_emit_next_record(struct console *con, char *text, char *ext_text, char *dropped_text, bool *handover, int cookie) static bool console_emit_next_record(struct console *con, bool *handover, int cookie) { static int panic_console_dropped; struct printk_info info; struct printk_record r; unsigned long flags; char *write_text; size_t len; static struct printk_buffers pbufs; prb_rec_init_rd(&r, &info, text, CONSOLE_LOG_MAX); bool is_extended = console_srcu_read_flags(con) & CON_EXTENDED; char *outbuf = &pbufs.outbuf[0]; struct printk_message pmsg = { .pbufs = &pbufs, }; unsigned long flags; *handover = false; if (!prb_read_valid(prb, con->seq, &r)) if (!printk_get_next_message(&pmsg, con->seq, is_extended, true)) return false; if (con->seq != r.info->seq) { con->dropped += r.info->seq - con->seq; con->seq = r.info->seq; if (panic_in_progress() && panic_console_dropped++ > 10) { suppress_panic_printk = 1; pr_warn_once("Too many dropped messages. Suppress messages on non-panic CPUs to prevent livelock.\n"); } } con->dropped += pmsg.dropped; /* Skip record that has level above the console loglevel. */ if (suppress_message_printing(r.info->level)) { con->seq++; /* Skip messages of formatted length 0. */ if (pmsg.outbuf_len == 0) { con->seq = pmsg.seq + 1; goto skip; } if (ext_text) { write_text = ext_text; len = info_print_ext_header(ext_text, CONSOLE_EXT_LOG_MAX, r.info); len += msg_print_ext_body(ext_text + len, CONSOLE_EXT_LOG_MAX - len, &r.text_buf[0], r.info->text_len, &r.info->dev_info); } else { write_text = text; len = record_print_text(&r, console_msg_format & MSG_FORMAT_SYSLOG, printk_time); if (con->dropped && !is_extended) { console_prepend_dropped(&pmsg, con->dropped); con->dropped = 0; } /* Loading @@ -2819,11 +2877,15 @@ static bool console_emit_next_record(struct console *con, char *text, char *ext_ printk_safe_enter_irqsave(flags); console_lock_spinning_enable(); stop_critical_timings(); /* don't trace print latency */ call_console_driver(con, write_text, len, dropped_text); /* Do not trace print latency. */ stop_critical_timings(); /* Write everything out to the hardware. */ con->write(con, outbuf, pmsg.outbuf_len); start_critical_timings(); con->seq++; con->seq = pmsg.seq + 1; *handover = console_lock_spinning_disable_and_check(cookie); printk_safe_exit_irqrestore(flags); Loading Loading @@ -2856,9 +2918,6 @@ static bool console_emit_next_record(struct console *con, char *text, char *ext_ */ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handover) { static char dropped_text[DROPPED_TEXT_MAX]; static char ext_text[CONSOLE_EXT_LOG_MAX]; static char text[CONSOLE_LOG_MAX]; bool any_usable = false; struct console *con; bool any_progress; Loading @@ -2878,16 +2937,7 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove continue; any_usable = true; if (console_srcu_read_flags(con) & CON_EXTENDED) { /* Extended consoles do not print "dropped messages". */ progress = console_emit_next_record(con, &text[0], &ext_text[0], NULL, handover, cookie); } else { progress = console_emit_next_record(con, &text[0], NULL, &dropped_text[0], handover, cookie); } progress = console_emit_next_record(con, handover, cookie); /* * If a handover has occurred, the SRCU read lock Loading