Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e2ae715d authored by Kay Sievers's avatar Kay Sievers Committed by Greg Kroah-Hartman
Browse files

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: default avatarKay Sievers <kay@vrfy.org>
Tested-by: default avatarTony Luck <tony.luck@intel.com>
Reported-by: default avatarAnton Vorontsov <anton.vorontsov@linaro.org>
Tested-by: default avatarAnton Vorontsov <anton.vorontsov@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 1bd289d1
Loading
Loading
Loading
Loading
+7 −54
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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)
@@ -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;
@@ -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;
	}
+5 −8
Original line number Diff line number Diff line
@@ -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 */
+4 −18
Original line number Diff line number Diff line
@@ -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)
@@ -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) {
+12 −22
Original line number Diff line number Diff line
@@ -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);

@@ -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()) {
+38 −7
Original line number Diff line number Diff line
@@ -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,
@@ -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);
@@ -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;
Loading