mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-30 08:04:13 +08:00
kmsg - kmsg_dump() use iterator to receive log buffer content
Provide an iterator to receive the log buffer content, and convert all kmsg_dump() users to it. The structured data in the kmsg buffer now contains binary data, which should no longer be copied verbatim to the kmsg_dump() users. The iterator should provide reliable access to the buffer data, and also supports proper log line-aware chunking of data while iterating. Signed-off-by: Kay Sievers <kay@vrfy.org> Tested-by: Tony Luck <tony.luck@intel.com> Reported-by: Anton Vorontsov <anton.vorontsov@linaro.org> Tested-by: Anton Vorontsov <anton.vorontsov@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
1bd289d1e8
commit
e2ae715d66
@ -68,9 +68,7 @@ static const char *pseries_nvram_os_partitions[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void oops_to_nvram(struct kmsg_dumper *dumper,
|
static void oops_to_nvram(struct kmsg_dumper *dumper,
|
||||||
enum kmsg_dump_reason reason,
|
enum kmsg_dump_reason reason);
|
||||||
const char *old_msgs, unsigned long old_len,
|
|
||||||
const char *new_msgs, unsigned long new_len);
|
|
||||||
|
|
||||||
static struct kmsg_dumper nvram_kmsg_dumper = {
|
static struct kmsg_dumper nvram_kmsg_dumper = {
|
||||||
.dump = oops_to_nvram
|
.dump = oops_to_nvram
|
||||||
@ -503,28 +501,6 @@ int __init pSeries_nvram_init(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Try to capture the last capture_len bytes of the printk buffer. Return
|
|
||||||
* the amount actually captured.
|
|
||||||
*/
|
|
||||||
static size_t capture_last_msgs(const char *old_msgs, size_t old_len,
|
|
||||||
const char *new_msgs, size_t new_len,
|
|
||||||
char *captured, size_t capture_len)
|
|
||||||
{
|
|
||||||
if (new_len >= capture_len) {
|
|
||||||
memcpy(captured, new_msgs + (new_len - capture_len),
|
|
||||||
capture_len);
|
|
||||||
return capture_len;
|
|
||||||
} else {
|
|
||||||
/* Grab the end of old_msgs. */
|
|
||||||
size_t old_tail_len = min(old_len, capture_len - new_len);
|
|
||||||
memcpy(captured, old_msgs + (old_len - old_tail_len),
|
|
||||||
old_tail_len);
|
|
||||||
memcpy(captured + old_tail_len, new_msgs, new_len);
|
|
||||||
return old_tail_len + new_len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Are we using the ibm,rtas-log for oops/panic reports? And if so,
|
* Are we using the ibm,rtas-log for oops/panic reports? And if so,
|
||||||
* would logging this oops/panic overwrite an RTAS event that rtas_errd
|
* would logging this oops/panic overwrite an RTAS event that rtas_errd
|
||||||
@ -541,27 +517,6 @@ static int clobbering_unread_rtas_event(void)
|
|||||||
NVRAM_RTAS_READ_TIMEOUT);
|
NVRAM_RTAS_READ_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Squeeze out each line's <n> severity prefix. */
|
|
||||||
static size_t elide_severities(char *buf, size_t len)
|
|
||||||
{
|
|
||||||
char *in, *out, *buf_end = buf + len;
|
|
||||||
/* Assume a <n> at the very beginning marks the start of a line. */
|
|
||||||
int newline = 1;
|
|
||||||
|
|
||||||
in = out = buf;
|
|
||||||
while (in < buf_end) {
|
|
||||||
if (newline && in+3 <= buf_end &&
|
|
||||||
*in == '<' && isdigit(in[1]) && in[2] == '>') {
|
|
||||||
in += 3;
|
|
||||||
newline = 0;
|
|
||||||
} else {
|
|
||||||
newline = (*in == '\n');
|
|
||||||
*out++ = *in++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out - buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Derived from logfs_compress() */
|
/* Derived from logfs_compress() */
|
||||||
static int nvram_compress(const void *in, void *out, size_t inlen,
|
static int nvram_compress(const void *in, void *out, size_t inlen,
|
||||||
size_t outlen)
|
size_t outlen)
|
||||||
@ -619,9 +574,7 @@ static int zip_oops(size_t text_len)
|
|||||||
* partition. If that's too much, go back and capture uncompressed text.
|
* partition. If that's too much, go back and capture uncompressed text.
|
||||||
*/
|
*/
|
||||||
static void oops_to_nvram(struct kmsg_dumper *dumper,
|
static void oops_to_nvram(struct kmsg_dumper *dumper,
|
||||||
enum kmsg_dump_reason reason,
|
enum kmsg_dump_reason reason)
|
||||||
const char *old_msgs, unsigned long old_len,
|
|
||||||
const char *new_msgs, unsigned long new_len)
|
|
||||||
{
|
{
|
||||||
static unsigned int oops_count = 0;
|
static unsigned int oops_count = 0;
|
||||||
static bool panicking = false;
|
static bool panicking = false;
|
||||||
@ -660,14 +613,14 @@ static void oops_to_nvram(struct kmsg_dumper *dumper,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (big_oops_buf) {
|
if (big_oops_buf) {
|
||||||
text_len = capture_last_msgs(old_msgs, old_len,
|
kmsg_dump_get_buffer(dumper, false,
|
||||||
new_msgs, new_len, big_oops_buf, big_oops_buf_sz);
|
big_oops_buf, big_oops_buf_sz, &text_len);
|
||||||
text_len = elide_severities(big_oops_buf, text_len);
|
|
||||||
rc = zip_oops(text_len);
|
rc = zip_oops(text_len);
|
||||||
}
|
}
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
text_len = capture_last_msgs(old_msgs, old_len,
|
kmsg_dump_rewind(dumper);
|
||||||
new_msgs, new_len, oops_data, oops_data_sz);
|
kmsg_dump_get_buffer(dumper, true,
|
||||||
|
oops_data, oops_data_sz, &text_len);
|
||||||
err_type = ERR_TYPE_KERNEL_PANIC;
|
err_type = ERR_TYPE_KERNEL_PANIC;
|
||||||
*oops_len = (u16) text_len;
|
*oops_len = (u16) text_len;
|
||||||
}
|
}
|
||||||
|
@ -110,19 +110,16 @@ static struct kmsg_dumper dw_dumper;
|
|||||||
static int dumper_registered;
|
static int dumper_registered;
|
||||||
|
|
||||||
static void dw_kmsg_dump(struct kmsg_dumper *dumper,
|
static void dw_kmsg_dump(struct kmsg_dumper *dumper,
|
||||||
enum kmsg_dump_reason reason,
|
enum kmsg_dump_reason reason)
|
||||||
const char *s1, unsigned long l1,
|
|
||||||
const char *s2, unsigned long l2)
|
|
||||||
{
|
{
|
||||||
int i;
|
static char line[1024];
|
||||||
|
size_t len;
|
||||||
|
|
||||||
/* When run to this, we'd better re-init the HW */
|
/* When run to this, we'd better re-init the HW */
|
||||||
mrst_early_console_init();
|
mrst_early_console_init();
|
||||||
|
|
||||||
for (i = 0; i < l1; i++)
|
while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len))
|
||||||
early_mrst_console.write(&early_mrst_console, s1 + i, 1);
|
early_mrst_console.write(&early_mrst_console, line, len);
|
||||||
for (i = 0; i < l2; i++)
|
|
||||||
early_mrst_console.write(&early_mrst_console, s2 + i, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the ratio rate to 115200, 8n1, IRQ disabled */
|
/* Set the ratio rate to 115200, 8n1, IRQ disabled */
|
||||||
|
@ -304,32 +304,17 @@ static void find_next_position(struct mtdoops_context *cxt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void mtdoops_do_dump(struct kmsg_dumper *dumper,
|
static void mtdoops_do_dump(struct kmsg_dumper *dumper,
|
||||||
enum kmsg_dump_reason reason, const char *s1, unsigned long l1,
|
enum kmsg_dump_reason reason)
|
||||||
const char *s2, unsigned long l2)
|
|
||||||
{
|
{
|
||||||
struct mtdoops_context *cxt = container_of(dumper,
|
struct mtdoops_context *cxt = container_of(dumper,
|
||||||
struct mtdoops_context, dump);
|
struct mtdoops_context, dump);
|
||||||
unsigned long s1_start, s2_start;
|
|
||||||
unsigned long l1_cpy, l2_cpy;
|
|
||||||
char *dst;
|
|
||||||
|
|
||||||
if (reason != KMSG_DUMP_OOPS &&
|
|
||||||
reason != KMSG_DUMP_PANIC)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Only dump oopses if dump_oops is set */
|
/* Only dump oopses if dump_oops is set */
|
||||||
if (reason == KMSG_DUMP_OOPS && !dump_oops)
|
if (reason == KMSG_DUMP_OOPS && !dump_oops)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dst = cxt->oops_buf + MTDOOPS_HEADER_SIZE; /* Skip the header */
|
kmsg_dump_get_buffer(dumper, true, cxt->oops_buf + MTDOOPS_HEADER_SIZE,
|
||||||
l2_cpy = min(l2, record_size - MTDOOPS_HEADER_SIZE);
|
record_size - MTDOOPS_HEADER_SIZE, NULL);
|
||||||
l1_cpy = min(l1, record_size - MTDOOPS_HEADER_SIZE - l2_cpy);
|
|
||||||
|
|
||||||
s2_start = l2 - l2_cpy;
|
|
||||||
s1_start = l1 - l1_cpy;
|
|
||||||
|
|
||||||
memcpy(dst, s1 + s1_start, l1_cpy);
|
|
||||||
memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy);
|
|
||||||
|
|
||||||
/* Panics must be written immediately */
|
/* Panics must be written immediately */
|
||||||
if (reason != KMSG_DUMP_OOPS)
|
if (reason != KMSG_DUMP_OOPS)
|
||||||
@ -375,6 +360,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cxt->dump.max_reason = KMSG_DUMP_OOPS;
|
||||||
cxt->dump.dump = mtdoops_do_dump;
|
cxt->dump.dump = mtdoops_do_dump;
|
||||||
err = kmsg_dump_register(&cxt->dump);
|
err = kmsg_dump_register(&cxt->dump);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -94,20 +94,15 @@ static const char *get_reason_str(enum kmsg_dump_reason reason)
|
|||||||
* as we can from the end of the buffer.
|
* as we can from the end of the buffer.
|
||||||
*/
|
*/
|
||||||
static void pstore_dump(struct kmsg_dumper *dumper,
|
static void pstore_dump(struct kmsg_dumper *dumper,
|
||||||
enum kmsg_dump_reason reason,
|
enum kmsg_dump_reason reason)
|
||||||
const char *s1, unsigned long l1,
|
|
||||||
const char *s2, unsigned long l2)
|
|
||||||
{
|
{
|
||||||
unsigned long s1_start, s2_start;
|
unsigned long total = 0;
|
||||||
unsigned long l1_cpy, l2_cpy;
|
|
||||||
unsigned long size, total = 0;
|
|
||||||
char *dst;
|
|
||||||
const char *why;
|
const char *why;
|
||||||
u64 id;
|
u64 id;
|
||||||
int hsize, ret;
|
|
||||||
unsigned int part = 1;
|
unsigned int part = 1;
|
||||||
unsigned long flags = 0;
|
unsigned long flags = 0;
|
||||||
int is_locked = 0;
|
int is_locked = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
why = get_reason_str(reason);
|
why = get_reason_str(reason);
|
||||||
|
|
||||||
@ -119,30 +114,25 @@ static void pstore_dump(struct kmsg_dumper *dumper,
|
|||||||
spin_lock_irqsave(&psinfo->buf_lock, flags);
|
spin_lock_irqsave(&psinfo->buf_lock, flags);
|
||||||
oopscount++;
|
oopscount++;
|
||||||
while (total < kmsg_bytes) {
|
while (total < kmsg_bytes) {
|
||||||
|
char *dst;
|
||||||
|
unsigned long size;
|
||||||
|
int hsize;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
dst = psinfo->buf;
|
dst = psinfo->buf;
|
||||||
hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part);
|
hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part);
|
||||||
size = psinfo->bufsize - hsize;
|
size = psinfo->bufsize - hsize;
|
||||||
dst += hsize;
|
dst += hsize;
|
||||||
|
|
||||||
l2_cpy = min(l2, size);
|
if (!kmsg_dump_get_buffer(dumper, true, dst, size, &len))
|
||||||
l1_cpy = min(l1, size - l2_cpy);
|
|
||||||
|
|
||||||
if (l1_cpy + l2_cpy == 0)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
s2_start = l2 - l2_cpy;
|
|
||||||
s1_start = l1 - l1_cpy;
|
|
||||||
|
|
||||||
memcpy(dst, s1 + s1_start, l1_cpy);
|
|
||||||
memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy);
|
|
||||||
|
|
||||||
ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part,
|
ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part,
|
||||||
hsize + l1_cpy + l2_cpy, psinfo);
|
hsize + len, psinfo);
|
||||||
if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
|
if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
|
||||||
pstore_new_entry = 1;
|
pstore_new_entry = 1;
|
||||||
l1 -= l1_cpy;
|
|
||||||
l2 -= l2_cpy;
|
total += hsize + len;
|
||||||
total += l1_cpy + l2_cpy;
|
|
||||||
part++;
|
part++;
|
||||||
}
|
}
|
||||||
if (in_nmi()) {
|
if (in_nmi()) {
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
* is passed to the kernel.
|
* is passed to the kernel.
|
||||||
*/
|
*/
|
||||||
enum kmsg_dump_reason {
|
enum kmsg_dump_reason {
|
||||||
|
KMSG_DUMP_UNDEF,
|
||||||
KMSG_DUMP_PANIC,
|
KMSG_DUMP_PANIC,
|
||||||
KMSG_DUMP_OOPS,
|
KMSG_DUMP_OOPS,
|
||||||
KMSG_DUMP_EMERG,
|
KMSG_DUMP_EMERG,
|
||||||
@ -31,23 +32,37 @@ enum kmsg_dump_reason {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* struct kmsg_dumper - kernel crash message dumper structure
|
* struct kmsg_dumper - kernel crash message dumper structure
|
||||||
* @dump: The callback which gets called on crashes. The buffer is passed
|
|
||||||
* as two sections, where s1 (length l1) contains the older
|
|
||||||
* messages and s2 (length l2) contains the newer.
|
|
||||||
* @list: Entry in the dumper list (private)
|
* @list: Entry in the dumper list (private)
|
||||||
|
* @dump: Call into dumping code which will retrieve the data with
|
||||||
|
* through the record iterator
|
||||||
|
* @max_reason: filter for highest reason number that should be dumped
|
||||||
* @registered: Flag that specifies if this is already registered
|
* @registered: Flag that specifies if this is already registered
|
||||||
*/
|
*/
|
||||||
struct kmsg_dumper {
|
struct kmsg_dumper {
|
||||||
void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason,
|
|
||||||
const char *s1, unsigned long l1,
|
|
||||||
const char *s2, unsigned long l2);
|
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
int registered;
|
void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason);
|
||||||
|
enum kmsg_dump_reason max_reason;
|
||||||
|
bool active;
|
||||||
|
bool registered;
|
||||||
|
|
||||||
|
/* private state of the kmsg iterator */
|
||||||
|
u32 cur_idx;
|
||||||
|
u32 next_idx;
|
||||||
|
u64 cur_seq;
|
||||||
|
u64 next_seq;
|
||||||
};
|
};
|
||||||
|
|
||||||
#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(struct kmsg_dumper *dumper, bool syslog,
|
||||||
|
char *line, size_t size, size_t *len);
|
||||||
|
|
||||||
|
bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
|
||||||
|
char *buf, size_t size, size_t *len);
|
||||||
|
|
||||||
|
void kmsg_dump_rewind(struct kmsg_dumper *dumper);
|
||||||
|
|
||||||
int kmsg_dump_register(struct kmsg_dumper *dumper);
|
int kmsg_dump_register(struct kmsg_dumper *dumper);
|
||||||
|
|
||||||
int kmsg_dump_unregister(struct kmsg_dumper *dumper);
|
int kmsg_dump_unregister(struct kmsg_dumper *dumper);
|
||||||
@ -56,6 +71,22 @@ static inline void kmsg_dump(enum kmsg_dump_reason reason)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
|
||||||
|
const char *line, size_t size, size_t *len)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
|
||||||
|
char *buf, size_t size, size_t *len)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kmsg_dump_rewind(struct kmsg_dumper *dumper)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
static inline int kmsg_dump_register(struct kmsg_dumper *dumper)
|
static inline int kmsg_dump_register(struct kmsg_dumper *dumper)
|
||||||
{
|
{
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
232
kernel/printk.c
232
kernel/printk.c
@ -909,7 +909,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
|
|||||||
/*
|
/*
|
||||||
* Find first record that fits, including all following records,
|
* Find first record that fits, including all following records,
|
||||||
* into the user-provided buffer for this dump.
|
* into the user-provided buffer for this dump.
|
||||||
*/
|
*/
|
||||||
seq = clear_seq;
|
seq = clear_seq;
|
||||||
idx = clear_idx;
|
idx = clear_idx;
|
||||||
while (seq < log_next_seq) {
|
while (seq < log_next_seq) {
|
||||||
@ -919,6 +919,8 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
|
|||||||
idx = log_next(idx);
|
idx = log_next(idx);
|
||||||
seq++;
|
seq++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* move first record forward until length fits into the buffer */
|
||||||
seq = clear_seq;
|
seq = clear_seq;
|
||||||
idx = clear_idx;
|
idx = clear_idx;
|
||||||
while (len > size && seq < log_next_seq) {
|
while (len > size && seq < log_next_seq) {
|
||||||
@ -929,7 +931,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
|
|||||||
seq++;
|
seq++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* last message in this dump */
|
/* last message fitting into this dump */
|
||||||
next_seq = log_next_seq;
|
next_seq = log_next_seq;
|
||||||
|
|
||||||
len = 0;
|
len = 0;
|
||||||
@ -2300,48 +2302,210 @@ module_param_named(always_kmsg_dump, always_kmsg_dump, bool, S_IRUGO | S_IWUSR);
|
|||||||
* kmsg_dump - dump kernel log to kernel message dumpers.
|
* kmsg_dump - dump kernel log to kernel message dumpers.
|
||||||
* @reason: the reason (oops, panic etc) for dumping
|
* @reason: the reason (oops, panic etc) for dumping
|
||||||
*
|
*
|
||||||
* Iterate through each of the dump devices and call the oops/panic
|
* Call each of the registered dumper's dump() callback, which can
|
||||||
* callbacks with the log buffer.
|
* retrieve the kmsg records with kmsg_dump_get_line() or
|
||||||
|
* kmsg_dump_get_buffer().
|
||||||
*/
|
*/
|
||||||
void kmsg_dump(enum kmsg_dump_reason reason)
|
void kmsg_dump(enum kmsg_dump_reason reason)
|
||||||
{
|
{
|
||||||
u64 idx;
|
|
||||||
struct kmsg_dumper *dumper;
|
struct kmsg_dumper *dumper;
|
||||||
const char *s1, *s2;
|
|
||||||
unsigned long l1, l2;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
if ((reason > KMSG_DUMP_OOPS) && !always_kmsg_dump)
|
if ((reason > KMSG_DUMP_OOPS) && !always_kmsg_dump)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Theoretically, the log could move on after we do this, but
|
|
||||||
there's not a lot we can do about that. The new messages
|
|
||||||
will overwrite the start of what we dump. */
|
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&logbuf_lock, flags);
|
|
||||||
if (syslog_seq < log_first_seq)
|
|
||||||
idx = syslog_idx;
|
|
||||||
else
|
|
||||||
idx = log_first_idx;
|
|
||||||
|
|
||||||
if (idx > log_next_idx) {
|
|
||||||
s1 = log_buf;
|
|
||||||
l1 = log_next_idx;
|
|
||||||
|
|
||||||
s2 = log_buf + idx;
|
|
||||||
l2 = log_buf_len - idx;
|
|
||||||
} else {
|
|
||||||
s1 = "";
|
|
||||||
l1 = 0;
|
|
||||||
|
|
||||||
s2 = log_buf + idx;
|
|
||||||
l2 = log_next_idx - idx;
|
|
||||||
}
|
|
||||||
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
|
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
list_for_each_entry_rcu(dumper, &dump_list, list)
|
list_for_each_entry_rcu(dumper, &dump_list, list) {
|
||||||
dumper->dump(dumper, reason, s1, l1, s2, l2);
|
if (dumper->max_reason && reason > dumper->max_reason)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* initialize iterator with data about the stored records */
|
||||||
|
dumper->active = true;
|
||||||
|
|
||||||
|
raw_spin_lock_irqsave(&logbuf_lock, flags);
|
||||||
|
dumper->cur_seq = clear_seq;
|
||||||
|
dumper->cur_idx = clear_idx;
|
||||||
|
dumper->next_seq = log_next_seq;
|
||||||
|
dumper->next_idx = log_next_idx;
|
||||||
|
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
|
||||||
|
|
||||||
|
/* invoke dumper which will iterate over records */
|
||||||
|
dumper->dump(dumper, reason);
|
||||||
|
|
||||||
|
/* reset iterator */
|
||||||
|
dumper->active = false;
|
||||||
|
}
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
struct log *msg;
|
||||||
|
size_t l = 0;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
if (!dumper->active)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
raw_spin_lock_irqsave(&logbuf_lock, flags);
|
||||||
|
if (dumper->cur_seq < log_first_seq) {
|
||||||
|
/* messages are gone, move to first available one */
|
||||||
|
dumper->cur_seq = log_first_seq;
|
||||||
|
dumper->cur_idx = log_first_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* last entry */
|
||||||
|
if (dumper->cur_seq >= log_next_seq) {
|
||||||
|
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = log_from_idx(dumper->cur_idx);
|
||||||
|
l = msg_print_text(msg, syslog,
|
||||||
|
line, size);
|
||||||
|
|
||||||
|
dumper->cur_idx = log_next(dumper->cur_idx);
|
||||||
|
dumper->cur_seq++;
|
||||||
|
ret = true;
|
||||||
|
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
|
||||||
|
out:
|
||||||
|
if (len)
|
||||||
|
*len = l;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(kmsg_dump_get_line);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kmsg_dump_get_buffer - copy kmsg log lines
|
||||||
|
* @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 end of the kmsg buffer and fill the provided buffer
|
||||||
|
* with as many of the the *youngest* kmsg records that fit into it.
|
||||||
|
* If the buffer is large enough, all available kmsg records will be
|
||||||
|
* copied with a single call.
|
||||||
|
*
|
||||||
|
* Consecutive calls will fill the buffer with the next block of
|
||||||
|
* available older records, not including the earlier retrieved ones.
|
||||||
|
*
|
||||||
|
* A return value of FALSE indicates that there are no more records to
|
||||||
|
* read.
|
||||||
|
*/
|
||||||
|
bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
|
||||||
|
char *buf, size_t size, size_t *len)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
u64 seq;
|
||||||
|
u32 idx;
|
||||||
|
u64 next_seq;
|
||||||
|
u32 next_idx;
|
||||||
|
size_t l = 0;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
if (!dumper->active)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
raw_spin_lock_irqsave(&logbuf_lock, flags);
|
||||||
|
if (dumper->cur_seq < log_first_seq) {
|
||||||
|
/* messages are gone, move to first available one */
|
||||||
|
dumper->cur_seq = log_first_seq;
|
||||||
|
dumper->cur_idx = log_first_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* last entry */
|
||||||
|
if (dumper->cur_seq >= dumper->next_seq) {
|
||||||
|
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* calculate length of entire buffer */
|
||||||
|
seq = dumper->cur_seq;
|
||||||
|
idx = dumper->cur_idx;
|
||||||
|
while (seq < dumper->next_seq) {
|
||||||
|
struct log *msg = log_from_idx(idx);
|
||||||
|
|
||||||
|
l += msg_print_text(msg, true, NULL, 0);
|
||||||
|
idx = log_next(idx);
|
||||||
|
seq++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* move first record forward until length fits into the buffer */
|
||||||
|
seq = dumper->cur_seq;
|
||||||
|
idx = dumper->cur_idx;
|
||||||
|
while (l > size && seq < dumper->next_seq) {
|
||||||
|
struct log *msg = log_from_idx(idx);
|
||||||
|
|
||||||
|
l -= msg_print_text(msg, true, NULL, 0);
|
||||||
|
idx = log_next(idx);
|
||||||
|
seq++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* last message in next interation */
|
||||||
|
next_seq = seq;
|
||||||
|
next_idx = idx;
|
||||||
|
|
||||||
|
l = 0;
|
||||||
|
while (seq < dumper->next_seq) {
|
||||||
|
struct log *msg = log_from_idx(idx);
|
||||||
|
|
||||||
|
l += msg_print_text(msg, syslog,
|
||||||
|
buf + l, size - l);
|
||||||
|
|
||||||
|
idx = log_next(idx);
|
||||||
|
seq++;
|
||||||
|
}
|
||||||
|
|
||||||
|
dumper->next_seq = next_seq;
|
||||||
|
dumper->next_idx = next_idx;
|
||||||
|
ret = true;
|
||||||
|
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
|
||||||
|
out:
|
||||||
|
if (len)
|
||||||
|
*len = l;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(kmsg_dump_get_buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kmsg_dump_rewind - reset the interator
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
void kmsg_dump_rewind(struct kmsg_dumper *dumper)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
raw_spin_lock_irqsave(&logbuf_lock, flags);
|
||||||
|
dumper->cur_seq = clear_seq;
|
||||||
|
dumper->cur_idx = clear_idx;
|
||||||
|
dumper->next_seq = log_next_seq;
|
||||||
|
dumper->next_idx = log_next_idx;
|
||||||
|
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(kmsg_dump_rewind);
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user