diff options
Diffstat (limited to 'kernel/printk.c')
| -rw-r--r-- | kernel/printk.c | 241 |
1 files changed, 208 insertions, 33 deletions
diff --git a/kernel/printk.c b/kernel/printk.c index 32462d2b364a..a2276b916769 100644 --- a/kernel/printk.c +++ b/kernel/printk.c | |||
| @@ -227,10 +227,10 @@ static u32 clear_idx; | |||
| 227 | #define LOG_LINE_MAX 1024 | 227 | #define LOG_LINE_MAX 1024 |
| 228 | 228 | ||
| 229 | /* record buffer */ | 229 | /* record buffer */ |
| 230 | #if !defined(CONFIG_64BIT) || defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) | 230 | #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) |
| 231 | #define LOG_ALIGN 4 | 231 | #define LOG_ALIGN 4 |
| 232 | #else | 232 | #else |
| 233 | #define LOG_ALIGN 8 | 233 | #define LOG_ALIGN __alignof__(struct log) |
| 234 | #endif | 234 | #endif |
| 235 | #define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT) | 235 | #define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT) |
| 236 | static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN); | 236 | static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN); |
| @@ -414,7 +414,9 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf, | |||
| 414 | if (!user) | 414 | if (!user) |
| 415 | return -EBADF; | 415 | return -EBADF; |
| 416 | 416 | ||
| 417 | mutex_lock(&user->lock); | 417 | ret = mutex_lock_interruptible(&user->lock); |
| 418 | if (ret) | ||
| 419 | return ret; | ||
| 418 | raw_spin_lock(&logbuf_lock); | 420 | raw_spin_lock(&logbuf_lock); |
| 419 | while (user->seq == log_next_seq) { | 421 | while (user->seq == log_next_seq) { |
| 420 | if (file->f_flags & O_NONBLOCK) { | 422 | if (file->f_flags & O_NONBLOCK) { |
| @@ -878,7 +880,9 @@ static int syslog_print(char __user *buf, int size) | |||
| 878 | syslog_seq++; | 880 | syslog_seq++; |
| 879 | raw_spin_unlock_irq(&logbuf_lock); | 881 | raw_spin_unlock_irq(&logbuf_lock); |
| 880 | 882 | ||
| 881 | if (len > 0 && copy_to_user(buf, text, len)) | 883 | if (len > size) |
| 884 | len = -EINVAL; | ||
| 885 | else if (len > 0 && copy_to_user(buf, text, len)) | ||
| 882 | len = -EFAULT; | 886 | len = -EFAULT; |
| 883 | 887 | ||
| 884 | kfree(text); | 888 | kfree(text); |
| @@ -909,7 +913,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear) | |||
| 909 | /* | 913 | /* |
| 910 | * Find first record that fits, including all following records, | 914 | * Find first record that fits, including all following records, |
| 911 | * into the user-provided buffer for this dump. | 915 | * into the user-provided buffer for this dump. |
| 912 | */ | 916 | */ |
| 913 | seq = clear_seq; | 917 | seq = clear_seq; |
| 914 | idx = clear_idx; | 918 | idx = clear_idx; |
| 915 | while (seq < log_next_seq) { | 919 | while (seq < log_next_seq) { |
| @@ -919,6 +923,8 @@ static int syslog_print_all(char __user *buf, int size, bool clear) | |||
| 919 | idx = log_next(idx); | 923 | idx = log_next(idx); |
| 920 | seq++; | 924 | seq++; |
| 921 | } | 925 | } |
| 926 | |||
| 927 | /* move first record forward until length fits into the buffer */ | ||
| 922 | seq = clear_seq; | 928 | seq = clear_seq; |
| 923 | idx = clear_idx; | 929 | idx = clear_idx; |
| 924 | while (len > size && seq < log_next_seq) { | 930 | while (len > size && seq < log_next_seq) { |
| @@ -929,7 +935,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear) | |||
| 929 | seq++; | 935 | seq++; |
| 930 | } | 936 | } |
| 931 | 937 | ||
| 932 | /* last message in this dump */ | 938 | /* last message fitting into this dump */ |
| 933 | next_seq = log_next_seq; | 939 | next_seq = log_next_seq; |
| 934 | 940 | ||
| 935 | len = 0; | 941 | len = 0; |
| @@ -974,6 +980,7 @@ int do_syslog(int type, char __user *buf, int len, bool from_file) | |||
| 974 | { | 980 | { |
| 975 | bool clear = false; | 981 | bool clear = false; |
| 976 | static int saved_console_loglevel = -1; | 982 | static int saved_console_loglevel = -1; |
| 983 | static DEFINE_MUTEX(syslog_mutex); | ||
| 977 | int error; | 984 | int error; |
| 978 | 985 | ||
| 979 | error = check_syslog_permissions(type, from_file); | 986 | error = check_syslog_permissions(type, from_file); |
| @@ -1000,11 +1007,17 @@ int do_syslog(int type, char __user *buf, int len, bool from_file) | |||
| 1000 | error = -EFAULT; | 1007 | error = -EFAULT; |
| 1001 | goto out; | 1008 | goto out; |
| 1002 | } | 1009 | } |
| 1010 | error = mutex_lock_interruptible(&syslog_mutex); | ||
| 1011 | if (error) | ||
| 1012 | goto out; | ||
| 1003 | error = wait_event_interruptible(log_wait, | 1013 | error = wait_event_interruptible(log_wait, |
| 1004 | syslog_seq != log_next_seq); | 1014 | syslog_seq != log_next_seq); |
| 1005 | if (error) | 1015 | if (error) { |
| 1016 | mutex_unlock(&syslog_mutex); | ||
| 1006 | goto out; | 1017 | goto out; |
| 1018 | } | ||
| 1007 | error = syslog_print(buf, len); | 1019 | error = syslog_print(buf, len); |
| 1020 | mutex_unlock(&syslog_mutex); | ||
| 1008 | break; | 1021 | break; |
| 1009 | /* Read/clear last kernel messages */ | 1022 | /* Read/clear last kernel messages */ |
| 1010 | case SYSLOG_ACTION_READ_CLEAR: | 1023 | case SYSLOG_ACTION_READ_CLEAR: |
| @@ -2300,48 +2313,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. | 2313 | * kmsg_dump - dump kernel log to kernel message dumpers. |
| 2301 | * @reason: the reason (oops, panic etc) for dumping | 2314 | * @reason: the reason (oops, panic etc) for dumping |
| 2302 | * | 2315 | * |
| 2303 | * Iterate through each of the dump devices and call the oops/panic | 2316 | * Call each of the registered dumper's dump() callback, which can |
| 2304 | * callbacks with the log buffer. | 2317 | * retrieve the kmsg records with kmsg_dump_get_line() or |
| 2318 | * kmsg_dump_get_buffer(). | ||
| 2305 | */ | 2319 | */ |
| 2306 | void kmsg_dump(enum kmsg_dump_reason reason) | 2320 | void kmsg_dump(enum kmsg_dump_reason reason) |
| 2307 | { | 2321 | { |
| 2308 | u64 idx; | ||
| 2309 | struct kmsg_dumper *dumper; | 2322 | struct kmsg_dumper *dumper; |
| 2310 | const char *s1, *s2; | ||
| 2311 | unsigned long l1, l2; | ||
| 2312 | unsigned long flags; | 2323 | unsigned long flags; |
| 2313 | 2324 | ||
| 2314 | if ((reason > KMSG_DUMP_OOPS) && !always_kmsg_dump) | 2325 | if ((reason > KMSG_DUMP_OOPS) && !always_kmsg_dump) |
| 2315 | return; | 2326 | return; |
| 2316 | 2327 | ||
| 2317 | /* Theoretically, the log could move on after we do this, but | 2328 | rcu_read_lock(); |
| 2318 | there's not a lot we can do about that. The new messages | 2329 | list_for_each_entry_rcu(dumper, &dump_list, list) { |
| 2319 | will overwrite the start of what we dump. */ | 2330 | if (dumper->max_reason && reason > dumper->max_reason) |
| 2331 | continue; | ||
| 2332 | |||
| 2333 | /* initialize iterator with data about the stored records */ | ||
| 2334 | dumper->active = true; | ||
| 2335 | |||
| 2336 | raw_spin_lock_irqsave(&logbuf_lock, flags); | ||
| 2337 | dumper->cur_seq = clear_seq; | ||
| 2338 | dumper->cur_idx = clear_idx; | ||
| 2339 | dumper->next_seq = log_next_seq; | ||
| 2340 | dumper->next_idx = log_next_idx; | ||
| 2341 | raw_spin_unlock_irqrestore(&logbuf_lock, flags); | ||
| 2342 | |||
| 2343 | /* invoke dumper which will iterate over records */ | ||
| 2344 | dumper->dump(dumper, reason); | ||
| 2345 | |||
| 2346 | /* reset iterator */ | ||
| 2347 | dumper->active = false; | ||
| 2348 | } | ||
| 2349 | rcu_read_unlock(); | ||
| 2350 | } | ||
| 2351 | |||
| 2352 | /** | ||
| 2353 | * kmsg_dump_get_line - retrieve one kmsg log line | ||
| 2354 | * @dumper: registered kmsg dumper | ||
| 2355 | * @syslog: include the "<4>" prefixes | ||
| 2356 | * @line: buffer to copy the line to | ||
| 2357 | * @size: maximum size of the buffer | ||
| 2358 | * @len: length of line placed into buffer | ||
| 2359 | * | ||
| 2360 | * Start at the beginning of the kmsg buffer, with the oldest kmsg | ||
| 2361 | * record, and copy one record into the provided buffer. | ||
| 2362 | * | ||
| 2363 | * Consecutive calls will return the next available record moving | ||
| 2364 | * towards the end of the buffer with the youngest messages. | ||
| 2365 | * | ||
| 2366 | * A return value of FALSE indicates that there are no more records to | ||
| 2367 | * read. | ||
| 2368 | */ | ||
| 2369 | bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog, | ||
| 2370 | char *line, size_t size, size_t *len) | ||
| 2371 | { | ||
| 2372 | unsigned long flags; | ||
| 2373 | struct log *msg; | ||
| 2374 | size_t l = 0; | ||
| 2375 | bool ret = false; | ||
| 2376 | |||
| 2377 | if (!dumper->active) | ||
| 2378 | goto out; | ||
| 2320 | 2379 | ||
| 2321 | raw_spin_lock_irqsave(&logbuf_lock, flags); | 2380 | raw_spin_lock_irqsave(&logbuf_lock, flags); |
| 2322 | if (syslog_seq < log_first_seq) | 2381 | if (dumper->cur_seq < log_first_seq) { |
| 2323 | idx = syslog_idx; | 2382 | /* messages are gone, move to first available one */ |
| 2324 | else | 2383 | dumper->cur_seq = log_first_seq; |
| 2325 | idx = log_first_idx; | 2384 | dumper->cur_idx = log_first_idx; |
| 2385 | } | ||
| 2386 | |||
| 2387 | /* last entry */ | ||
| 2388 | if (dumper->cur_seq >= log_next_seq) { | ||
| 2389 | raw_spin_unlock_irqrestore(&logbuf_lock, flags); | ||
| 2390 | goto out; | ||
| 2391 | } | ||
| 2326 | 2392 | ||
| 2327 | if (idx > log_next_idx) { | 2393 | msg = log_from_idx(dumper->cur_idx); |
| 2328 | s1 = log_buf; | 2394 | l = msg_print_text(msg, syslog, |
| 2329 | l1 = log_next_idx; | 2395 | line, size); |
| 2330 | 2396 | ||
| 2331 | s2 = log_buf + idx; | 2397 | dumper->cur_idx = log_next(dumper->cur_idx); |
| 2332 | l2 = log_buf_len - idx; | 2398 | dumper->cur_seq++; |
| 2333 | } else { | 2399 | ret = true; |
| 2334 | s1 = ""; | 2400 | raw_spin_unlock_irqrestore(&logbuf_lock, flags); |
| 2335 | l1 = 0; | 2401 | out: |
| 2402 | if (len) | ||
| 2403 | *len = l; | ||
| 2404 | return ret; | ||
| 2405 | } | ||
| 2406 | EXPORT_SYMBOL_GPL(kmsg_dump_get_line); | ||
| 2407 | |||
| 2408 | /** | ||
| 2409 | * kmsg_dump_get_buffer - copy kmsg log lines | ||
| 2410 | * @dumper: registered kmsg dumper | ||
| 2411 | * @syslog: include the "<4>" prefixes | ||
| 2412 | * @line: buffer to copy the line to | ||
| 2413 | * @size: maximum size of the buffer | ||
| 2414 | * @len: length of line placed into buffer | ||
| 2415 | * | ||
| 2416 | * Start at the end of the kmsg buffer and fill the provided buffer | ||
| 2417 | * with as many of the the *youngest* kmsg records that fit into it. | ||
| 2418 | * If the buffer is large enough, all available kmsg records will be | ||
| 2419 | * copied with a single call. | ||
| 2420 | * | ||
| 2421 | * Consecutive calls will fill the buffer with the next block of | ||
| 2422 | * available older records, not including the earlier retrieved ones. | ||
| 2423 | * | ||
| 2424 | * A return value of FALSE indicates that there are no more records to | ||
| 2425 | * read. | ||
| 2426 | */ | ||
| 2427 | bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog, | ||
| 2428 | char *buf, size_t size, size_t *len) | ||
| 2429 | { | ||
| 2430 | unsigned long flags; | ||
| 2431 | u64 seq; | ||
| 2432 | u32 idx; | ||
| 2433 | u64 next_seq; | ||
| 2434 | u32 next_idx; | ||
| 2435 | size_t l = 0; | ||
| 2436 | bool ret = false; | ||
| 2437 | |||
| 2438 | if (!dumper->active) | ||
| 2439 | goto out; | ||
| 2440 | |||
| 2441 | raw_spin_lock_irqsave(&logbuf_lock, flags); | ||
| 2442 | if (dumper->cur_seq < log_first_seq) { | ||
| 2443 | /* messages are gone, move to first available one */ | ||
| 2444 | dumper->cur_seq = log_first_seq; | ||
| 2445 | dumper->cur_idx = log_first_idx; | ||
| 2446 | } | ||
| 2447 | |||
| 2448 | /* last entry */ | ||
| 2449 | if (dumper->cur_seq >= dumper->next_seq) { | ||
| 2450 | raw_spin_unlock_irqrestore(&logbuf_lock, flags); | ||
| 2451 | goto out; | ||
| 2452 | } | ||
| 2453 | |||
| 2454 | /* calculate length of entire buffer */ | ||
| 2455 | seq = dumper->cur_seq; | ||
| 2456 | idx = dumper->cur_idx; | ||
| 2457 | while (seq < dumper->next_seq) { | ||
| 2458 | struct log *msg = log_from_idx(idx); | ||
| 2459 | |||
| 2460 | l += msg_print_text(msg, true, NULL, 0); | ||
| 2461 | idx = log_next(idx); | ||
| 2462 | seq++; | ||
| 2463 | } | ||
| 2336 | 2464 | ||
| 2337 | s2 = log_buf + idx; | 2465 | /* move first record forward until length fits into the buffer */ |
| 2338 | l2 = log_next_idx - idx; | 2466 | seq = dumper->cur_seq; |
| 2467 | idx = dumper->cur_idx; | ||
| 2468 | while (l > size && seq < dumper->next_seq) { | ||
| 2469 | struct log *msg = log_from_idx(idx); | ||
| 2470 | |||
| 2471 | l -= msg_print_text(msg, true, NULL, 0); | ||
| 2472 | idx = log_next(idx); | ||
| 2473 | seq++; | ||
| 2474 | } | ||
| 2475 | |||
| 2476 | /* last message in next interation */ | ||
| 2477 | next_seq = seq; | ||
| 2478 | next_idx = idx; | ||
| 2479 | |||
| 2480 | l = 0; | ||
| 2481 | while (seq < dumper->next_seq) { | ||
| 2482 | struct log *msg = log_from_idx(idx); | ||
| 2483 | |||
| 2484 | l += msg_print_text(msg, syslog, | ||
| 2485 | buf + l, size - l); | ||
| 2486 | |||
| 2487 | idx = log_next(idx); | ||
| 2488 | seq++; | ||
| 2339 | } | 2489 | } |
| 2490 | |||
| 2491 | dumper->next_seq = next_seq; | ||
| 2492 | dumper->next_idx = next_idx; | ||
| 2493 | ret = true; | ||
| 2340 | raw_spin_unlock_irqrestore(&logbuf_lock, flags); | 2494 | raw_spin_unlock_irqrestore(&logbuf_lock, flags); |
| 2495 | out: | ||
| 2496 | if (len) | ||
| 2497 | *len = l; | ||
| 2498 | return ret; | ||
| 2499 | } | ||
| 2500 | EXPORT_SYMBOL_GPL(kmsg_dump_get_buffer); | ||
| 2341 | 2501 | ||
| 2342 | rcu_read_lock(); | 2502 | /** |
| 2343 | list_for_each_entry_rcu(dumper, &dump_list, list) | 2503 | * kmsg_dump_rewind - reset the interator |
| 2344 | dumper->dump(dumper, reason, s1, l1, s2, l2); | 2504 | * @dumper: registered kmsg dumper |
| 2345 | rcu_read_unlock(); | 2505 | * |
| 2506 | * Reset the dumper's iterator so that kmsg_dump_get_line() and | ||
| 2507 | * kmsg_dump_get_buffer() can be called again and used multiple | ||
| 2508 | * times within the same dumper.dump() callback. | ||
| 2509 | */ | ||
| 2510 | void kmsg_dump_rewind(struct kmsg_dumper *dumper) | ||
| 2511 | { | ||
| 2512 | unsigned long flags; | ||
| 2513 | |||
| 2514 | raw_spin_lock_irqsave(&logbuf_lock, flags); | ||
| 2515 | dumper->cur_seq = clear_seq; | ||
| 2516 | dumper->cur_idx = clear_idx; | ||
| 2517 | dumper->next_seq = log_next_seq; | ||
| 2518 | dumper->next_idx = log_next_idx; | ||
| 2519 | raw_spin_unlock_irqrestore(&logbuf_lock, flags); | ||
| 2346 | } | 2520 | } |
| 2521 | EXPORT_SYMBOL_GPL(kmsg_dump_rewind); | ||
| 2347 | #endif | 2522 | #endif |
