|
@@ -84,6 +84,18 @@ static struct lockdep_map console_lock_dep_map = {
|
|
};
|
|
};
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Number of registered extended console drivers.
|
|
|
|
+ *
|
|
|
|
+ * If extended consoles are present, in-kernel cont reassembly is disabled
|
|
|
|
+ * and each fragment is stored as a separate log entry with proper
|
|
|
|
+ * continuation flag so that every emitted message has full metadata. This
|
|
|
|
+ * doesn't change the result for regular consoles or /proc/kmsg. For
|
|
|
|
+ * /dev/kmsg, as long as the reader concatenates messages according to
|
|
|
|
+ * consecutive continuation flags, the end result should be the same too.
|
|
|
|
+ */
|
|
|
|
+static int nr_ext_console_drivers;
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Helper macros to handle lockdep when locking/unlocking console_sem. We use
|
|
* Helper macros to handle lockdep when locking/unlocking console_sem. We use
|
|
* macros instead of functions so that _RET_IP_ contains useful information.
|
|
* macros instead of functions so that _RET_IP_ contains useful information.
|
|
@@ -195,7 +207,7 @@ static int console_may_schedule;
|
|
* need to be changed in the future, when the requirements change.
|
|
* need to be changed in the future, when the requirements change.
|
|
*
|
|
*
|
|
* /dev/kmsg exports the structured data in the following line format:
|
|
* /dev/kmsg exports the structured data in the following line format:
|
|
- * "level,sequnum,timestamp;<message text>\n"
|
|
|
|
|
|
+ * "<level>,<sequnum>,<timestamp>,<contflag>;<message text>\n"
|
|
*
|
|
*
|
|
* The optional key/value pairs are attached as continuation lines starting
|
|
* The optional key/value pairs are attached as continuation lines starting
|
|
* with a space character and terminated by a newline. All possible
|
|
* with a space character and terminated by a newline. All possible
|
|
@@ -1417,7 +1429,9 @@ SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
|
|
* log_buf[start] to log_buf[end - 1].
|
|
* log_buf[start] to log_buf[end - 1].
|
|
* The console_lock must be held.
|
|
* The console_lock must be held.
|
|
*/
|
|
*/
|
|
-static void call_console_drivers(int level, const char *text, size_t len)
|
|
|
|
|
|
+static void call_console_drivers(int level,
|
|
|
|
+ const char *ext_text, size_t ext_len,
|
|
|
|
+ const char *text, size_t len)
|
|
{
|
|
{
|
|
struct console *con;
|
|
struct console *con;
|
|
|
|
|
|
@@ -1438,7 +1452,10 @@ static void call_console_drivers(int level, const char *text, size_t len)
|
|
if (!cpu_online(smp_processor_id()) &&
|
|
if (!cpu_online(smp_processor_id()) &&
|
|
!(con->flags & CON_ANYTIME))
|
|
!(con->flags & CON_ANYTIME))
|
|
continue;
|
|
continue;
|
|
- con->write(con, text, len);
|
|
|
|
|
|
+ if (con->flags & CON_EXTENDED)
|
|
|
|
+ con->write(con, ext_text, ext_len);
|
|
|
|
+ else
|
|
|
|
+ con->write(con, text, len);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1581,8 +1598,12 @@ static bool cont_add(int facility, int level, const char *text, size_t len)
|
|
if (cont.len && cont.flushed)
|
|
if (cont.len && cont.flushed)
|
|
return false;
|
|
return false;
|
|
|
|
|
|
- if (cont.len + len > sizeof(cont.buf)) {
|
|
|
|
- /* the line gets too long, split it up in separate records */
|
|
|
|
|
|
+ /*
|
|
|
|
+ * If ext consoles are present, flush and skip in-kernel
|
|
|
|
+ * continuation. See nr_ext_console_drivers definition. Also, if
|
|
|
|
+ * the line gets too long, split it up in separate records.
|
|
|
|
+ */
|
|
|
|
+ if (nr_ext_console_drivers || cont.len + len > sizeof(cont.buf)) {
|
|
cont_flush(LOG_CONT);
|
|
cont_flush(LOG_CONT);
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
@@ -1917,9 +1938,19 @@ static struct cont {
|
|
u8 level;
|
|
u8 level;
|
|
bool flushed:1;
|
|
bool flushed:1;
|
|
} cont;
|
|
} cont;
|
|
|
|
+static char *log_text(const struct printk_log *msg) { return NULL; }
|
|
|
|
+static char *log_dict(const struct printk_log *msg) { return NULL; }
|
|
static struct printk_log *log_from_idx(u32 idx) { return NULL; }
|
|
static struct printk_log *log_from_idx(u32 idx) { return NULL; }
|
|
static u32 log_next(u32 idx) { return 0; }
|
|
static u32 log_next(u32 idx) { return 0; }
|
|
-static void call_console_drivers(int level, const char *text, size_t len) {}
|
|
|
|
|
|
+static ssize_t msg_print_ext_header(char *buf, size_t size,
|
|
|
|
+ struct printk_log *msg, u64 seq,
|
|
|
|
+ enum log_flags prev_flags) { return 0; }
|
|
|
|
+static ssize_t msg_print_ext_body(char *buf, size_t size,
|
|
|
|
+ char *dict, size_t dict_len,
|
|
|
|
+ char *text, size_t text_len) { return 0; }
|
|
|
|
+static void call_console_drivers(int level,
|
|
|
|
+ const char *ext_text, size_t ext_len,
|
|
|
|
+ const char *text, size_t len) {}
|
|
static size_t msg_print_text(const struct printk_log *msg, enum log_flags prev,
|
|
static size_t msg_print_text(const struct printk_log *msg, enum log_flags prev,
|
|
bool syslog, char *buf, size_t size) { return 0; }
|
|
bool syslog, char *buf, size_t size) { return 0; }
|
|
static size_t cont_print_text(char *text, size_t size) { return 0; }
|
|
static size_t cont_print_text(char *text, size_t size) { return 0; }
|
|
@@ -2172,7 +2203,7 @@ static void console_cont_flush(char *text, size_t size)
|
|
len = cont_print_text(text, size);
|
|
len = cont_print_text(text, size);
|
|
raw_spin_unlock(&logbuf_lock);
|
|
raw_spin_unlock(&logbuf_lock);
|
|
stop_critical_timings();
|
|
stop_critical_timings();
|
|
- call_console_drivers(cont.level, text, len);
|
|
|
|
|
|
+ call_console_drivers(cont.level, NULL, 0, text, len);
|
|
start_critical_timings();
|
|
start_critical_timings();
|
|
local_irq_restore(flags);
|
|
local_irq_restore(flags);
|
|
return;
|
|
return;
|
|
@@ -2196,6 +2227,7 @@ out:
|
|
*/
|
|
*/
|
|
void console_unlock(void)
|
|
void console_unlock(void)
|
|
{
|
|
{
|
|
|
|
+ static char ext_text[CONSOLE_EXT_LOG_MAX];
|
|
static char text[LOG_LINE_MAX + PREFIX_MAX];
|
|
static char text[LOG_LINE_MAX + PREFIX_MAX];
|
|
static u64 seen_seq;
|
|
static u64 seen_seq;
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
@@ -2214,6 +2246,7 @@ void console_unlock(void)
|
|
again:
|
|
again:
|
|
for (;;) {
|
|
for (;;) {
|
|
struct printk_log *msg;
|
|
struct printk_log *msg;
|
|
|
|
+ size_t ext_len = 0;
|
|
size_t len;
|
|
size_t len;
|
|
int level;
|
|
int level;
|
|
|
|
|
|
@@ -2259,13 +2292,22 @@ skip:
|
|
level = msg->level;
|
|
level = msg->level;
|
|
len += msg_print_text(msg, console_prev, false,
|
|
len += msg_print_text(msg, console_prev, false,
|
|
text + len, sizeof(text) - len);
|
|
text + len, sizeof(text) - len);
|
|
|
|
+ if (nr_ext_console_drivers) {
|
|
|
|
+ ext_len = msg_print_ext_header(ext_text,
|
|
|
|
+ sizeof(ext_text),
|
|
|
|
+ msg, console_seq, console_prev);
|
|
|
|
+ ext_len += msg_print_ext_body(ext_text + ext_len,
|
|
|
|
+ sizeof(ext_text) - ext_len,
|
|
|
|
+ log_dict(msg), msg->dict_len,
|
|
|
|
+ log_text(msg), msg->text_len);
|
|
|
|
+ }
|
|
console_idx = log_next(console_idx);
|
|
console_idx = log_next(console_idx);
|
|
console_seq++;
|
|
console_seq++;
|
|
console_prev = msg->flags;
|
|
console_prev = msg->flags;
|
|
raw_spin_unlock(&logbuf_lock);
|
|
raw_spin_unlock(&logbuf_lock);
|
|
|
|
|
|
stop_critical_timings(); /* don't trace print latency */
|
|
stop_critical_timings(); /* don't trace print latency */
|
|
- call_console_drivers(level, text, len);
|
|
|
|
|
|
+ call_console_drivers(level, ext_text, ext_len, text, len);
|
|
start_critical_timings();
|
|
start_critical_timings();
|
|
local_irq_restore(flags);
|
|
local_irq_restore(flags);
|
|
}
|
|
}
|
|
@@ -2521,6 +2563,11 @@ void register_console(struct console *newcon)
|
|
newcon->next = console_drivers->next;
|
|
newcon->next = console_drivers->next;
|
|
console_drivers->next = newcon;
|
|
console_drivers->next = newcon;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if (newcon->flags & CON_EXTENDED)
|
|
|
|
+ if (!nr_ext_console_drivers++)
|
|
|
|
+ pr_info("printk: continuation disabled due to ext consoles, expect more fragments in /dev/kmsg\n");
|
|
|
|
+
|
|
if (newcon->flags & CON_PRINTBUFFER) {
|
|
if (newcon->flags & CON_PRINTBUFFER) {
|
|
/*
|
|
/*
|
|
* console_unlock(); will print out the buffered messages
|
|
* console_unlock(); will print out the buffered messages
|
|
@@ -2593,6 +2640,9 @@ int unregister_console(struct console *console)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (!res && (console->flags & CON_EXTENDED))
|
|
|
|
+ nr_ext_console_drivers--;
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* If this isn't the last console and it has CON_CONSDEV set, we
|
|
* If this isn't the last console and it has CON_CONSDEV set, we
|
|
* need to set it on the next preferred console.
|
|
* need to set it on the next preferred console.
|