diff options
Diffstat (limited to 'kernel/printk.c')
-rw-r--r-- | kernel/printk.c | 220 |
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 | */ |
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 |