Some pstore backend only supports async mode, so it's possible to do some IO at the same time. In this case we need to manage the single psinfo->buf not to broken by concurrent IO. For example PSTORE_TYPE_CONSOLE IO is serialized by psinfo->buf_lock but later IO can be issued before finishing previous request. In this case it overwrites psinfo->buf so the previous result might be broken. This patch adds psinfo->bufpos field to keep track of current position in order to use psinfo->buf as a ring buffer. However it's just a simple, best-effort way of doing that, and provides no 100% guarantee. It's only effective for small concurrent IO like PSTORE_TYPE_CONSOLE IMHO. The new PSTORE_FLAGS_ASYNC flag enables management of buffer position. The pstore_prepare_buf() is called before accessing the psinfo->buf and the pstore_update_buf() is called after accessing the buf. The pstore_get_buf() is provided for psinfo->write callback to determine the current position of available buffer. Cc: Anton Vorontsov <anton@xxxxxxxxxx> Cc: Colin Cross <ccross@xxxxxxxxxxx> Cc: Kees Cook <keescook@xxxxxxxxxxxx> Cc: Tony Luck <tony.luck@xxxxxxxxx> Cc: Matt Fleming <matt@xxxxxxxxxxxxxxxxxxx> Cc: linux-efi@xxxxxxxxxxxxxxx Signed-off-by: Namhyung Kim <namhyung@xxxxxxxxxx> --- drivers/firmware/efi/efi-pstore.c | 2 +- fs/pstore/platform.c | 48 +++++++++++++++++++++++++++++++-------- include/linux/pstore.h | 4 ++++ 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index 4daa5acd9117..ae0ffe259e07 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -259,7 +259,7 @@ static int efi_pstore_write(enum pstore_type_id type, efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES, !pstore_cannot_block_path(reason), - size, psi->buf); + size, pstore_get_buf(psi)); if (reason == KMSG_DUMP_OOPS) efivar_run_worker(); diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 76dd604a0f2c..26e2808cf554 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -150,6 +150,27 @@ bool pstore_cannot_block_path(enum kmsg_dump_reason reason) } EXPORT_SYMBOL_GPL(pstore_cannot_block_path); +static void *pstore_prepare_buf(struct pstore_info *psi, size_t len) +{ + if (psi->bufpos + len > psi->bufsize || + (psi->flags & PSTORE_FLAGS_ASYNC) == 0) + psi->bufpos = 0; + + return psi->buf + psi->bufpos; +} + +static void pstore_update_buf(struct pstore_info *psi, size_t len) +{ + if (psi->flags & PSTORE_FLAGS_ASYNC) + psi->bufpos += len; +} + +void *pstore_get_buf(struct pstore_info *psi) +{ + return psi->buf + psi->bufpos; +} +EXPORT_SYMBOL_GPL(pstore_get_buf); + #ifdef CONFIG_PSTORE_ZLIB_COMPRESS /* Derived from logfs_compress() */ static int compress_zlib(const void *in, void *out, size_t inlen, size_t outlen) @@ -455,18 +476,21 @@ static size_t copy_kmsg_to_buffer(int hsize, size_t len) { size_t total_len; size_t diff; + void *dst; total_len = hsize + len; + dst = pstore_prepare_buf(psinfo, total_len); if (total_len > psinfo->bufsize) { diff = total_len - psinfo->bufsize + hsize; - memcpy(psinfo->buf, big_oops_buf, hsize); - memcpy(psinfo->buf + hsize, big_oops_buf + diff, + memcpy(dst, big_oops_buf, hsize); + memcpy(dst + hsize, big_oops_buf + diff, psinfo->bufsize - hsize); total_len = psinfo->bufsize; } else - memcpy(psinfo->buf, big_oops_buf, total_len); + memcpy(dst, big_oops_buf, total_len); + pstore_update_buf(psinfo, total_len); return total_len; } @@ -500,7 +524,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, } oopscount++; while (total < kmsg_bytes) { - char *dst; + char *dst, *buf; unsigned long size; int hsize; int zipped_len = -1; @@ -514,6 +538,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, } else { dst = psinfo->buf; size = psinfo->bufsize; + psinfo->bufpos = 0; } hsize = sprintf(dst, "%s#%d Part%u\n", why, oopscount, part); @@ -524,8 +549,9 @@ static void pstore_dump(struct kmsg_dumper *dumper, break; if (big_oops_buf && is_locked) { - zipped_len = pstore_compress(dst, psinfo->buf, - hsize + len, psinfo->bufsize); + buf = pstore_prepare_buf(psinfo, hsize + len); + zipped_len = pstore_compress(dst, buf, hsize + len, + psinfo->bufsize - psinfo->bufpos); if (zipped_len > 0) { compressed = true; @@ -543,6 +569,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, pstore_new_entry = 1; total += total_len; + pstore_update_buf(psinfo, total_len); part++; } if (is_locked) @@ -573,6 +600,7 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c) while (s < e) { unsigned long flags; + void *dst; u64 id; if (c > psinfo->bufsize) @@ -584,8 +612,10 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c) } else { spin_lock_irqsave(&psinfo->buf_lock, flags); } - memcpy(psinfo->buf, s, c); + dst = pstore_prepare_buf(psinfo, c); + memcpy(dst, s, c); psinfo->write(PSTORE_TYPE_CONSOLE, 0, &id, 0, 0, 0, c, psinfo); + pstore_update_buf(psinfo, c); spin_unlock_irqrestore(&psinfo->buf_lock, flags); s += c; c = e - s; @@ -619,8 +649,8 @@ static int pstore_write_compat(enum pstore_type_id type, bool compressed, size_t size, struct pstore_info *psi) { - return psi->write_buf(type, reason, id, part, psinfo->buf, compressed, - size, psi); + return psi->write_buf(type, reason, id, part, pstore_get_buf(psinfo), + compressed, size, psi); } /* diff --git a/include/linux/pstore.h b/include/linux/pstore.h index 9790904de6d2..14f524177b1f 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -52,6 +52,7 @@ struct pstore_info { spinlock_t buf_lock; /* serialize access to 'buf' */ char *buf; size_t bufsize; + size_t bufpos; struct mutex read_mutex; /* serialize open/read/close */ int flags; int (*open)(struct pstore_info *psi); @@ -79,8 +80,11 @@ struct pstore_info { #define PSTORE_FLAGS_FTRACE (1 << 2) #define PSTORE_FLAGS_PMSG (1 << 3) +#define PSTORE_FLAGS_ASYNC (1 << 30) + extern int pstore_register(struct pstore_info *); extern void pstore_unregister(struct pstore_info *); extern bool pstore_cannot_block_path(enum kmsg_dump_reason reason); +extern void *pstore_get_buf(struct pstore_info *); #endif /*_LINUX_PSTORE_H*/ -- 2.8.0 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html