.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Persistent Storage - platform driver interface parts. |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2007-2008 Google, Inc. |
---|
5 | 6 | * Copyright (C) 2010 Intel Corporation <tony.luck@intel.com> |
---|
6 | | - * |
---|
7 | | - * This program is free software; you can redistribute it and/or modify |
---|
8 | | - * it under the terms of the GNU General Public License version 2 as |
---|
9 | | - * published by the Free Software Foundation. |
---|
10 | | - * |
---|
11 | | - * This program is distributed in the hope that it will be useful, |
---|
12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
14 | | - * GNU General Public License for more details. |
---|
15 | | - * |
---|
16 | | - * You should have received a copy of the GNU General Public License |
---|
17 | | - * along with this program; if not, write to the Free Software |
---|
18 | | - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
---|
19 | 7 | */ |
---|
20 | 8 | |
---|
21 | 9 | #define pr_fmt(fmt) "pstore: " fmt |
---|
.. | .. |
---|
56 | 44 | module_param_named(update_ms, pstore_update_ms, int, 0600); |
---|
57 | 45 | MODULE_PARM_DESC(update_ms, "milliseconds before pstore updates its content " |
---|
58 | 46 | "(default is -1, which means runtime updates are disabled; " |
---|
59 | | - "enabling this option is not safe, it may lead to further " |
---|
| 47 | + "enabling this option may not be safe; it may lead to further " |
---|
60 | 48 | "corruption on Oopses)"); |
---|
| 49 | + |
---|
| 50 | +/* Names should be in the same order as the enum pstore_type_id */ |
---|
| 51 | +static const char * const pstore_type_names[] = { |
---|
| 52 | + "dmesg", |
---|
| 53 | + "mce", |
---|
| 54 | + "console", |
---|
| 55 | + "ftrace", |
---|
| 56 | + "rtas", |
---|
| 57 | + "powerpc-ofw", |
---|
| 58 | + "powerpc-common", |
---|
| 59 | + "pmsg", |
---|
| 60 | + "powerpc-opal", |
---|
| 61 | +#ifdef CONFIG_PSTORE_BOOT_LOG |
---|
| 62 | + "boot-log", |
---|
| 63 | +#endif |
---|
| 64 | +}; |
---|
61 | 65 | |
---|
62 | 66 | static int pstore_new_entry; |
---|
63 | 67 | |
---|
.. | .. |
---|
68 | 72 | static DECLARE_WORK(pstore_work, pstore_dowork); |
---|
69 | 73 | |
---|
70 | 74 | /* |
---|
71 | | - * pstore_lock just protects "psinfo" during |
---|
72 | | - * calls to pstore_register() |
---|
| 75 | + * psinfo_lock protects "psinfo" during calls to |
---|
| 76 | + * pstore_register(), pstore_unregister(), and |
---|
| 77 | + * the filesystem mount/unmount routines. |
---|
73 | 78 | */ |
---|
74 | | -static DEFINE_SPINLOCK(pstore_lock); |
---|
| 79 | +static DEFINE_MUTEX(psinfo_lock); |
---|
75 | 80 | struct pstore_info *psinfo; |
---|
76 | 81 | |
---|
77 | 82 | static char *backend; |
---|
| 83 | +module_param(backend, charp, 0444); |
---|
| 84 | +MODULE_PARM_DESC(backend, "specific backend to use"); |
---|
| 85 | + |
---|
78 | 86 | static char *compress = |
---|
79 | 87 | #ifdef CONFIG_PSTORE_COMPRESS_DEFAULT |
---|
80 | 88 | CONFIG_PSTORE_COMPRESS_DEFAULT; |
---|
81 | 89 | #else |
---|
82 | 90 | NULL; |
---|
83 | 91 | #endif |
---|
| 92 | +module_param(compress, charp, 0444); |
---|
| 93 | +MODULE_PARM_DESC(compress, "compression to use"); |
---|
84 | 94 | |
---|
85 | 95 | /* Compression parameters */ |
---|
86 | 96 | static struct crypto_comp *tfm; |
---|
.. | .. |
---|
104 | 114 | /* Tag each group of saved records with a sequence number */ |
---|
105 | 115 | static int oopscount; |
---|
106 | 116 | |
---|
107 | | -static const char *get_reason_str(enum kmsg_dump_reason reason) |
---|
| 117 | +const char *pstore_type_to_name(enum pstore_type_id type) |
---|
108 | 118 | { |
---|
109 | | - switch (reason) { |
---|
110 | | - case KMSG_DUMP_PANIC: |
---|
111 | | - return "Panic"; |
---|
112 | | - case KMSG_DUMP_OOPS: |
---|
113 | | - return "Oops"; |
---|
114 | | - case KMSG_DUMP_EMERG: |
---|
115 | | - return "Emergency"; |
---|
116 | | - case KMSG_DUMP_RESTART: |
---|
117 | | - return "Restart"; |
---|
118 | | - case KMSG_DUMP_HALT: |
---|
119 | | - return "Halt"; |
---|
120 | | - case KMSG_DUMP_POWEROFF: |
---|
121 | | - return "Poweroff"; |
---|
122 | | - default: |
---|
123 | | - return "Unknown"; |
---|
| 119 | + BUILD_BUG_ON(ARRAY_SIZE(pstore_type_names) != PSTORE_TYPE_MAX); |
---|
| 120 | + |
---|
| 121 | + if (WARN_ON_ONCE(type >= PSTORE_TYPE_MAX)) |
---|
| 122 | + return "unknown"; |
---|
| 123 | + |
---|
| 124 | + return pstore_type_names[type]; |
---|
| 125 | +} |
---|
| 126 | +EXPORT_SYMBOL_GPL(pstore_type_to_name); |
---|
| 127 | + |
---|
| 128 | +enum pstore_type_id pstore_name_to_type(const char *name) |
---|
| 129 | +{ |
---|
| 130 | + int i; |
---|
| 131 | + |
---|
| 132 | + for (i = 0; i < PSTORE_TYPE_MAX; i++) { |
---|
| 133 | + if (!strcmp(pstore_type_names[i], name)) |
---|
| 134 | + return i; |
---|
124 | 135 | } |
---|
| 136 | + |
---|
| 137 | + return PSTORE_TYPE_MAX; |
---|
| 138 | +} |
---|
| 139 | +EXPORT_SYMBOL_GPL(pstore_name_to_type); |
---|
| 140 | + |
---|
| 141 | +static void pstore_timer_kick(void) |
---|
| 142 | +{ |
---|
| 143 | + if (pstore_update_ms < 0) |
---|
| 144 | + return; |
---|
| 145 | + |
---|
| 146 | + mod_timer(&pstore_timer, jiffies + msecs_to_jiffies(pstore_update_ms)); |
---|
125 | 147 | } |
---|
126 | 148 | |
---|
127 | 149 | /* |
---|
.. | .. |
---|
262 | 284 | return outlen; |
---|
263 | 285 | } |
---|
264 | 286 | |
---|
265 | | -static int pstore_decompress(void *in, void *out, |
---|
266 | | - unsigned int inlen, unsigned int outlen) |
---|
267 | | -{ |
---|
268 | | - int ret; |
---|
269 | | - |
---|
270 | | - ret = crypto_comp_decompress(tfm, in, inlen, out, &outlen); |
---|
271 | | - if (ret) { |
---|
272 | | - pr_err("crypto_comp_decompress failed, ret = %d!\n", ret); |
---|
273 | | - return ret; |
---|
274 | | - } |
---|
275 | | - |
---|
276 | | - return outlen; |
---|
277 | | -} |
---|
278 | | - |
---|
279 | 287 | static void allocate_buf_for_compression(void) |
---|
280 | 288 | { |
---|
281 | 289 | struct crypto_comp *ctx; |
---|
.. | .. |
---|
322 | 330 | big_oops_buf_sz = size; |
---|
323 | 331 | big_oops_buf = buf; |
---|
324 | 332 | |
---|
325 | | - pr_info("Using compression: %s\n", zbackend->name); |
---|
| 333 | + pr_info("Using crash dump compression: %s\n", zbackend->name); |
---|
326 | 334 | } |
---|
327 | 335 | |
---|
328 | 336 | static void free_buf_for_compression(void) |
---|
.. | .. |
---|
374 | 382 | } |
---|
375 | 383 | |
---|
376 | 384 | /* |
---|
377 | | - * callback from kmsg_dump. (s2,l2) has the most recently |
---|
378 | | - * written bytes, older bytes are in (s1,l1). Save as much |
---|
379 | | - * as we can from the end of the buffer. |
---|
| 385 | + * callback from kmsg_dump. Save as much as we can (up to kmsg_bytes) from the |
---|
| 386 | + * end of the buffer. |
---|
380 | 387 | */ |
---|
381 | 388 | static void pstore_dump(struct kmsg_dumper *dumper, |
---|
382 | 389 | enum kmsg_dump_reason reason) |
---|
.. | .. |
---|
386 | 393 | unsigned int part = 1; |
---|
387 | 394 | int ret; |
---|
388 | 395 | |
---|
389 | | - why = get_reason_str(reason); |
---|
| 396 | + why = kmsg_dump_reason_str(reason); |
---|
390 | 397 | |
---|
391 | 398 | if (down_trylock(&psinfo->buf_lock)) { |
---|
392 | 399 | /* Failed to acquire lock: give up if we cannot wait. */ |
---|
.. | .. |
---|
452 | 459 | } |
---|
453 | 460 | |
---|
454 | 461 | ret = psinfo->write(&record); |
---|
455 | | - if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) |
---|
| 462 | + if (ret == 0 && reason == KMSG_DUMP_OOPS) { |
---|
456 | 463 | pstore_new_entry = 1; |
---|
| 464 | + pstore_timer_kick(); |
---|
| 465 | + } |
---|
457 | 466 | |
---|
458 | 467 | total += record.size; |
---|
459 | 468 | part++; |
---|
.. | .. |
---|
484 | 493 | { |
---|
485 | 494 | struct pstore_record record; |
---|
486 | 495 | |
---|
| 496 | + if (!c) |
---|
| 497 | + return; |
---|
| 498 | + |
---|
487 | 499 | pstore_record_init(&record, psinfo); |
---|
488 | 500 | record.type = PSTORE_TYPE_CONSOLE; |
---|
489 | 501 | |
---|
.. | .. |
---|
493 | 505 | } |
---|
494 | 506 | |
---|
495 | 507 | static struct console pstore_console = { |
---|
496 | | - .name = "pstore", |
---|
497 | 508 | .write = pstore_console_write, |
---|
498 | | -#ifdef CON_PSTORE |
---|
499 | | - .flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME | CON_PSTORE, |
---|
500 | | -#else |
---|
501 | | - .flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME, |
---|
502 | | -#endif |
---|
503 | 509 | .index = -1, |
---|
504 | 510 | }; |
---|
505 | 511 | |
---|
506 | 512 | static void pstore_register_console(void) |
---|
507 | 513 | { |
---|
| 514 | + /* Show which backend is going to get console writes. */ |
---|
| 515 | + strscpy(pstore_console.name, psinfo->name, |
---|
| 516 | + sizeof(pstore_console.name)); |
---|
| 517 | + /* |
---|
| 518 | + * Always initialize flags here since prior unregister_console() |
---|
| 519 | + * calls may have changed settings (specifically CON_ENABLED). |
---|
| 520 | + */ |
---|
| 521 | + pstore_console.flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME; |
---|
508 | 522 | register_console(&pstore_console); |
---|
509 | 523 | } |
---|
510 | 524 | |
---|
.. | .. |
---|
549 | 563 | */ |
---|
550 | 564 | int pstore_register(struct pstore_info *psi) |
---|
551 | 565 | { |
---|
552 | | - struct module *owner = psi->owner; |
---|
553 | | - |
---|
554 | 566 | if (backend && strcmp(backend, psi->name)) { |
---|
555 | 567 | pr_warn("ignoring unexpected backend '%s'\n", psi->name); |
---|
556 | 568 | return -EPERM; |
---|
.. | .. |
---|
570 | 582 | return -EINVAL; |
---|
571 | 583 | } |
---|
572 | 584 | |
---|
573 | | - spin_lock(&pstore_lock); |
---|
| 585 | + mutex_lock(&psinfo_lock); |
---|
574 | 586 | if (psinfo) { |
---|
575 | 587 | pr_warn("backend '%s' already loaded: ignoring '%s'\n", |
---|
576 | 588 | psinfo->name, psi->name); |
---|
577 | | - spin_unlock(&pstore_lock); |
---|
| 589 | + mutex_unlock(&psinfo_lock); |
---|
578 | 590 | return -EBUSY; |
---|
579 | 591 | } |
---|
580 | 592 | |
---|
.. | .. |
---|
583 | 595 | psinfo = psi; |
---|
584 | 596 | mutex_init(&psinfo->read_mutex); |
---|
585 | 597 | sema_init(&psinfo->buf_lock, 1); |
---|
586 | | - spin_unlock(&pstore_lock); |
---|
587 | | - |
---|
588 | | - if (owner && !try_module_get(owner)) { |
---|
589 | | - psinfo = NULL; |
---|
590 | | - return -EINVAL; |
---|
591 | | - } |
---|
592 | 598 | |
---|
593 | 599 | if (psi->flags & PSTORE_FLAGS_DMESG) |
---|
594 | 600 | allocate_buf_for_compression(); |
---|
595 | 601 | |
---|
596 | | - if (pstore_is_mounted()) |
---|
597 | | - pstore_get_records(0); |
---|
| 602 | + pstore_get_records(0); |
---|
598 | 603 | |
---|
599 | | - if (psi->flags & PSTORE_FLAGS_DMESG) |
---|
| 604 | + if (psi->flags & PSTORE_FLAGS_DMESG) { |
---|
| 605 | + pstore_dumper.max_reason = psinfo->max_reason; |
---|
600 | 606 | pstore_register_kmsg(); |
---|
| 607 | + } |
---|
601 | 608 | if (psi->flags & PSTORE_FLAGS_CONSOLE) |
---|
602 | 609 | pstore_register_console(); |
---|
603 | 610 | if (psi->flags & PSTORE_FLAGS_FTRACE) |
---|
.. | .. |
---|
606 | 613 | pstore_register_pmsg(); |
---|
607 | 614 | |
---|
608 | 615 | /* Start watching for new records, if desired. */ |
---|
609 | | - if (pstore_update_ms >= 0) { |
---|
610 | | - pstore_timer.expires = jiffies + |
---|
611 | | - msecs_to_jiffies(pstore_update_ms); |
---|
612 | | - add_timer(&pstore_timer); |
---|
613 | | - } |
---|
| 616 | + pstore_timer_kick(); |
---|
614 | 617 | |
---|
615 | 618 | /* |
---|
616 | 619 | * Update the module parameter backend, so it is visible |
---|
617 | 620 | * through /sys/module/pstore/parameters/backend |
---|
618 | 621 | */ |
---|
619 | | - backend = psi->name; |
---|
| 622 | + backend = kstrdup(psi->name, GFP_KERNEL); |
---|
620 | 623 | |
---|
621 | 624 | pr_info("Registered %s as persistent store backend\n", psi->name); |
---|
622 | 625 | |
---|
623 | | - module_put(owner); |
---|
624 | | - |
---|
| 626 | + mutex_unlock(&psinfo_lock); |
---|
625 | 627 | return 0; |
---|
626 | 628 | } |
---|
627 | 629 | EXPORT_SYMBOL_GPL(pstore_register); |
---|
628 | 630 | |
---|
629 | 631 | void pstore_unregister(struct pstore_info *psi) |
---|
630 | 632 | { |
---|
631 | | - /* Stop timer and make sure all work has finished. */ |
---|
632 | | - pstore_update_ms = -1; |
---|
633 | | - del_timer_sync(&pstore_timer); |
---|
634 | | - flush_work(&pstore_work); |
---|
| 633 | + /* It's okay to unregister nothing. */ |
---|
| 634 | + if (!psi) |
---|
| 635 | + return; |
---|
635 | 636 | |
---|
| 637 | + mutex_lock(&psinfo_lock); |
---|
| 638 | + |
---|
| 639 | + /* Only one backend can be registered at a time. */ |
---|
| 640 | + if (WARN_ON(psi != psinfo)) { |
---|
| 641 | + mutex_unlock(&psinfo_lock); |
---|
| 642 | + return; |
---|
| 643 | + } |
---|
| 644 | + |
---|
| 645 | + /* Unregister all callbacks. */ |
---|
636 | 646 | if (psi->flags & PSTORE_FLAGS_PMSG) |
---|
637 | 647 | pstore_unregister_pmsg(); |
---|
638 | 648 | if (psi->flags & PSTORE_FLAGS_FTRACE) |
---|
.. | .. |
---|
642 | 652 | if (psi->flags & PSTORE_FLAGS_DMESG) |
---|
643 | 653 | pstore_unregister_kmsg(); |
---|
644 | 654 | |
---|
| 655 | + /* Stop timer and make sure all work has finished. */ |
---|
| 656 | + del_timer_sync(&pstore_timer); |
---|
| 657 | + flush_work(&pstore_work); |
---|
| 658 | + |
---|
| 659 | + /* Remove all backend records from filesystem tree. */ |
---|
| 660 | + pstore_put_backend_records(psi); |
---|
| 661 | + |
---|
645 | 662 | free_buf_for_compression(); |
---|
646 | 663 | |
---|
647 | 664 | psinfo = NULL; |
---|
| 665 | + kfree(backend); |
---|
648 | 666 | backend = NULL; |
---|
| 667 | + mutex_unlock(&psinfo_lock); |
---|
649 | 668 | } |
---|
650 | 669 | EXPORT_SYMBOL_GPL(pstore_unregister); |
---|
651 | 670 | |
---|
652 | 671 | static void decompress_record(struct pstore_record *record) |
---|
653 | 672 | { |
---|
| 673 | + int ret; |
---|
654 | 674 | int unzipped_len; |
---|
655 | | - char *decompressed; |
---|
| 675 | + char *unzipped, *workspace; |
---|
656 | 676 | |
---|
657 | 677 | if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS) || !record->compressed) |
---|
658 | 678 | return; |
---|
.. | .. |
---|
663 | 683 | return; |
---|
664 | 684 | } |
---|
665 | 685 | |
---|
666 | | - /* No compression method has created the common buffer. */ |
---|
| 686 | + /* Missing compression buffer means compression was not initialized. */ |
---|
667 | 687 | if (!big_oops_buf) { |
---|
668 | | - pr_warn("no decompression buffer allocated\n"); |
---|
| 688 | + pr_warn("no decompression method initialized!\n"); |
---|
669 | 689 | return; |
---|
670 | 690 | } |
---|
671 | 691 | |
---|
672 | | - unzipped_len = pstore_decompress(record->buf, big_oops_buf, |
---|
673 | | - record->size, big_oops_buf_sz); |
---|
674 | | - if (unzipped_len <= 0) { |
---|
675 | | - pr_err("decompression failed: %d\n", unzipped_len); |
---|
| 692 | + /* Allocate enough space to hold max decompression and ECC. */ |
---|
| 693 | + unzipped_len = big_oops_buf_sz; |
---|
| 694 | + workspace = kmalloc(unzipped_len + record->ecc_notice_size, |
---|
| 695 | + GFP_KERNEL); |
---|
| 696 | + if (!workspace) |
---|
676 | 697 | return; |
---|
677 | | - } |
---|
678 | 698 | |
---|
679 | | - /* Build new buffer for decompressed contents. */ |
---|
680 | | - decompressed = kmalloc(unzipped_len + record->ecc_notice_size, |
---|
681 | | - GFP_KERNEL); |
---|
682 | | - if (!decompressed) { |
---|
683 | | - pr_err("decompression ran out of memory\n"); |
---|
| 699 | + /* After decompression "unzipped_len" is almost certainly smaller. */ |
---|
| 700 | + ret = crypto_comp_decompress(tfm, record->buf, record->size, |
---|
| 701 | + workspace, &unzipped_len); |
---|
| 702 | + if (ret) { |
---|
| 703 | + pr_err("crypto_comp_decompress failed, ret = %d!\n", ret); |
---|
| 704 | + kfree(workspace); |
---|
684 | 705 | return; |
---|
685 | 706 | } |
---|
686 | | - memcpy(decompressed, big_oops_buf, unzipped_len); |
---|
687 | 707 | |
---|
688 | 708 | /* Append ECC notice to decompressed buffer. */ |
---|
689 | | - memcpy(decompressed + unzipped_len, record->buf + record->size, |
---|
| 709 | + memcpy(workspace + unzipped_len, record->buf + record->size, |
---|
690 | 710 | record->ecc_notice_size); |
---|
691 | 711 | |
---|
692 | | - /* Swap out compresed contents with decompressed contents. */ |
---|
| 712 | + /* Copy decompressed contents into an minimum-sized allocation. */ |
---|
| 713 | + unzipped = kmemdup(workspace, unzipped_len + record->ecc_notice_size, |
---|
| 714 | + GFP_KERNEL); |
---|
| 715 | + kfree(workspace); |
---|
| 716 | + if (!unzipped) |
---|
| 717 | + return; |
---|
| 718 | + |
---|
| 719 | + /* Swap out compressed contents with decompressed contents. */ |
---|
693 | 720 | kfree(record->buf); |
---|
694 | | - record->buf = decompressed; |
---|
| 721 | + record->buf = unzipped; |
---|
695 | 722 | record->size = unzipped_len; |
---|
696 | 723 | record->compressed = false; |
---|
697 | 724 | } |
---|
.. | .. |
---|
774 | 801 | schedule_work(&pstore_work); |
---|
775 | 802 | } |
---|
776 | 803 | |
---|
777 | | - if (pstore_update_ms >= 0) |
---|
778 | | - mod_timer(&pstore_timer, |
---|
779 | | - jiffies + msecs_to_jiffies(pstore_update_ms)); |
---|
| 804 | + pstore_timer_kick(); |
---|
780 | 805 | } |
---|
781 | 806 | |
---|
782 | | -void __init pstore_choose_compression(void) |
---|
| 807 | +static void __init pstore_choose_compression(void) |
---|
783 | 808 | { |
---|
784 | 809 | const struct pstore_zbackend *step; |
---|
785 | 810 | |
---|
.. | .. |
---|
820 | 845 | pstore_exit_fs(); |
---|
821 | 846 | } |
---|
822 | 847 | module_exit(pstore_exit) |
---|
823 | | - |
---|
824 | | -module_param(compress, charp, 0444); |
---|
825 | | -MODULE_PARM_DESC(compress, "Pstore compression to use"); |
---|
826 | | - |
---|
827 | | -module_param(backend, charp, 0444); |
---|
828 | | -MODULE_PARM_DESC(backend, "Pstore backend to use"); |
---|
829 | 848 | |
---|
830 | 849 | MODULE_AUTHOR("Tony Luck <tony.luck@intel.com>"); |
---|
831 | 850 | MODULE_LICENSE("GPL"); |
---|