aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/printk.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/printk.c')
-rw-r--r--kernel/printk.c220
1 files changed, 192 insertions, 28 deletions
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