Commit 9a2bc860 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'anton-kgdb' (kgdb dmesg fixups)

Merge emailed kgdb dmesg fixups patches from Anton Vorontsov:
 "The dmesg command appears to be broken after the printk rework.  The
  old logic in the kdb code makes no sense in terms of current
  printk/logging storage format, and KDB simply hangs forever upon
  entering 'dmesg' command.

  The first patch revives the command by switching to kmsg_dumper
  iterator.  As a side-effect, the code is now much more simpler.

  A few changes were needed in the printk.c: we needed unlocked variant
  of the kmsg_dumper iterator, but these can surely wait for 3.6.

  It's probably too late even for the first patch to go to 3.5, but I'll
  try to convince otherwise.  :-) Here we go:

   - The current code is broken for sure, and has no hope to work at
     all.  It is a regression
   - The new code works for me, and probably works for everyone else;
   - If it compiles (and I urge everyone to compile-test it on your
     setup), it hardly can make things worse."

* Merge emailed patches from Anton Vorontsov: (4 commits)
  kdb: Switch to nolock variants of kmsg_dump functions
  printk: Implement some unlocked kmsg_dump functions
  printk: Remove kdb_syslog_data
  kdb: Revive dmesg command
parents d75e2c9a c064da47
...@@ -55,12 +55,17 @@ struct kmsg_dumper { ...@@ -55,12 +55,17 @@ struct kmsg_dumper {
#ifdef CONFIG_PRINTK #ifdef CONFIG_PRINTK
void kmsg_dump(enum kmsg_dump_reason reason); void kmsg_dump(enum kmsg_dump_reason reason);
bool kmsg_dump_get_line_nolock(struct kmsg_dumper *dumper, bool syslog,
char *line, size_t size, size_t *len);
bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog, bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
char *line, size_t size, size_t *len); char *line, size_t size, size_t *len);
bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog, bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
char *buf, size_t size, size_t *len); char *buf, size_t size, size_t *len);
void kmsg_dump_rewind_nolock(struct kmsg_dumper *dumper);
void kmsg_dump_rewind(struct kmsg_dumper *dumper); void kmsg_dump_rewind(struct kmsg_dumper *dumper);
int kmsg_dump_register(struct kmsg_dumper *dumper); int kmsg_dump_register(struct kmsg_dumper *dumper);
...@@ -71,6 +76,13 @@ static inline void kmsg_dump(enum kmsg_dump_reason reason) ...@@ -71,6 +76,13 @@ static inline void kmsg_dump(enum kmsg_dump_reason reason)
{ {
} }
static inline bool kmsg_dump_get_line_nolock(struct kmsg_dumper *dumper,
bool syslog, const char *line,
size_t size, size_t *len)
{
return false;
}
static inline bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog, static inline bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
const char *line, size_t size, size_t *len) const char *line, size_t size, size_t *len)
{ {
...@@ -83,6 +95,10 @@ static inline bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog, ...@@ -83,6 +95,10 @@ static inline bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
return false; return false;
} }
static inline void kmsg_dump_rewind_nolock(struct kmsg_dumper *dumper)
{
}
static inline void kmsg_dump_rewind(struct kmsg_dumper *dumper) static inline void kmsg_dump_rewind(struct kmsg_dumper *dumper)
{ {
} }
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/kmsg_dump.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/sysrq.h> #include <linux/sysrq.h>
...@@ -2040,8 +2041,15 @@ static int kdb_env(int argc, const char **argv) ...@@ -2040,8 +2041,15 @@ static int kdb_env(int argc, const char **argv)
*/ */
static int kdb_dmesg(int argc, const char **argv) static int kdb_dmesg(int argc, const char **argv)
{ {
char *syslog_data[4], *start, *end, c = '\0', *p; int diag;
int diag, logging, logsize, lines = 0, adjust = 0, n; int logging;
int lines = 0;
int adjust = 0;
int n = 0;
int skip = 0;
struct kmsg_dumper dumper = { .active = 1 };
size_t len;
char buf[201];
if (argc > 2) if (argc > 2)
return KDB_ARGCOUNT; return KDB_ARGCOUNT;
...@@ -2064,22 +2072,10 @@ static int kdb_dmesg(int argc, const char **argv) ...@@ -2064,22 +2072,10 @@ static int kdb_dmesg(int argc, const char **argv)
kdb_set(2, setargs); kdb_set(2, setargs);
} }
/* syslog_data[0,1] physical start, end+1. syslog_data[2,3] kmsg_dump_rewind_nolock(&dumper);
* logical start, end+1. */ while (kmsg_dump_get_line_nolock(&dumper, 1, NULL, 0, NULL))
kdb_syslog_data(syslog_data); n++;
if (syslog_data[2] == syslog_data[3])
return 0;
logsize = syslog_data[1] - syslog_data[0];
start = syslog_data[2];
end = syslog_data[3];
#define KDB_WRAP(p) (((p - syslog_data[0]) % logsize) + syslog_data[0])
for (n = 0, p = start; p < end; ++p) {
c = *KDB_WRAP(p);
if (c == '\n')
++n;
}
if (c != '\n')
++n;
if (lines < 0) { if (lines < 0) {
if (adjust >= n) if (adjust >= n)
kdb_printf("buffer only contains %d lines, nothing " kdb_printf("buffer only contains %d lines, nothing "
...@@ -2087,21 +2083,11 @@ static int kdb_dmesg(int argc, const char **argv) ...@@ -2087,21 +2083,11 @@ static int kdb_dmesg(int argc, const char **argv)
else if (adjust - lines >= n) else if (adjust - lines >= n)
kdb_printf("buffer only contains %d lines, last %d " kdb_printf("buffer only contains %d lines, last %d "
"lines printed\n", n, n - adjust); "lines printed\n", n, n - adjust);
if (adjust) { skip = adjust;
for (; start < end && adjust; ++start) { lines = abs(lines);
if (*KDB_WRAP(start) == '\n')
--adjust;
}
if (start < end)
++start;
}
for (p = start; p < end && lines; ++p) {
if (*KDB_WRAP(p) == '\n')
++lines;
}
end = p;
} else if (lines > 0) { } else if (lines > 0) {
int skip = n - (adjust + lines); skip = n - lines - adjust;
lines = abs(lines);
if (adjust >= n) { if (adjust >= n) {
kdb_printf("buffer only contains %d lines, " kdb_printf("buffer only contains %d lines, "
"nothing printed\n", n); "nothing printed\n", n);
...@@ -2112,35 +2098,24 @@ static int kdb_dmesg(int argc, const char **argv) ...@@ -2112,35 +2098,24 @@ static int kdb_dmesg(int argc, const char **argv)
kdb_printf("buffer only contains %d lines, first " kdb_printf("buffer only contains %d lines, first "
"%d lines printed\n", n, lines); "%d lines printed\n", n, lines);
} }
for (; start < end && skip; ++start) { } else {
if (*KDB_WRAP(start) == '\n') lines = n;
--skip;
}
for (p = start; p < end && lines; ++p) {
if (*KDB_WRAP(p) == '\n')
--lines;
}
end = p;
} }
/* Do a line at a time (max 200 chars) to reduce protocol overhead */
c = '\n'; if (skip >= n || skip < 0)
while (start != end) {
char buf[201];
p = buf;
if (KDB_FLAG(CMD_INTERRUPT))
return 0; return 0;
while (start < end && (c = *KDB_WRAP(start)) &&
(p - buf) < sizeof(buf)-1) { kmsg_dump_rewind_nolock(&dumper);
++start; while (kmsg_dump_get_line_nolock(&dumper, 1, buf, sizeof(buf), &len)) {
*p++ = c; if (skip) {
if (c == '\n') skip--;
break; continue;
} }
*p = '\0'; if (!lines--)
kdb_printf("%s", buf); break;
kdb_printf("%.*s\n", (int)len - 1, buf);
} }
if (c != '\n')
kdb_printf("\n");
return 0; return 0;
} }
......
...@@ -205,7 +205,6 @@ extern char kdb_grep_string[]; ...@@ -205,7 +205,6 @@ extern char kdb_grep_string[];
extern int kdb_grep_leading; extern int kdb_grep_leading;
extern int kdb_grep_trailing; extern int kdb_grep_trailing;
extern char *kdb_cmds[]; extern char *kdb_cmds[];
extern void kdb_syslog_data(char *syslog_data[]);
extern unsigned long kdb_task_state_string(const char *); extern unsigned long kdb_task_state_string(const char *);
extern char kdb_task_state_char (const struct task_struct *); extern char kdb_task_state_char (const struct task_struct *);
extern unsigned long kdb_task_state(const struct task_struct *p, extern unsigned long kdb_task_state(const struct task_struct *p,
......
...@@ -1192,21 +1192,6 @@ SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len) ...@@ -1192,21 +1192,6 @@ SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
return do_syslog(type, buf, len, SYSLOG_FROM_CALL); return do_syslog(type, buf, len, SYSLOG_FROM_CALL);
} }
#ifdef CONFIG_KGDB_KDB
/* kdb dmesg command needs access to the syslog buffer. do_syslog()
* uses locks so it cannot be used during debugging. Just tell kdb
* where the start and end of the physical and logical logs are. This
* is equivalent to do_syslog(3).
*/
void kdb_syslog_data(char *syslog_data[4])
{
syslog_data[0] = log_buf;
syslog_data[1] = log_buf + log_buf_len;
syslog_data[2] = log_buf + log_first_idx;
syslog_data[3] = log_buf + log_next_idx;
}
#endif /* CONFIG_KGDB_KDB */
static bool __read_mostly ignore_loglevel; static bool __read_mostly ignore_loglevel;
static int __init ignore_loglevel_setup(char *str) static int __init ignore_loglevel_setup(char *str)
...@@ -2525,7 +2510,7 @@ void kmsg_dump(enum kmsg_dump_reason reason) ...@@ -2525,7 +2510,7 @@ void kmsg_dump(enum kmsg_dump_reason reason)
} }
/** /**
* kmsg_dump_get_line - retrieve one kmsg log line * kmsg_dump_get_line_nolock - retrieve one kmsg log line (unlocked version)
* @dumper: registered kmsg dumper * @dumper: registered kmsg dumper
* @syslog: include the "<4>" prefixes * @syslog: include the "<4>" prefixes
* @line: buffer to copy the line to * @line: buffer to copy the line to
...@@ -2540,11 +2525,12 @@ void kmsg_dump(enum kmsg_dump_reason reason) ...@@ -2540,11 +2525,12 @@ void kmsg_dump(enum kmsg_dump_reason reason)
* *
* A return value of FALSE indicates that there are no more records to * A return value of FALSE indicates that there are no more records to
* read. * read.
*
* The function is similar to kmsg_dump_get_line(), but grabs no locks.
*/ */
bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog, bool kmsg_dump_get_line_nolock(struct kmsg_dumper *dumper, bool syslog,
char *line, size_t size, size_t *len) char *line, size_t size, size_t *len)
{ {
unsigned long flags;
struct log *msg; struct log *msg;
size_t l = 0; size_t l = 0;
bool ret = false; bool ret = false;
...@@ -2552,7 +2538,6 @@ bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog, ...@@ -2552,7 +2538,6 @@ bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
if (!dumper->active) if (!dumper->active)
goto out; goto out;
raw_spin_lock_irqsave(&logbuf_lock, flags);
if (dumper->cur_seq < log_first_seq) { if (dumper->cur_seq < log_first_seq) {
/* messages are gone, move to first available one */ /* messages are gone, move to first available one */
dumper->cur_seq = log_first_seq; dumper->cur_seq = log_first_seq;
...@@ -2560,10 +2545,8 @@ bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog, ...@@ -2560,10 +2545,8 @@ bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
} }
/* last entry */ /* last entry */
if (dumper->cur_seq >= log_next_seq) { if (dumper->cur_seq >= log_next_seq)
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
goto out; goto out;
}
msg = log_from_idx(dumper->cur_idx); msg = log_from_idx(dumper->cur_idx);
l = msg_print_text(msg, 0, syslog, line, size); l = msg_print_text(msg, 0, syslog, line, size);
...@@ -2571,12 +2554,41 @@ bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog, ...@@ -2571,12 +2554,41 @@ bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
dumper->cur_idx = log_next(dumper->cur_idx); dumper->cur_idx = log_next(dumper->cur_idx);
dumper->cur_seq++; dumper->cur_seq++;
ret = true; ret = true;
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
out: out:
if (len) if (len)
*len = l; *len = l;
return ret; return ret;
} }
/**
* kmsg_dump_get_line - retrieve one kmsg log line
* @dumper: registered kmsg dumper
* @syslog: include the "<4>" prefixes
* @line: buffer to copy the line to
* @size: maximum size of the buffer
* @len: length of line placed into buffer
*
* Start at the beginning of the kmsg buffer, with the oldest kmsg
* record, and copy one record into the provided buffer.
*
* Consecutive calls will return the next available record moving
* towards the end of the buffer with the youngest messages.
*
* A return value of FALSE indicates that there are no more records to
* read.
*/
bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
char *line, size_t size, size_t *len)
{
unsigned long flags;
bool ret;
raw_spin_lock_irqsave(&logbuf_lock, flags);
ret = kmsg_dump_get_line_nolock(dumper, syslog, line, size, len);
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(kmsg_dump_get_line); EXPORT_SYMBOL_GPL(kmsg_dump_get_line);
/** /**
...@@ -2678,6 +2690,24 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog, ...@@ -2678,6 +2690,24 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
} }
EXPORT_SYMBOL_GPL(kmsg_dump_get_buffer); EXPORT_SYMBOL_GPL(kmsg_dump_get_buffer);
/**
* kmsg_dump_rewind_nolock - reset the interator (unlocked version)
* @dumper: registered kmsg dumper
*
* Reset the dumper's iterator so that kmsg_dump_get_line() and
* kmsg_dump_get_buffer() can be called again and used multiple
* times within the same dumper.dump() callback.
*
* The function is similar to kmsg_dump_rewind(), but grabs no locks.
*/
void kmsg_dump_rewind_nolock(struct kmsg_dumper *dumper)
{
dumper->cur_seq = clear_seq;
dumper->cur_idx = clear_idx;
dumper->next_seq = log_next_seq;
dumper->next_idx = log_next_idx;
}
/** /**
* kmsg_dump_rewind - reset the interator * kmsg_dump_rewind - reset the interator
* @dumper: registered kmsg dumper * @dumper: registered kmsg dumper
...@@ -2691,10 +2721,7 @@ void kmsg_dump_rewind(struct kmsg_dumper *dumper) ...@@ -2691,10 +2721,7 @@ void kmsg_dump_rewind(struct kmsg_dumper *dumper)
unsigned long flags; unsigned long flags;
raw_spin_lock_irqsave(&logbuf_lock, flags); raw_spin_lock_irqsave(&logbuf_lock, flags);
dumper->cur_seq = clear_seq; kmsg_dump_rewind_nolock(dumper);
dumper->cur_idx = clear_idx;
dumper->next_seq = log_next_seq;
dumper->next_idx = log_next_idx;
raw_spin_unlock_irqrestore(&logbuf_lock, flags); raw_spin_unlock_irqrestore(&logbuf_lock, flags);
} }
EXPORT_SYMBOL_GPL(kmsg_dump_rewind); EXPORT_SYMBOL_GPL(kmsg_dump_rewind);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment