diff options
author | Kay Sievers <kay@vrfy.org> | 2012-06-15 08:07:51 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-06-15 17:53:59 -0400 |
commit | e2ae715d66bf4becfb85eb84b7150e23cf27df30 (patch) | |
tree | 206f08bc1f5138d1416c040f78c5b527ca07536d | |
parent | 1bd289d1e8e29c09be0e783a53f16a1dc80684be (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.c | 61 | ||||
-rw-r--r-- | arch/x86/platform/mrst/early_printk_mrst.c | 13 | ||||
-rw-r--r-- | drivers/mtd/mtdoops.c | 22 | ||||
-rw-r--r-- | fs/pstore/platform.c | 34 | ||||
-rw-r--r-- | include/linux/kmsg_dump.h | 45 | ||||
-rw-r--r-- | kernel/printk.c | 220 |
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 | ||
70 | static void oops_to_nvram(struct kmsg_dumper *dumper, | 70 | static 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 | ||
75 | static struct kmsg_dumper nvram_kmsg_dumper = { | 73 | static 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 | */ | ||
510 | static 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. */ | ||
545 | static 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() */ |
566 | static int nvram_compress(const void *in, void *out, size_t inlen, | 521 | static 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 | */ |
621 | static void oops_to_nvram(struct kmsg_dumper *dumper, | 576 | static 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; | |||
110 | static int dumper_registered; | 110 | static int dumper_registered; |
111 | 111 | ||
112 | static void dw_kmsg_dump(struct kmsg_dumper *dumper, | 112 | static 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 | ||
306 | static void mtdoops_do_dump(struct kmsg_dumper *dumper, | 306 | static 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 | */ |
96 | static void pstore_dump(struct kmsg_dumper *dumper, | 96 | static 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 | */ |
23 | enum kmsg_dump_reason { | 23 | enum 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 | */ |
40 | struct kmsg_dumper { | 41 | struct 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 |
49 | void kmsg_dump(enum kmsg_dump_reason reason); | 56 | void kmsg_dump(enum kmsg_dump_reason reason); |
50 | 57 | ||
58 | bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog, | ||
59 | char *line, size_t size, size_t *len); | ||
60 | |||
61 | bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog, | ||
62 | char *buf, size_t size, size_t *len); | ||
63 | |||
64 | void kmsg_dump_rewind(struct kmsg_dumper *dumper); | ||
65 | |||
51 | int kmsg_dump_register(struct kmsg_dumper *dumper); | 66 | int kmsg_dump_register(struct kmsg_dumper *dumper); |
52 | 67 | ||
53 | int kmsg_dump_unregister(struct kmsg_dumper *dumper); | 68 | int 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 | ||
74 | bool 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 | |||
80 | bool 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 | |||
86 | void kmsg_dump_rewind(struct kmsg_dumper *dumper) | ||
87 | { | ||
88 | } | ||
89 | |||
59 | static inline int kmsg_dump_register(struct kmsg_dumper *dumper) | 90 | static 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 | */ |
2306 | void kmsg_dump(enum kmsg_dump_reason reason) | 2309 | void 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 | */ | ||
2358 | bool 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); | ||
2390 | out: | ||
2391 | if (len) | ||
2392 | *len = l; | ||
2393 | return ret; | ||
2394 | } | ||
2395 | EXPORT_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 | */ | ||
2416 | bool 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); |
2484 | out: | ||
2485 | if (len) | ||
2486 | *len = l; | ||
2487 | return ret; | ||
2488 | } | ||
2489 | EXPORT_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 | */ | ||
2499 | void 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 | } |
2510 | EXPORT_SYMBOL_GPL(kmsg_dump_rewind); | ||
2347 | #endif | 2511 | #endif |