aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKay Sievers <kay@vrfy.org>2012-06-15 08:07:51 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-06-15 17:53:59 -0400
commite2ae715d66bf4becfb85eb84b7150e23cf27df30 (patch)
tree206f08bc1f5138d1416c040f78c5b527ca07536d
parent1bd289d1e8e29c09be0e783a53f16a1dc80684be (diff)
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>
-rw-r--r--arch/powerpc/platforms/pseries/nvram.c61
-rw-r--r--arch/x86/platform/mrst/early_printk_mrst.c13
-rw-r--r--drivers/mtd/mtdoops.c22
-rw-r--r--fs/pstore/platform.c34
-rw-r--r--include/linux/kmsg_dump.h45
-rw-r--r--kernel/printk.c220
6 files changed, 258 insertions, 137 deletions
diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index 36f957f31842..8733a86ad52e 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -68,9 +68,7 @@ static const char *pseries_nvram_os_partitions[] = {
68}; 68};
69 69
70static void oops_to_nvram(struct kmsg_dumper *dumper, 70static void oops_to_nvram(struct kmsg_dumper *dumper,
71 enum kmsg_dump_reason reason, 71 enum kmsg_dump_reason reason);
72 const char *old_msgs, unsigned long old_len,
73 const char *new_msgs, unsigned long new_len);
74 72
75static struct kmsg_dumper nvram_kmsg_dumper = { 73static struct kmsg_dumper nvram_kmsg_dumper = {
76 .dump = oops_to_nvram 74 .dump = oops_to_nvram
@@ -504,28 +502,6 @@ int __init pSeries_nvram_init(void)
504} 502}
505 503
506/* 504/*
507 * Try to capture the last capture_len bytes of the printk buffer. Return
508 * the amount actually captured.
509 */
510static size_t capture_last_msgs(const char *old_msgs, size_t old_len,
511 const char *new_msgs, size_t new_len,
512 char *captured, size_t capture_len)
513{
514 if (new_len >= capture_len) {
515 memcpy(captured, new_msgs + (new_len - capture_len),
516 capture_len);
517 return capture_len;
518 } else {
519 /* Grab the end of old_msgs. */
520 size_t old_tail_len = min(old_len, capture_len - new_len);
521 memcpy(captured, old_msgs + (old_len - old_tail_len),
522 old_tail_len);
523 memcpy(captured + old_tail_len, new_msgs, new_len);
524 return old_tail_len + new_len;
525 }
526}
527
528/*
529 * Are we using the ibm,rtas-log for oops/panic reports? And if so, 505 * Are we using the ibm,rtas-log for oops/panic reports? And if so,
530 * would logging this oops/panic overwrite an RTAS event that rtas_errd 506 * would logging this oops/panic overwrite an RTAS event that rtas_errd
531 * hasn't had a chance to read and process? Return 1 if so, else 0. 507 * hasn't had a chance to read and process? Return 1 if so, else 0.
@@ -541,27 +517,6 @@ static int clobbering_unread_rtas_event(void)
541 NVRAM_RTAS_READ_TIMEOUT); 517 NVRAM_RTAS_READ_TIMEOUT);
542} 518}
543 519
544/* Squeeze out each line's <n> severity prefix. */
545static size_t elide_severities(char *buf, size_t len)
546{
547 char *in, *out, *buf_end = buf + len;
548 /* Assume a <n> at the very beginning marks the start of a line. */
549 int newline = 1;
550
551 in = out = buf;
552 while (in < buf_end) {
553 if (newline && in+3 <= buf_end &&
554 *in == '<' && isdigit(in[1]) && in[2] == '>') {
555 in += 3;
556 newline = 0;
557 } else {
558 newline = (*in == '\n');
559 *out++ = *in++;
560 }
561 }
562 return out - buf;
563}
564
565/* Derived from logfs_compress() */ 520/* Derived from logfs_compress() */
566static int nvram_compress(const void *in, void *out, size_t inlen, 521static int nvram_compress(const void *in, void *out, size_t inlen,
567 size_t outlen) 522 size_t outlen)
@@ -619,9 +574,7 @@ static int zip_oops(size_t text_len)
619 * partition. If that's too much, go back and capture uncompressed text. 574 * partition. If that's too much, go back and capture uncompressed text.
620 */ 575 */
621static void oops_to_nvram(struct kmsg_dumper *dumper, 576static void oops_to_nvram(struct kmsg_dumper *dumper,
622 enum kmsg_dump_reason reason, 577 enum kmsg_dump_reason reason)
623 const char *old_msgs, unsigned long old_len,
624 const char *new_msgs, unsigned long new_len)
625{ 578{
626 static unsigned int oops_count = 0; 579 static unsigned int oops_count = 0;
627 static bool panicking = false; 580 static bool panicking = false;
@@ -660,14 +613,14 @@ static void oops_to_nvram(struct kmsg_dumper *dumper,
660 return; 613 return;
661 614
662 if (big_oops_buf) { 615 if (big_oops_buf) {
663 text_len = capture_last_msgs(old_msgs, old_len, 616 kmsg_dump_get_buffer(dumper, false,
664 new_msgs, new_len, big_oops_buf, big_oops_buf_sz); 617 big_oops_buf, big_oops_buf_sz, &text_len);
665 text_len = elide_severities(big_oops_buf, text_len);
666 rc = zip_oops(text_len); 618 rc = zip_oops(text_len);
667 } 619 }
668 if (rc != 0) { 620 if (rc != 0) {
669 text_len = capture_last_msgs(old_msgs, old_len, 621 kmsg_dump_rewind(dumper);
670 new_msgs, new_len, oops_data, oops_data_sz); 622 kmsg_dump_get_buffer(dumper, true,
623 oops_data, oops_data_sz, &text_len);
671 err_type = ERR_TYPE_KERNEL_PANIC; 624 err_type = ERR_TYPE_KERNEL_PANIC;
672 *oops_len = (u16) text_len; 625 *oops_len = (u16) text_len;
673 } 626 }
diff --git a/arch/x86/platform/mrst/early_printk_mrst.c b/arch/x86/platform/mrst/early_printk_mrst.c
index 3c6e328483c7..028454f0c3a5 100644
--- a/arch/x86/platform/mrst/early_printk_mrst.c
+++ b/arch/x86/platform/mrst/early_printk_mrst.c
@@ -110,19 +110,16 @@ static struct kmsg_dumper dw_dumper;
110static int dumper_registered; 110static int dumper_registered;
111 111
112static void dw_kmsg_dump(struct kmsg_dumper *dumper, 112static void dw_kmsg_dump(struct kmsg_dumper *dumper,
113 enum kmsg_dump_reason reason, 113 enum kmsg_dump_reason reason)
114 const char *s1, unsigned long l1,
115 const char *s2, unsigned long l2)
116{ 114{
117 int i; 115 static char line[1024];
116 size_t len;
118 117
119 /* When run to this, we'd better re-init the HW */ 118 /* When run to this, we'd better re-init the HW */
120 mrst_early_console_init(); 119 mrst_early_console_init();
121 120
122 for (i = 0; i < l1; i++) 121 while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len))
123 early_mrst_console.write(&early_mrst_console, s1 + i, 1); 122 early_mrst_console.write(&early_mrst_console, line, len);
124 for (i = 0; i < l2; i++)
125 early_mrst_console.write(&early_mrst_console, s2 + i, 1);
126} 123}
127 124
128/* Set the ratio rate to 115200, 8n1, IRQ disabled */ 125/* Set the ratio rate to 115200, 8n1, IRQ disabled */
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c
index ae36d7e1e913..551e316e4454 100644
--- a/drivers/mtd/mtdoops.c
+++ b/drivers/mtd/mtdoops.c
@@ -304,32 +304,17 @@ static void find_next_position(struct mtdoops_context *cxt)
304} 304}
305 305
306static void mtdoops_do_dump(struct kmsg_dumper *dumper, 306static void mtdoops_do_dump(struct kmsg_dumper *dumper,
307 enum kmsg_dump_reason reason, const char *s1, unsigned long l1, 307 enum kmsg_dump_reason reason)
308 const char *s2, unsigned long l2)
309{ 308{
310 struct mtdoops_context *cxt = container_of(dumper, 309 struct mtdoops_context *cxt = container_of(dumper,
311 struct mtdoops_context, dump); 310 struct mtdoops_context, dump);
312 unsigned long s1_start, s2_start;
313 unsigned long l1_cpy, l2_cpy;
314 char *dst;
315
316 if (reason != KMSG_DUMP_OOPS &&
317 reason != KMSG_DUMP_PANIC)
318 return;
319 311
320 /* Only dump oopses if dump_oops is set */ 312 /* Only dump oopses if dump_oops is set */
321 if (reason == KMSG_DUMP_OOPS && !dump_oops) 313 if (reason == KMSG_DUMP_OOPS && !dump_oops)
322 return; 314 return;
323 315
324 dst = cxt->oops_buf + MTDOOPS_HEADER_SIZE; /* Skip the header */ 316 kmsg_dump_get_buffer(dumper, true, cxt->oops_buf + MTDOOPS_HEADER_SIZE,
325 l2_cpy = min(l2, record_size - MTDOOPS_HEADER_SIZE); 317 record_size - MTDOOPS_HEADER_SIZE, NULL);
326 l1_cpy = min(l1, record_size - MTDOOPS_HEADER_SIZE - l2_cpy);
327
328 s2_start = l2 - l2_cpy;
329 s1_start = l1 - l1_cpy;
330
331 memcpy(dst, s1 + s1_start, l1_cpy);
332 memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy);
333 318
334 /* Panics must be written immediately */ 319 /* Panics must be written immediately */
335 if (reason != KMSG_DUMP_OOPS) 320 if (reason != KMSG_DUMP_OOPS)
@@ -375,6 +360,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
375 return; 360 return;
376 } 361 }
377 362
363 cxt->dump.max_reason = KMSG_DUMP_OOPS;
378 cxt->dump.dump = mtdoops_do_dump; 364 cxt->dump.dump = mtdoops_do_dump;
379 err = kmsg_dump_register(&cxt->dump); 365 err = kmsg_dump_register(&cxt->dump);
380 if (err) { 366 if (err) {
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index 82c585f715e3..03ce7a9b81cc 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -94,20 +94,15 @@ static const char *get_reason_str(enum kmsg_dump_reason reason)
94 * as we can from the end of the buffer. 94 * as we can from the end of the buffer.
95 */ 95 */
96static void pstore_dump(struct kmsg_dumper *dumper, 96static void pstore_dump(struct kmsg_dumper *dumper,
97 enum kmsg_dump_reason reason, 97 enum kmsg_dump_reason reason)
98 const char *s1, unsigned long l1,
99 const char *s2, unsigned long l2)
100{ 98{
101 unsigned long s1_start, s2_start; 99 unsigned long total = 0;
102 unsigned long l1_cpy, l2_cpy;
103 unsigned long size, total = 0;
104 char *dst;
105 const char *why; 100 const char *why;
106 u64 id; 101 u64 id;
107 int hsize, ret;
108 unsigned int part = 1; 102 unsigned int part = 1;
109 unsigned long flags = 0; 103 unsigned long flags = 0;
110 int is_locked = 0; 104 int is_locked = 0;
105 int ret;
111 106
112 why = get_reason_str(reason); 107 why = get_reason_str(reason);
113 108
@@ -119,30 +114,25 @@ static void pstore_dump(struct kmsg_dumper *dumper,
119 spin_lock_irqsave(&psinfo->buf_lock, flags); 114 spin_lock_irqsave(&psinfo->buf_lock, flags);
120 oopscount++; 115 oopscount++;
121 while (total < kmsg_bytes) { 116 while (total < kmsg_bytes) {
117 char *dst;
118 unsigned long size;
119 int hsize;
120 size_t len;
121
122 dst = psinfo->buf; 122 dst = psinfo->buf;
123 hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part); 123 hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part);
124 size = psinfo->bufsize - hsize; 124 size = psinfo->bufsize - hsize;
125 dst += hsize; 125 dst += hsize;
126 126
127 l2_cpy = min(l2, size); 127 if (!kmsg_dump_get_buffer(dumper, true, dst, size, &len))
128 l1_cpy = min(l1, size - l2_cpy);
129
130 if (l1_cpy + l2_cpy == 0)
131 break; 128 break;
132 129
133 s2_start = l2 - l2_cpy;
134 s1_start = l1 - l1_cpy;
135
136 memcpy(dst, s1 + s1_start, l1_cpy);
137 memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy);
138
139 ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part, 130 ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part,
140 hsize + l1_cpy + l2_cpy, psinfo); 131 hsize + len, psinfo);
141 if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) 132 if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
142 pstore_new_entry = 1; 133 pstore_new_entry = 1;
143 l1 -= l1_cpy; 134
144 l2 -= l2_cpy; 135 total += hsize + len;
145 total += l1_cpy + l2_cpy;
146 part++; 136 part++;
147 } 137 }
148 if (in_nmi()) { 138 if (in_nmi()) {
diff --git a/include/linux/kmsg_dump.h b/include/linux/kmsg_dump.h
index 35f7237ec972..af4eb5a39d9a 100644
--- a/include/linux/kmsg_dump.h
+++ b/include/linux/kmsg_dump.h
@@ -21,6 +21,7 @@
21 * is passed to the kernel. 21 * is passed to the kernel.
22 */ 22 */
23enum kmsg_dump_reason { 23enum kmsg_dump_reason {
24 KMSG_DUMP_UNDEF,
24 KMSG_DUMP_PANIC, 25 KMSG_DUMP_PANIC,
25 KMSG_DUMP_OOPS, 26 KMSG_DUMP_OOPS,
26 KMSG_DUMP_EMERG, 27 KMSG_DUMP_EMERG,
@@ -31,23 +32,37 @@ enum kmsg_dump_reason {
31 32
32/** 33/**
33 * struct kmsg_dumper - kernel crash message dumper structure 34 * struct kmsg_dumper - kernel crash message dumper structure
34 * @dump: The callback which gets called on crashes. The buffer is passed
35 * as two sections, where s1 (length l1) contains the older
36 * messages and s2 (length l2) contains the newer.
37 * @list: Entry in the dumper list (private) 35 * @list: Entry in the dumper list (private)
36 * @dump: Call into dumping code which will retrieve the data with
37 * through the record iterator
38 * @max_reason: filter for highest reason number that should be dumped
38 * @registered: Flag that specifies if this is already registered 39 * @registered: Flag that specifies if this is already registered
39 */ 40 */
40struct kmsg_dumper { 41struct kmsg_dumper {
41 void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason,
42 const char *s1, unsigned long l1,
43 const char *s2, unsigned long l2);
44 struct list_head list; 42 struct list_head list;
45 int registered; 43 void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason);
44 enum kmsg_dump_reason max_reason;
45 bool active;
46 bool registered;
47
48 /* private state of the kmsg iterator */
49 u32 cur_idx;
50 u32 next_idx;
51 u64 cur_seq;
52 u64 next_seq;
46}; 53};
47 54
48#ifdef CONFIG_PRINTK 55#ifdef CONFIG_PRINTK
49void kmsg_dump(enum kmsg_dump_reason reason); 56void kmsg_dump(enum kmsg_dump_reason reason);
50 57
58bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
59 char *line, size_t size, size_t *len);
60
61bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
62 char *buf, size_t size, size_t *len);
63
64void kmsg_dump_rewind(struct kmsg_dumper *dumper);
65
51int kmsg_dump_register(struct kmsg_dumper *dumper); 66int kmsg_dump_register(struct kmsg_dumper *dumper);
52 67
53int kmsg_dump_unregister(struct kmsg_dumper *dumper); 68int kmsg_dump_unregister(struct kmsg_dumper *dumper);
@@ -56,6 +71,22 @@ static inline void kmsg_dump(enum kmsg_dump_reason reason)
56{ 71{
57} 72}
58 73
74bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
75 const char *line, size_t size, size_t *len)
76{
77 return false;
78}
79
80bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
81 char *buf, size_t size, size_t *len)
82{
83 return false;
84}
85
86void kmsg_dump_rewind(struct kmsg_dumper *dumper)
87{
88}
89
59static inline int kmsg_dump_register(struct kmsg_dumper *dumper) 90static inline int kmsg_dump_register(struct kmsg_dumper *dumper)
60{ 91{
61 return -EINVAL; 92 return -EINVAL;
diff --git a/kernel/printk.c b/kernel/printk.c
index f205c25c37e2..ceb4a2f775a1 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -909,7 +909,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
909 /* 909 /*
910 * Find first record that fits, including all following records, 910 * Find first record that fits, including all following records,
911 * into the user-provided buffer for this dump. 911 * into the user-provided buffer for this dump.
912 */ 912 */
913 seq = clear_seq; 913 seq = clear_seq;
914 idx = clear_idx; 914 idx = clear_idx;
915 while (seq < log_next_seq) { 915 while (seq < log_next_seq) {
@@ -919,6 +919,8 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
919 idx = log_next(idx); 919 idx = log_next(idx);
920 seq++; 920 seq++;
921 } 921 }
922
923 /* move first record forward until length fits into the buffer */
922 seq = clear_seq; 924 seq = clear_seq;
923 idx = clear_idx; 925 idx = clear_idx;
924 while (len > size && seq < log_next_seq) { 926 while (len > size && seq < log_next_seq) {
@@ -929,7 +931,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
929 seq++; 931 seq++;
930 } 932 }
931 933
932 /* last message in this dump */ 934 /* last message fitting into this dump */
933 next_seq = log_next_seq; 935 next_seq = log_next_seq;
934 936
935 len = 0; 937 len = 0;
@@ -2300,48 +2302,210 @@ module_param_named(always_kmsg_dump, always_kmsg_dump, bool, S_IRUGO | S_IWUSR);
2300 * kmsg_dump - dump kernel log to kernel message dumpers. 2302 * kmsg_dump - dump kernel log to kernel message dumpers.
2301 * @reason: the reason (oops, panic etc) for dumping 2303 * @reason: the reason (oops, panic etc) for dumping
2302 * 2304 *
2303 * Iterate through each of the dump devices and call the oops/panic 2305 * Call each of the registered dumper's dump() callback, which can
2304 * callbacks with the log buffer. 2306 * retrieve the kmsg records with kmsg_dump_get_line() or
2307 * kmsg_dump_get_buffer().
2305 */ 2308 */
2306void kmsg_dump(enum kmsg_dump_reason reason) 2309void kmsg_dump(enum kmsg_dump_reason reason)
2307{ 2310{
2308 u64 idx;
2309 struct kmsg_dumper *dumper; 2311 struct kmsg_dumper *dumper;
2310 const char *s1, *s2;
2311 unsigned long l1, l2;
2312 unsigned long flags; 2312 unsigned long flags;
2313 2313
2314 if ((reason > KMSG_DUMP_OOPS) && !always_kmsg_dump) 2314 if ((reason > KMSG_DUMP_OOPS) && !always_kmsg_dump)
2315 return; 2315 return;
2316 2316
2317 /* Theoretically, the log could move on after we do this, but 2317 rcu_read_lock();
2318 there's not a lot we can do about that. The new messages 2318 list_for_each_entry_rcu(dumper, &dump_list, list) {
2319 will overwrite the start of what we dump. */ 2319 if (dumper->max_reason && reason > dumper->max_reason)
2320 continue;
2321
2322 /* initialize iterator with data about the stored records */
2323 dumper->active = true;
2324
2325 raw_spin_lock_irqsave(&logbuf_lock, flags);
2326 dumper->cur_seq = clear_seq;
2327 dumper->cur_idx = clear_idx;
2328 dumper->next_seq = log_next_seq;
2329 dumper->next_idx = log_next_idx;
2330 raw_spin_unlock_irqrestore(&logbuf_lock, flags);
2331
2332 /* invoke dumper which will iterate over records */
2333 dumper->dump(dumper, reason);
2334
2335 /* reset iterator */
2336 dumper->active = false;
2337 }
2338 rcu_read_unlock();
2339}
2340
2341/**
2342 * kmsg_dump_get_line - retrieve one kmsg log line
2343 * @dumper: registered kmsg dumper
2344 * @syslog: include the "<4>" prefixes
2345 * @line: buffer to copy the line to
2346 * @size: maximum size of the buffer
2347 * @len: length of line placed into buffer
2348 *
2349 * Start at the beginning of the kmsg buffer, with the oldest kmsg
2350 * record, and copy one record into the provided buffer.
2351 *
2352 * Consecutive calls will return the next available record moving
2353 * towards the end of the buffer with the youngest messages.
2354 *
2355 * A return value of FALSE indicates that there are no more records to
2356 * read.
2357 */
2358bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
2359 char *line, size_t size, size_t *len)
2360{
2361 unsigned long flags;
2362 struct log *msg;
2363 size_t l = 0;
2364 bool ret = false;
2365
2366 if (!dumper->active)
2367 goto out;
2320 2368
2321 raw_spin_lock_irqsave(&logbuf_lock, flags); 2369 raw_spin_lock_irqsave(&logbuf_lock, flags);
2322 if (syslog_seq < log_first_seq) 2370 if (dumper->cur_seq < log_first_seq) {
2323 idx = syslog_idx; 2371 /* messages are gone, move to first available one */
2324 else 2372 dumper->cur_seq = log_first_seq;
2325 idx = log_first_idx; 2373 dumper->cur_idx = log_first_idx;
2374 }
2326 2375
2327 if (idx > log_next_idx) { 2376 /* last entry */
2328 s1 = log_buf; 2377 if (dumper->cur_seq >= log_next_seq) {
2329 l1 = log_next_idx; 2378 raw_spin_unlock_irqrestore(&logbuf_lock, flags);
2379 goto out;
2380 }
2330 2381
2331 s2 = log_buf + idx; 2382 msg = log_from_idx(dumper->cur_idx);
2332 l2 = log_buf_len - idx; 2383 l = msg_print_text(msg, syslog,
2333 } else { 2384 line, size);
2334 s1 = ""; 2385
2335 l1 = 0; 2386 dumper->cur_idx = log_next(dumper->cur_idx);
2387 dumper->cur_seq++;
2388 ret = true;
2389 raw_spin_unlock_irqrestore(&logbuf_lock, flags);
2390out:
2391 if (len)
2392 *len = l;
2393 return ret;
2394}
2395EXPORT_SYMBOL_GPL(kmsg_dump_get_line);
2396
2397/**
2398 * kmsg_dump_get_buffer - copy kmsg log lines
2399 * @dumper: registered kmsg dumper
2400 * @syslog: include the "<4>" prefixes
2401 * @line: buffer to copy the line to
2402 * @size: maximum size of the buffer
2403 * @len: length of line placed into buffer
2404 *
2405 * Start at the end of the kmsg buffer and fill the provided buffer
2406 * with as many of the the *youngest* kmsg records that fit into it.
2407 * If the buffer is large enough, all available kmsg records will be
2408 * copied with a single call.
2409 *
2410 * Consecutive calls will fill the buffer with the next block of
2411 * available older records, not including the earlier retrieved ones.
2412 *
2413 * A return value of FALSE indicates that there are no more records to
2414 * read.
2415 */
2416bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
2417 char *buf, size_t size, size_t *len)
2418{
2419 unsigned long flags;
2420 u64 seq;
2421 u32 idx;
2422 u64 next_seq;
2423 u32 next_idx;
2424 size_t l = 0;
2425 bool ret = false;
2426
2427 if (!dumper->active)
2428 goto out;
2429
2430 raw_spin_lock_irqsave(&logbuf_lock, flags);
2431 if (dumper->cur_seq < log_first_seq) {
2432 /* messages are gone, move to first available one */
2433 dumper->cur_seq = log_first_seq;
2434 dumper->cur_idx = log_first_idx;
2435 }
2436
2437 /* last entry */
2438 if (dumper->cur_seq >= dumper->next_seq) {
2439 raw_spin_unlock_irqrestore(&logbuf_lock, flags);
2440 goto out;
2441 }
2442
2443 /* calculate length of entire buffer */
2444 seq = dumper->cur_seq;
2445 idx = dumper->cur_idx;
2446 while (seq < dumper->next_seq) {
2447 struct log *msg = log_from_idx(idx);
2448
2449 l += msg_print_text(msg, true, NULL, 0);
2450 idx = log_next(idx);
2451 seq++;
2452 }
2453
2454 /* move first record forward until length fits into the buffer */
2455 seq = dumper->cur_seq;
2456 idx = dumper->cur_idx;
2457 while (l > size && seq < dumper->next_seq) {
2458 struct log *msg = log_from_idx(idx);
2336 2459
2337 s2 = log_buf + idx; 2460 l -= msg_print_text(msg, true, NULL, 0);
2338 l2 = log_next_idx - idx; 2461 idx = log_next(idx);
2462 seq++;
2339 } 2463 }
2464
2465 /* last message in next interation */
2466 next_seq = seq;
2467 next_idx = idx;
2468
2469 l = 0;
2470 while (seq < dumper->next_seq) {
2471 struct log *msg = log_from_idx(idx);
2472
2473 l += msg_print_text(msg, syslog,
2474 buf + l, size - l);
2475
2476 idx = log_next(idx);
2477 seq++;
2478 }
2479
2480 dumper->next_seq = next_seq;
2481 dumper->next_idx = next_idx;
2482 ret = true;
2340 raw_spin_unlock_irqrestore(&logbuf_lock, flags); 2483 raw_spin_unlock_irqrestore(&logbuf_lock, flags);
2484out:
2485 if (len)
2486 *len = l;
2487 return ret;
2488}
2489EXPORT_SYMBOL_GPL(kmsg_dump_get_buffer);
2341 2490
2342 rcu_read_lock(); 2491/**
2343 list_for_each_entry_rcu(dumper, &dump_list, list) 2492 * kmsg_dump_rewind - reset the interator
2344 dumper->dump(dumper, reason, s1, l1, s2, l2); 2493 * @dumper: registered kmsg dumper
2345 rcu_read_unlock(); 2494 *
2495 * Reset the dumper's iterator so that kmsg_dump_get_line() and
2496 * kmsg_dump_get_buffer() can be called again and used multiple
2497 * times within the same dumper.dump() callback.
2498 */
2499void kmsg_dump_rewind(struct kmsg_dumper *dumper)
2500{
2501 unsigned long flags;
2502
2503 raw_spin_lock_irqsave(&logbuf_lock, flags);
2504 dumper->cur_seq = clear_seq;
2505 dumper->cur_idx = clear_idx;
2506 dumper->next_seq = log_next_seq;
2507 dumper->next_idx = log_next_idx;
2508 raw_spin_unlock_irqrestore(&logbuf_lock, flags);
2346} 2509}
2510EXPORT_SYMBOL_GPL(kmsg_dump_rewind);
2347#endif 2511#endif