Skip to content

Commit

Permalink
kmsg - kmsg_dump() use iterator to receive log buffer content
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Kay Sievers authored and Greg Kroah-Hartman committed Jun 15, 2012
1 parent 1bd289d commit e2ae715
Show file tree
Hide file tree
Showing 6 changed files with 258 additions and 137 deletions.
61 changes: 7 additions & 54 deletions arch/powerpc/platforms/pseries/nvram.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,7 @@ static const char *pseries_nvram_os_partitions[] = {
};

static void oops_to_nvram(struct kmsg_dumper *dumper,
enum kmsg_dump_reason reason,
const char *old_msgs, unsigned long old_len,
const char *new_msgs, unsigned long new_len);
enum kmsg_dump_reason reason);

static struct kmsg_dumper nvram_kmsg_dumper = {
.dump = oops_to_nvram
Expand Down Expand Up @@ -503,28 +501,6 @@ int __init pSeries_nvram_init(void)
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,
* would logging this oops/panic overwrite an RTAS event that rtas_errd
Expand All @@ -541,27 +517,6 @@ static int clobbering_unread_rtas_event(void)
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() */
static int nvram_compress(const void *in, void *out, size_t inlen,
size_t outlen)
Expand Down Expand Up @@ -619,9 +574,7 @@ static int zip_oops(size_t text_len)
* partition. If that's too much, go back and capture uncompressed text.
*/
static void oops_to_nvram(struct kmsg_dumper *dumper,
enum kmsg_dump_reason reason,
const char *old_msgs, unsigned long old_len,
const char *new_msgs, unsigned long new_len)
enum kmsg_dump_reason reason)
{
static unsigned int oops_count = 0;
static bool panicking = false;
Expand Down Expand Up @@ -660,14 +613,14 @@ static void oops_to_nvram(struct kmsg_dumper *dumper,
return;

if (big_oops_buf) {
text_len = capture_last_msgs(old_msgs, old_len,
new_msgs, new_len, big_oops_buf, big_oops_buf_sz);
text_len = elide_severities(big_oops_buf, text_len);
kmsg_dump_get_buffer(dumper, false,
big_oops_buf, big_oops_buf_sz, &text_len);
rc = zip_oops(text_len);
}
if (rc != 0) {
text_len = capture_last_msgs(old_msgs, old_len,
new_msgs, new_len, oops_data, oops_data_sz);
kmsg_dump_rewind(dumper);
kmsg_dump_get_buffer(dumper, true,
oops_data, oops_data_sz, &text_len);
err_type = ERR_TYPE_KERNEL_PANIC;
*oops_len = (u16) text_len;
}
Expand Down
13 changes: 5 additions & 8 deletions arch/x86/platform/mrst/early_printk_mrst.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,19 +110,16 @@ static struct kmsg_dumper dw_dumper;
static int dumper_registered;

static void dw_kmsg_dump(struct kmsg_dumper *dumper,
enum kmsg_dump_reason reason,
const char *s1, unsigned long l1,
const char *s2, unsigned long l2)
enum kmsg_dump_reason reason)
{
int i;
static char line[1024];
size_t len;

/* When run to this, we'd better re-init the HW */
mrst_early_console_init();

for (i = 0; i < l1; i++)
early_mrst_console.write(&early_mrst_console, s1 + i, 1);
for (i = 0; i < l2; i++)
early_mrst_console.write(&early_mrst_console, s2 + i, 1);
while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len))
early_mrst_console.write(&early_mrst_console, line, len);
}

/* Set the ratio rate to 115200, 8n1, IRQ disabled */
Expand Down
22 changes: 4 additions & 18 deletions drivers/mtd/mtdoops.c
Original file line number Diff line number Diff line change
Expand Up @@ -304,32 +304,17 @@ static void find_next_position(struct mtdoops_context *cxt)
}

static void mtdoops_do_dump(struct kmsg_dumper *dumper,
enum kmsg_dump_reason reason, const char *s1, unsigned long l1,
const char *s2, unsigned long l2)
enum kmsg_dump_reason reason)
{
struct mtdoops_context *cxt = container_of(dumper,
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 */
if (reason == KMSG_DUMP_OOPS && !dump_oops)
return;

dst = cxt->oops_buf + MTDOOPS_HEADER_SIZE; /* Skip the header */
l2_cpy = min(l2, record_size - MTDOOPS_HEADER_SIZE);
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);
kmsg_dump_get_buffer(dumper, true, cxt->oops_buf + MTDOOPS_HEADER_SIZE,
record_size - MTDOOPS_HEADER_SIZE, NULL);

/* Panics must be written immediately */
if (reason != KMSG_DUMP_OOPS)
Expand Down Expand Up @@ -375,6 +360,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
return;
}

cxt->dump.max_reason = KMSG_DUMP_OOPS;
cxt->dump.dump = mtdoops_do_dump;
err = kmsg_dump_register(&cxt->dump);
if (err) {
Expand Down
34 changes: 12 additions & 22 deletions fs/pstore/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,20 +94,15 @@ static const char *get_reason_str(enum kmsg_dump_reason reason)
* as we can from the end of the buffer.
*/
static void pstore_dump(struct kmsg_dumper *dumper,
enum kmsg_dump_reason reason,
const char *s1, unsigned long l1,
const char *s2, unsigned long l2)
enum kmsg_dump_reason reason)
{
unsigned long s1_start, s2_start;
unsigned long l1_cpy, l2_cpy;
unsigned long size, total = 0;
char *dst;
unsigned long total = 0;
const char *why;
u64 id;
int hsize, ret;
unsigned int part = 1;
unsigned long flags = 0;
int is_locked = 0;
int ret;

why = get_reason_str(reason);

Expand All @@ -119,30 +114,25 @@ static void pstore_dump(struct kmsg_dumper *dumper,
spin_lock_irqsave(&psinfo->buf_lock, flags);
oopscount++;
while (total < kmsg_bytes) {
char *dst;
unsigned long size;
int hsize;
size_t len;

dst = psinfo->buf;
hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part);
size = psinfo->bufsize - hsize;
dst += hsize;

l2_cpy = min(l2, size);
l1_cpy = min(l1, size - l2_cpy);

if (l1_cpy + l2_cpy == 0)
if (!kmsg_dump_get_buffer(dumper, true, dst, size, &len))
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,
hsize + l1_cpy + l2_cpy, psinfo);
hsize + len, psinfo);
if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
pstore_new_entry = 1;
l1 -= l1_cpy;
l2 -= l2_cpy;
total += l1_cpy + l2_cpy;

total += hsize + len;
part++;
}
if (in_nmi()) {
Expand Down
45 changes: 38 additions & 7 deletions include/linux/kmsg_dump.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* is passed to the kernel.
*/
enum kmsg_dump_reason {
KMSG_DUMP_UNDEF,
KMSG_DUMP_PANIC,
KMSG_DUMP_OOPS,
KMSG_DUMP_EMERG,
Expand All @@ -31,23 +32,37 @@ enum kmsg_dump_reason {

/**
* 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)
* @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
*/
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;
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
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_unregister(struct kmsg_dumper *dumper);
Expand All @@ -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)
{
return -EINVAL;
Expand Down
Loading

0 comments on commit e2ae715

Please sign in to comment.