Commit 533827c9 authored by Anton Vorontsov's avatar Anton Vorontsov Committed by Linus Torvalds

printk: Implement some unlocked kmsg_dump functions

If used from KDB, the locked variants are prone to deadlocks (suppose we
got to the debugger w/ the logbuf lock held).

So, we have to implement a few routines that grab no logbuf lock.

Yet we don't need these functions in modules, so we don't export them.
Signed-off-by: 's avatarAnton Vorontsov <anton.vorontsov@linaro.org>
Signed-off-by: 's avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 1b499d05
...@@ -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)
{ {
} }
......
...@@ -2510,7 +2510,7 @@ void kmsg_dump(enum kmsg_dump_reason reason) ...@@ -2510,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
...@@ -2525,11 +2525,12 @@ void kmsg_dump(enum kmsg_dump_reason reason) ...@@ -2525,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;
...@@ -2537,7 +2538,6 @@ bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog, ...@@ -2537,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;
...@@ -2545,10 +2545,8 @@ bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog, ...@@ -2545,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);
...@@ -2556,12 +2554,41 @@ bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog, ...@@ -2556,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);
/** /**
...@@ -2663,6 +2690,24 @@ out: ...@@ -2663,6 +2690,24 @@ out:
} }
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
...@@ -2676,10 +2721,7 @@ void kmsg_dump_rewind(struct kmsg_dumper *dumper) ...@@ -2676,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