#define LOG_LINE_MAX 1024
/* record buffer */
+#if !defined(CONFIG_64BIT) || defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+#define LOG_ALIGN 4
+#else
+#define LOG_ALIGN 8
+#endif
#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
-static char __log_buf[__LOG_BUF_LEN];
+static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);
static char *log_buf = __log_buf;
static u32 log_buf_len = __LOG_BUF_LEN;
return idx + msg->len;
}
-#if !defined(CONFIG_64BIT) || defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
-#define LOG_ALIGN 4
-#else
-#define LOG_ALIGN 8
-#endif
-
/* insert record into the buffer, discard old ones, update heads */
static void log_store(int facility, int level,
const char *dict, u16 dict_len,
/* escape non-printable characters */
for (i = 0; i < msg->text_len; i++) {
- char c = log_text(msg)[i];
+ unsigned char c = log_text(msg)[i];
if (c < ' ' || c >= 128)
len += sprintf(user->buf + len, "\\x%02x", c);
bool line = true;
for (i = 0; i < msg->dict_len; i++) {
- char c = log_dict(msg)[i];
+ unsigned char c = log_dict(msg)[i];
if (line) {
user->buf[len++] = ' ';
#endif
module_param_named(time, printk_time, bool, S_IRUGO | S_IWUSR);
-static int syslog_print_line(u32 idx, char *text, size_t size)
+static size_t print_prefix(const struct log *msg, bool syslog, char *buf)
{
- struct log *msg;
- size_t len;
+ size_t len = 0;
- msg = log_from_idx(idx);
- if (!text) {
- /* calculate length only */
- len = 3;
+ if (syslog) {
+ if (buf) {
+ len += sprintf(buf, "<%u>", msg->level);
+ } else {
+ len += 3;
+ if (msg->level > 9)
+ len++;
+ if (msg->level > 99)
+ len++;
+ }
+ }
- if (msg->level > 9)
- len++;
- if (msg->level > 99)
- len++;
+ if (printk_time) {
+ if (buf) {
+ unsigned long long ts = msg->ts_nsec;
+ unsigned long rem_nsec = do_div(ts, 1000000000);
- if (printk_time)
+ len += sprintf(buf + len, "[%5lu.%06lu] ",
+ (unsigned long) ts, rem_nsec / 1000);
+ } else {
len += 15;
-
- len += msg->text_len;
- len++;
- return len;
+ }
}
- len = sprintf(text, "<%u>", msg->level);
+ return len;
+}
+
+static size_t msg_print_text(const struct log *msg, bool syslog,
+ char *buf, size_t size)
+{
+ const char *text = log_text(msg);
+ size_t text_size = msg->text_len;
+ size_t len = 0;
- if (printk_time) {
- unsigned long long t = msg->ts_nsec;
- unsigned long rem_ns = do_div(t, 1000000000);
+ do {
+ const char *next = memchr(text, '\n', text_size);
+ size_t text_len;
- len += sprintf(text + len, "[%5lu.%06lu] ",
- (unsigned long) t, rem_ns / 1000);
- }
+ if (next) {
+ text_len = next - text;
+ next++;
+ text_size -= next - text;
+ } else {
+ text_len = text_size;
+ }
+
+ if (buf) {
+ if (print_prefix(msg, syslog, NULL) +
+ text_len + 1>= size - len)
+ break;
+
+ len += print_prefix(msg, syslog, buf + len);
+ memcpy(buf + len, text, text_len);
+ len += text_len;
+ buf[len++] = '\n';
+ } else {
+ /* SYSLOG_ACTION_* buffer size only calculation */
+ len += print_prefix(msg, syslog, NULL);
+ len += text_len + 1;
+ }
+
+ text = next;
+ } while (text);
- if (len + msg->text_len > size)
- return -EINVAL;
- memcpy(text + len, log_text(msg), msg->text_len);
- len += msg->text_len;
- text[len++] = '\n';
return len;
}
static int syslog_print(char __user *buf, int size)
{
char *text;
+ struct log *msg;
int len;
text = kmalloc(LOG_LINE_MAX, GFP_KERNEL);
syslog_seq = log_first_seq;
syslog_idx = log_first_idx;
}
- len = syslog_print_line(syslog_idx, text, LOG_LINE_MAX);
+ msg = log_from_idx(syslog_idx);
+ len = msg_print_text(msg, true, text, LOG_LINE_MAX);
syslog_idx = log_next(syslog_idx);
syslog_seq++;
raw_spin_unlock_irq(&logbuf_lock);
seq = clear_seq;
idx = clear_idx;
while (seq < log_next_seq) {
- len += syslog_print_line(idx, NULL, 0);
+ struct log *msg = log_from_idx(idx);
+
+ len += msg_print_text(msg, true, NULL, 0);
idx = log_next(idx);
seq++;
}
seq = clear_seq;
idx = clear_idx;
while (len > size && seq < log_next_seq) {
- len -= syslog_print_line(idx, NULL, 0);
+ struct log *msg = log_from_idx(idx);
+
+ len -= msg_print_text(msg, true, NULL, 0);
idx = log_next(idx);
seq++;
}
len = 0;
while (len >= 0 && seq < next_seq) {
+ struct log *msg = log_from_idx(idx);
int textlen;
- textlen = syslog_print_line(idx, text, LOG_LINE_MAX);
+ textlen = msg_print_text(msg, true, text, LOG_LINE_MAX);
if (textlen < 0) {
len = textlen;
break;
seq = syslog_seq;
idx = syslog_idx;
while (seq < log_next_seq) {
- error += syslog_print_line(idx, NULL, 0);
+ struct log *msg = log_from_idx(idx);
+
+ error += msg_print_text(msg, true, NULL, 0);
idx = log_next(idx);
seq++;
}
const char *fmt, va_list args)
{
static int recursion_bug;
- static char buf[LOG_LINE_MAX];
- static size_t buflen;
- static int buflevel;
+ static char cont_buf[LOG_LINE_MAX];
+ static size_t cont_len;
+ static int cont_level;
+ static struct task_struct *cont_task;
static char textbuf[LOG_LINE_MAX];
char *text = textbuf;
- size_t textlen;
+ size_t text_len;
unsigned long flags;
int this_cpu;
bool newline = false;
- bool cont = false;
+ bool prefix = false;
int printed_len = 0;
boot_delay_msec();
* The printf needs to come first; we need the syslog
* prefix which might be passed-in as a parameter.
*/
- textlen = vscnprintf(text, sizeof(textbuf), fmt, args);
+ text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
/* mark and strip a trailing newline */
- if (textlen && text[textlen-1] == '\n') {
- textlen--;
+ if (text_len && text[text_len-1] == '\n') {
+ text_len--;
newline = true;
}
- /* strip syslog prefix and extract log level or flags */
+ /* strip syslog prefix and extract log level or control flags */
if (text[0] == '<' && text[1] && text[2] == '>') {
switch (text[1]) {
case '0' ... '7':
if (level == -1)
level = text[1] - '0';
- text += 3;
- textlen -= 3;
- break;
- case 'c': /* KERN_CONT */
- cont = true;
case 'd': /* KERN_DEFAULT */
+ prefix = true;
+ case 'c': /* KERN_CONT */
text += 3;
- textlen -= 3;
- break;
+ text_len -= 3;
}
}
- if (buflen && (!cont || dict)) {
- /* no continuation; flush existing buffer */
- log_store(facility, buflevel, NULL, 0, buf, buflen);
- printed_len += buflen;
- buflen = 0;
- }
+ if (level == -1)
+ level = default_message_loglevel;
- if (buflen == 0) {
- /* remember level for first message in the buffer */
- if (level == -1)
- buflevel = default_message_loglevel;
- else
- buflevel = level;
+ if (dict) {
+ prefix = true;
+ newline = true;
}
- if (buflen || !newline) {
- /* append to existing buffer, or buffer until next message */
- if (buflen + textlen > sizeof(buf))
- textlen = sizeof(buf) - buflen;
- memcpy(buf + buflen, text, textlen);
- buflen += textlen;
- }
+ if (!newline) {
+ if (cont_len && (prefix || cont_task != current)) {
+ /*
+ * Flush earlier buffer, which is either from a
+ * different thread, or when we got a new prefix.
+ */
+ log_store(facility, cont_level, NULL, 0, cont_buf, cont_len);
+ cont_len = 0;
+ }
+
+ if (!cont_len) {
+ cont_level = level;
+ cont_task = current;
+ }
- if (newline) {
- /* end of line; flush buffer */
- if (buflen) {
- log_store(facility, buflevel,
- dict, dictlen, buf, buflen);
- printed_len += buflen;
- buflen = 0;
+ /* buffer or append to earlier buffer from the same thread */
+ if (cont_len + text_len > sizeof(cont_buf))
+ text_len = sizeof(cont_buf) - cont_len;
+ memcpy(cont_buf + cont_len, text, text_len);
+ cont_len += text_len;
+ } else {
+ if (cont_len && cont_task == current) {
+ if (prefix) {
+ /*
+ * New prefix from the same thread; flush. We
+ * either got no earlier newline, or we race
+ * with an interrupt.
+ */
+ log_store(facility, cont_level,
+ NULL, 0, cont_buf, cont_len);
+ cont_len = 0;
+ }
+
+ /* append to the earlier buffer and flush */
+ if (cont_len + text_len > sizeof(cont_buf))
+ text_len = sizeof(cont_buf) - cont_len;
+ memcpy(cont_buf + cont_len, text, text_len);
+ cont_len += text_len;
+ log_store(facility, cont_level,
+ NULL, 0, cont_buf, cont_len);
+ cont_len = 0;
+ cont_task = NULL;
+ printed_len = cont_len;
} else {
- log_store(facility, buflevel,
- dict, dictlen, text, textlen);
- printed_len += textlen;
+ /* ordinary single and terminated line */
+ log_store(facility, level,
+ dict, dictlen, text, text_len);
+ printed_len = text_len;
}
}
#define LOG_LINE_MAX 0
static struct log *log_from_idx(u32 idx) { return NULL; }
static u32 log_next(u32 idx) { return 0; }
-static char *log_text(const struct log *msg) { return NULL; }
static void call_console_drivers(int level, const char *text, size_t len) {}
+static size_t msg_print_text(const struct log *msg, bool syslog,
+ char *buf, size_t size) { return 0; }
#endif /* CONFIG_PRINTK */
msg = log_from_idx(console_idx);
level = msg->level & 7;
- len = msg->text_len;
- if (len+1 >= sizeof(text))
- len = sizeof(text)-1;
- memcpy(text, log_text(msg), len);
- text[len++] = '\n';
+
+ len = msg_print_text(msg, false, text, sizeof(text));
console_idx = log_next(console_idx);
console_seq++;