diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-02 13:35:45 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-02 13:35:45 -0400 |
commit | 2575be8ad32f9910d7e7c118e73f529b8d5b8b7b (patch) | |
tree | ae27a631c15914e0870813af57d443fbd4ad0acf | |
parent | c58d4055c054fc6dc72f1be8bc71bd6fff209e48 (diff) | |
parent | 3a7d2fd16c57a1ef47dc2891171514231c9c7c6e (diff) |
Merge tag 'pstore-v4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux
Pull pstore updates from Kees Cook:
"This has a large internal refactoring along with several smaller
fixes.
- constify compression structures; Bhumika Goyal
- restore powerpc dumping; Ankit Kumar
- fix more bugs in the rarely exercises module unloading logic
- reorganize filesystem locking to fix problems noticed by lockdep
- refactor internal pstore APIs to make development and review
easier:
- improve error reporting
- add kernel-doc structure and function comments
- avoid insane argument passing by using a common record
structure"
* tag 'pstore-v4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: (23 commits)
pstore: Solve lockdep warning by moving inode locks
pstore: Fix flags to enable dumps on powerpc
pstore: Remove unused vmalloc.h in pmsg
pstore: simplify write_user_compat()
pstore: Remove write_buf() callback
pstore: Replace arguments for write_buf_user() API
pstore: Replace arguments for write_buf() API
pstore: Replace arguments for erase() API
pstore: Do not duplicate record metadata
pstore: Allocate records on heap instead of stack
pstore: Pass record contents instead of copying
pstore: Always allocate buffer for decompression
pstore: Replace arguments for write() API
pstore: Replace arguments for read() API
pstore: Switch pstore_mkfile to pass record
pstore: Move record decompression to function
pstore: Extract common arguments into structure
pstore: Add kernel-doc for struct pstore_info
pstore: Improve register_pstore() error reporting
pstore: Avoid race in module unloading
...
-rw-r--r-- | arch/powerpc/kernel/nvram_64.c | 89 | ||||
-rw-r--r-- | drivers/acpi/apei/erst.c | 64 | ||||
-rw-r--r-- | drivers/firmware/efi/efi-pstore.c | 148 | ||||
-rw-r--r-- | fs/pstore/ftrace.c | 11 | ||||
-rw-r--r-- | fs/pstore/inode.c | 147 | ||||
-rw-r--r-- | fs/pstore/internal.h | 8 | ||||
-rw-r--r-- | fs/pstore/platform.c | 301 | ||||
-rw-r--r-- | fs/pstore/pmsg.c | 12 | ||||
-rw-r--r-- | fs/pstore/ram.c | 130 | ||||
-rw-r--r-- | fs/pstore/ram_core.c | 2 | ||||
-rw-r--r-- | include/linux/pstore.h | 156 |
11 files changed, 607 insertions, 461 deletions
diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c index d5e2b8309939..eae61b044e9e 100644 --- a/arch/powerpc/kernel/nvram_64.c +++ b/arch/powerpc/kernel/nvram_64.c | |||
@@ -389,51 +389,40 @@ static int nvram_pstore_open(struct pstore_info *psi) | |||
389 | 389 | ||
390 | /** | 390 | /** |
391 | * nvram_pstore_write - pstore write callback for nvram | 391 | * nvram_pstore_write - pstore write callback for nvram |
392 | * @type: Type of message logged | 392 | * @record: pstore record to write, with @id to be set |
393 | * @reason: reason behind dump (oops/panic) | ||
394 | * @id: identifier to indicate the write performed | ||
395 | * @part: pstore writes data to registered buffer in parts, | ||
396 | * part number will indicate the same. | ||
397 | * @count: Indicates oops count | ||
398 | * @compressed: Flag to indicate the log is compressed | ||
399 | * @size: number of bytes written to the registered buffer | ||
400 | * @psi: registered pstore_info structure | ||
401 | * | 393 | * |
402 | * Called by pstore_dump() when an oops or panic report is logged in the | 394 | * Called by pstore_dump() when an oops or panic report is logged in the |
403 | * printk buffer. | 395 | * printk buffer. |
404 | * Returns 0 on successful write. | 396 | * Returns 0 on successful write. |
405 | */ | 397 | */ |
406 | static int nvram_pstore_write(enum pstore_type_id type, | 398 | static int nvram_pstore_write(struct pstore_record *record) |
407 | enum kmsg_dump_reason reason, | ||
408 | u64 *id, unsigned int part, int count, | ||
409 | bool compressed, size_t size, | ||
410 | struct pstore_info *psi) | ||
411 | { | 399 | { |
412 | int rc; | 400 | int rc; |
413 | unsigned int err_type = ERR_TYPE_KERNEL_PANIC; | 401 | unsigned int err_type = ERR_TYPE_KERNEL_PANIC; |
414 | struct oops_log_info *oops_hdr = (struct oops_log_info *) oops_buf; | 402 | struct oops_log_info *oops_hdr = (struct oops_log_info *) oops_buf; |
415 | 403 | ||
416 | /* part 1 has the recent messages from printk buffer */ | 404 | /* part 1 has the recent messages from printk buffer */ |
417 | if (part > 1 || (type != PSTORE_TYPE_DMESG)) | 405 | if (record->part > 1 || (record->type != PSTORE_TYPE_DMESG)) |
418 | return -1; | 406 | return -1; |
419 | 407 | ||
420 | if (clobbering_unread_rtas_event()) | 408 | if (clobbering_unread_rtas_event()) |
421 | return -1; | 409 | return -1; |
422 | 410 | ||
423 | oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION); | 411 | oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION); |
424 | oops_hdr->report_length = cpu_to_be16(size); | 412 | oops_hdr->report_length = cpu_to_be16(record->size); |
425 | oops_hdr->timestamp = cpu_to_be64(ktime_get_real_seconds()); | 413 | oops_hdr->timestamp = cpu_to_be64(ktime_get_real_seconds()); |
426 | 414 | ||
427 | if (compressed) | 415 | if (record->compressed) |
428 | err_type = ERR_TYPE_KERNEL_PANIC_GZ; | 416 | err_type = ERR_TYPE_KERNEL_PANIC_GZ; |
429 | 417 | ||
430 | rc = nvram_write_os_partition(&oops_log_partition, oops_buf, | 418 | rc = nvram_write_os_partition(&oops_log_partition, oops_buf, |
431 | (int) (sizeof(*oops_hdr) + size), err_type, count); | 419 | (int) (sizeof(*oops_hdr) + record->size), err_type, |
420 | record->count); | ||
432 | 421 | ||
433 | if (rc != 0) | 422 | if (rc != 0) |
434 | return rc; | 423 | return rc; |
435 | 424 | ||
436 | *id = part; | 425 | record->id = record->part; |
437 | return 0; | 426 | return 0; |
438 | } | 427 | } |
439 | 428 | ||
@@ -442,10 +431,7 @@ static int nvram_pstore_write(enum pstore_type_id type, | |||
442 | * Returns the length of the data we read from each partition. | 431 | * Returns the length of the data we read from each partition. |
443 | * Returns 0 if we've been called before. | 432 | * Returns 0 if we've been called before. |
444 | */ | 433 | */ |
445 | static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, | 434 | static ssize_t nvram_pstore_read(struct pstore_record *record) |
446 | int *count, struct timespec *time, char **buf, | ||
447 | bool *compressed, ssize_t *ecc_notice_size, | ||
448 | struct pstore_info *psi) | ||
449 | { | 435 | { |
450 | struct oops_log_info *oops_hdr; | 436 | struct oops_log_info *oops_hdr; |
451 | unsigned int err_type, id_no, size = 0; | 437 | unsigned int err_type, id_no, size = 0; |
@@ -459,40 +445,40 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, | |||
459 | switch (nvram_type_ids[read_type]) { | 445 | switch (nvram_type_ids[read_type]) { |
460 | case PSTORE_TYPE_DMESG: | 446 | case PSTORE_TYPE_DMESG: |
461 | part = &oops_log_partition; | 447 | part = &oops_log_partition; |
462 | *type = PSTORE_TYPE_DMESG; | 448 | record->type = PSTORE_TYPE_DMESG; |
463 | break; | 449 | break; |
464 | case PSTORE_TYPE_PPC_COMMON: | 450 | case PSTORE_TYPE_PPC_COMMON: |
465 | sig = NVRAM_SIG_SYS; | 451 | sig = NVRAM_SIG_SYS; |
466 | part = &common_partition; | 452 | part = &common_partition; |
467 | *type = PSTORE_TYPE_PPC_COMMON; | 453 | record->type = PSTORE_TYPE_PPC_COMMON; |
468 | *id = PSTORE_TYPE_PPC_COMMON; | 454 | record->id = PSTORE_TYPE_PPC_COMMON; |
469 | time->tv_sec = 0; | 455 | record->time.tv_sec = 0; |
470 | time->tv_nsec = 0; | 456 | record->time.tv_nsec = 0; |
471 | break; | 457 | break; |
472 | #ifdef CONFIG_PPC_PSERIES | 458 | #ifdef CONFIG_PPC_PSERIES |
473 | case PSTORE_TYPE_PPC_RTAS: | 459 | case PSTORE_TYPE_PPC_RTAS: |
474 | part = &rtas_log_partition; | 460 | part = &rtas_log_partition; |
475 | *type = PSTORE_TYPE_PPC_RTAS; | 461 | record->type = PSTORE_TYPE_PPC_RTAS; |
476 | time->tv_sec = last_rtas_event; | 462 | record->time.tv_sec = last_rtas_event; |
477 | time->tv_nsec = 0; | 463 | record->time.tv_nsec = 0; |
478 | break; | 464 | break; |
479 | case PSTORE_TYPE_PPC_OF: | 465 | case PSTORE_TYPE_PPC_OF: |
480 | sig = NVRAM_SIG_OF; | 466 | sig = NVRAM_SIG_OF; |
481 | part = &of_config_partition; | 467 | part = &of_config_partition; |
482 | *type = PSTORE_TYPE_PPC_OF; | 468 | record->type = PSTORE_TYPE_PPC_OF; |
483 | *id = PSTORE_TYPE_PPC_OF; | 469 | record->id = PSTORE_TYPE_PPC_OF; |
484 | time->tv_sec = 0; | 470 | record->time.tv_sec = 0; |
485 | time->tv_nsec = 0; | 471 | record->time.tv_nsec = 0; |
486 | break; | 472 | break; |
487 | #endif | 473 | #endif |
488 | #ifdef CONFIG_PPC_POWERNV | 474 | #ifdef CONFIG_PPC_POWERNV |
489 | case PSTORE_TYPE_PPC_OPAL: | 475 | case PSTORE_TYPE_PPC_OPAL: |
490 | sig = NVRAM_SIG_FW; | 476 | sig = NVRAM_SIG_FW; |
491 | part = &skiboot_partition; | 477 | part = &skiboot_partition; |
492 | *type = PSTORE_TYPE_PPC_OPAL; | 478 | record->type = PSTORE_TYPE_PPC_OPAL; |
493 | *id = PSTORE_TYPE_PPC_OPAL; | 479 | record->id = PSTORE_TYPE_PPC_OPAL; |
494 | time->tv_sec = 0; | 480 | record->time.tv_sec = 0; |
495 | time->tv_nsec = 0; | 481 | record->time.tv_nsec = 0; |
496 | break; | 482 | break; |
497 | #endif | 483 | #endif |
498 | default: | 484 | default: |
@@ -520,10 +506,10 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, | |||
520 | return 0; | 506 | return 0; |
521 | } | 507 | } |
522 | 508 | ||
523 | *count = 0; | 509 | record->count = 0; |
524 | 510 | ||
525 | if (part->os_partition) | 511 | if (part->os_partition) |
526 | *id = id_no; | 512 | record->id = id_no; |
527 | 513 | ||
528 | if (nvram_type_ids[read_type] == PSTORE_TYPE_DMESG) { | 514 | if (nvram_type_ids[read_type] == PSTORE_TYPE_DMESG) { |
529 | size_t length, hdr_size; | 515 | size_t length, hdr_size; |
@@ -533,34 +519,35 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, | |||
533 | /* Old format oops header had 2-byte record size */ | 519 | /* Old format oops header had 2-byte record size */ |
534 | hdr_size = sizeof(u16); | 520 | hdr_size = sizeof(u16); |
535 | length = be16_to_cpu(oops_hdr->version); | 521 | length = be16_to_cpu(oops_hdr->version); |
536 | time->tv_sec = 0; | 522 | record->time.tv_sec = 0; |
537 | time->tv_nsec = 0; | 523 | record->time.tv_nsec = 0; |
538 | } else { | 524 | } else { |
539 | hdr_size = sizeof(*oops_hdr); | 525 | hdr_size = sizeof(*oops_hdr); |
540 | length = be16_to_cpu(oops_hdr->report_length); | 526 | length = be16_to_cpu(oops_hdr->report_length); |
541 | time->tv_sec = be64_to_cpu(oops_hdr->timestamp); | 527 | record->time.tv_sec = be64_to_cpu(oops_hdr->timestamp); |
542 | time->tv_nsec = 0; | 528 | record->time.tv_nsec = 0; |
543 | } | 529 | } |
544 | *buf = kmemdup(buff + hdr_size, length, GFP_KERNEL); | 530 | record->buf = kmemdup(buff + hdr_size, length, GFP_KERNEL); |
545 | kfree(buff); | 531 | kfree(buff); |
546 | if (*buf == NULL) | 532 | if (record->buf == NULL) |
547 | return -ENOMEM; | 533 | return -ENOMEM; |
548 | 534 | ||
549 | *ecc_notice_size = 0; | 535 | record->ecc_notice_size = 0; |
550 | if (err_type == ERR_TYPE_KERNEL_PANIC_GZ) | 536 | if (err_type == ERR_TYPE_KERNEL_PANIC_GZ) |
551 | *compressed = true; | 537 | record->compressed = true; |
552 | else | 538 | else |
553 | *compressed = false; | 539 | record->compressed = false; |
554 | return length; | 540 | return length; |
555 | } | 541 | } |
556 | 542 | ||
557 | *buf = buff; | 543 | record->buf = buff; |
558 | return part->size; | 544 | return part->size; |
559 | } | 545 | } |
560 | 546 | ||
561 | static struct pstore_info nvram_pstore_info = { | 547 | static struct pstore_info nvram_pstore_info = { |
562 | .owner = THIS_MODULE, | 548 | .owner = THIS_MODULE, |
563 | .name = "nvram", | 549 | .name = "nvram", |
550 | .flags = PSTORE_FLAGS_DMESG, | ||
564 | .open = nvram_pstore_open, | 551 | .open = nvram_pstore_open, |
565 | .read = nvram_pstore_read, | 552 | .read = nvram_pstore_read, |
566 | .write = nvram_pstore_write, | 553 | .write = nvram_pstore_write, |
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index ec4f507b524f..7207e5fc9d3d 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c | |||
@@ -925,15 +925,9 @@ static int erst_check_table(struct acpi_table_erst *erst_tab) | |||
925 | 925 | ||
926 | static int erst_open_pstore(struct pstore_info *psi); | 926 | static int erst_open_pstore(struct pstore_info *psi); |
927 | static int erst_close_pstore(struct pstore_info *psi); | 927 | static int erst_close_pstore(struct pstore_info *psi); |
928 | static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, | 928 | static ssize_t erst_reader(struct pstore_record *record); |
929 | struct timespec *time, char **buf, | 929 | static int erst_writer(struct pstore_record *record); |
930 | bool *compressed, ssize_t *ecc_notice_size, | 930 | static int erst_clearer(struct pstore_record *record); |
931 | struct pstore_info *psi); | ||
932 | static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, | ||
933 | u64 *id, unsigned int part, int count, bool compressed, | ||
934 | size_t size, struct pstore_info *psi); | ||
935 | static int erst_clearer(enum pstore_type_id type, u64 id, int count, | ||
936 | struct timespec time, struct pstore_info *psi); | ||
937 | 931 | ||
938 | static struct pstore_info erst_info = { | 932 | static struct pstore_info erst_info = { |
939 | .owner = THIS_MODULE, | 933 | .owner = THIS_MODULE, |
@@ -986,10 +980,7 @@ static int erst_close_pstore(struct pstore_info *psi) | |||
986 | return 0; | 980 | return 0; |
987 | } | 981 | } |
988 | 982 | ||
989 | static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, | 983 | static ssize_t erst_reader(struct pstore_record *record) |
990 | struct timespec *time, char **buf, | ||
991 | bool *compressed, ssize_t *ecc_notice_size, | ||
992 | struct pstore_info *psi) | ||
993 | { | 984 | { |
994 | int rc; | 985 | int rc; |
995 | ssize_t len = 0; | 986 | ssize_t len = 0; |
@@ -1027,42 +1018,40 @@ skip: | |||
1027 | if (uuid_le_cmp(rcd->hdr.creator_id, CPER_CREATOR_PSTORE) != 0) | 1018 | if (uuid_le_cmp(rcd->hdr.creator_id, CPER_CREATOR_PSTORE) != 0) |
1028 | goto skip; | 1019 | goto skip; |
1029 | 1020 | ||
1030 | *buf = kmalloc(len, GFP_KERNEL); | 1021 | record->buf = kmalloc(len, GFP_KERNEL); |
1031 | if (*buf == NULL) { | 1022 | if (record->buf == NULL) { |
1032 | rc = -ENOMEM; | 1023 | rc = -ENOMEM; |
1033 | goto out; | 1024 | goto out; |
1034 | } | 1025 | } |
1035 | memcpy(*buf, rcd->data, len - sizeof(*rcd)); | 1026 | memcpy(record->buf, rcd->data, len - sizeof(*rcd)); |
1036 | *id = record_id; | 1027 | record->id = record_id; |
1037 | *compressed = false; | 1028 | record->compressed = false; |
1038 | *ecc_notice_size = 0; | 1029 | record->ecc_notice_size = 0; |
1039 | if (uuid_le_cmp(rcd->sec_hdr.section_type, | 1030 | if (uuid_le_cmp(rcd->sec_hdr.section_type, |
1040 | CPER_SECTION_TYPE_DMESG_Z) == 0) { | 1031 | CPER_SECTION_TYPE_DMESG_Z) == 0) { |
1041 | *type = PSTORE_TYPE_DMESG; | 1032 | record->type = PSTORE_TYPE_DMESG; |
1042 | *compressed = true; | 1033 | record->compressed = true; |
1043 | } else if (uuid_le_cmp(rcd->sec_hdr.section_type, | 1034 | } else if (uuid_le_cmp(rcd->sec_hdr.section_type, |
1044 | CPER_SECTION_TYPE_DMESG) == 0) | 1035 | CPER_SECTION_TYPE_DMESG) == 0) |
1045 | *type = PSTORE_TYPE_DMESG; | 1036 | record->type = PSTORE_TYPE_DMESG; |
1046 | else if (uuid_le_cmp(rcd->sec_hdr.section_type, | 1037 | else if (uuid_le_cmp(rcd->sec_hdr.section_type, |
1047 | CPER_SECTION_TYPE_MCE) == 0) | 1038 | CPER_SECTION_TYPE_MCE) == 0) |
1048 | *type = PSTORE_TYPE_MCE; | 1039 | record->type = PSTORE_TYPE_MCE; |
1049 | else | 1040 | else |
1050 | *type = PSTORE_TYPE_UNKNOWN; | 1041 | record->type = PSTORE_TYPE_UNKNOWN; |
1051 | 1042 | ||
1052 | if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP) | 1043 | if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP) |
1053 | time->tv_sec = rcd->hdr.timestamp; | 1044 | record->time.tv_sec = rcd->hdr.timestamp; |
1054 | else | 1045 | else |
1055 | time->tv_sec = 0; | 1046 | record->time.tv_sec = 0; |
1056 | time->tv_nsec = 0; | 1047 | record->time.tv_nsec = 0; |
1057 | 1048 | ||
1058 | out: | 1049 | out: |
1059 | kfree(rcd); | 1050 | kfree(rcd); |
1060 | return (rc < 0) ? rc : (len - sizeof(*rcd)); | 1051 | return (rc < 0) ? rc : (len - sizeof(*rcd)); |
1061 | } | 1052 | } |
1062 | 1053 | ||
1063 | static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, | 1054 | static int erst_writer(struct pstore_record *record) |
1064 | u64 *id, unsigned int part, int count, bool compressed, | ||
1065 | size_t size, struct pstore_info *psi) | ||
1066 | { | 1055 | { |
1067 | struct cper_pstore_record *rcd = (struct cper_pstore_record *) | 1056 | struct cper_pstore_record *rcd = (struct cper_pstore_record *) |
1068 | (erst_info.buf - sizeof(*rcd)); | 1057 | (erst_info.buf - sizeof(*rcd)); |
@@ -1077,21 +1066,21 @@ static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, | |||
1077 | /* timestamp valid. platform_id, partition_id are invalid */ | 1066 | /* timestamp valid. platform_id, partition_id are invalid */ |
1078 | rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP; | 1067 | rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP; |
1079 | rcd->hdr.timestamp = get_seconds(); | 1068 | rcd->hdr.timestamp = get_seconds(); |
1080 | rcd->hdr.record_length = sizeof(*rcd) + size; | 1069 | rcd->hdr.record_length = sizeof(*rcd) + record->size; |
1081 | rcd->hdr.creator_id = CPER_CREATOR_PSTORE; | 1070 | rcd->hdr.creator_id = CPER_CREATOR_PSTORE; |
1082 | rcd->hdr.notification_type = CPER_NOTIFY_MCE; | 1071 | rcd->hdr.notification_type = CPER_NOTIFY_MCE; |
1083 | rcd->hdr.record_id = cper_next_record_id(); | 1072 | rcd->hdr.record_id = cper_next_record_id(); |
1084 | rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR; | 1073 | rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR; |
1085 | 1074 | ||
1086 | rcd->sec_hdr.section_offset = sizeof(*rcd); | 1075 | rcd->sec_hdr.section_offset = sizeof(*rcd); |
1087 | rcd->sec_hdr.section_length = size; | 1076 | rcd->sec_hdr.section_length = record->size; |
1088 | rcd->sec_hdr.revision = CPER_SEC_REV; | 1077 | rcd->sec_hdr.revision = CPER_SEC_REV; |
1089 | /* fru_id and fru_text is invalid */ | 1078 | /* fru_id and fru_text is invalid */ |
1090 | rcd->sec_hdr.validation_bits = 0; | 1079 | rcd->sec_hdr.validation_bits = 0; |
1091 | rcd->sec_hdr.flags = CPER_SEC_PRIMARY; | 1080 | rcd->sec_hdr.flags = CPER_SEC_PRIMARY; |
1092 | switch (type) { | 1081 | switch (record->type) { |
1093 | case PSTORE_TYPE_DMESG: | 1082 | case PSTORE_TYPE_DMESG: |
1094 | if (compressed) | 1083 | if (record->compressed) |
1095 | rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG_Z; | 1084 | rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG_Z; |
1096 | else | 1085 | else |
1097 | rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG; | 1086 | rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG; |
@@ -1105,15 +1094,14 @@ static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, | |||
1105 | rcd->sec_hdr.section_severity = CPER_SEV_FATAL; | 1094 | rcd->sec_hdr.section_severity = CPER_SEV_FATAL; |
1106 | 1095 | ||
1107 | ret = erst_write(&rcd->hdr); | 1096 | ret = erst_write(&rcd->hdr); |
1108 | *id = rcd->hdr.record_id; | 1097 | record->id = rcd->hdr.record_id; |
1109 | 1098 | ||
1110 | return ret; | 1099 | return ret; |
1111 | } | 1100 | } |
1112 | 1101 | ||
1113 | static int erst_clearer(enum pstore_type_id type, u64 id, int count, | 1102 | static int erst_clearer(struct pstore_record *record) |
1114 | struct timespec time, struct pstore_info *psi) | ||
1115 | { | 1103 | { |
1116 | return erst_clear(id); | 1104 | return erst_clear(record->id); |
1117 | } | 1105 | } |
1118 | 1106 | ||
1119 | static int __init erst_init(void) | 1107 | static int __init erst_init(void) |
diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index 6b5acefce6b3..ed3137c1ceb0 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c | |||
@@ -28,26 +28,16 @@ static int efi_pstore_close(struct pstore_info *psi) | |||
28 | return 0; | 28 | return 0; |
29 | } | 29 | } |
30 | 30 | ||
31 | struct pstore_read_data { | ||
32 | u64 *id; | ||
33 | enum pstore_type_id *type; | ||
34 | int *count; | ||
35 | struct timespec *timespec; | ||
36 | bool *compressed; | ||
37 | ssize_t *ecc_notice_size; | ||
38 | char **buf; | ||
39 | }; | ||
40 | |||
41 | static inline u64 generic_id(unsigned long timestamp, | 31 | static inline u64 generic_id(unsigned long timestamp, |
42 | unsigned int part, int count) | 32 | unsigned int part, int count) |
43 | { | 33 | { |
44 | return ((u64) timestamp * 100 + part) * 1000 + count; | 34 | return ((u64) timestamp * 100 + part) * 1000 + count; |
45 | } | 35 | } |
46 | 36 | ||
47 | static int efi_pstore_read_func(struct efivar_entry *entry, void *data) | 37 | static int efi_pstore_read_func(struct efivar_entry *entry, |
38 | struct pstore_record *record) | ||
48 | { | 39 | { |
49 | efi_guid_t vendor = LINUX_EFI_CRASH_GUID; | 40 | efi_guid_t vendor = LINUX_EFI_CRASH_GUID; |
50 | struct pstore_read_data *cb_data = data; | ||
51 | char name[DUMP_NAME_LEN], data_type; | 41 | char name[DUMP_NAME_LEN], data_type; |
52 | int i; | 42 | int i; |
53 | int cnt; | 43 | int cnt; |
@@ -61,37 +51,37 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) | |||
61 | name[i] = entry->var.VariableName[i]; | 51 | name[i] = entry->var.VariableName[i]; |
62 | 52 | ||
63 | if (sscanf(name, "dump-type%u-%u-%d-%lu-%c", | 53 | if (sscanf(name, "dump-type%u-%u-%d-%lu-%c", |
64 | cb_data->type, &part, &cnt, &time, &data_type) == 5) { | 54 | &record->type, &part, &cnt, &time, &data_type) == 5) { |
65 | *cb_data->id = generic_id(time, part, cnt); | 55 | record->id = generic_id(time, part, cnt); |
66 | *cb_data->count = cnt; | 56 | record->count = cnt; |
67 | cb_data->timespec->tv_sec = time; | 57 | record->time.tv_sec = time; |
68 | cb_data->timespec->tv_nsec = 0; | 58 | record->time.tv_nsec = 0; |
69 | if (data_type == 'C') | 59 | if (data_type == 'C') |
70 | *cb_data->compressed = true; | 60 | record->compressed = true; |
71 | else | 61 | else |
72 | *cb_data->compressed = false; | 62 | record->compressed = false; |
73 | *cb_data->ecc_notice_size = 0; | 63 | record->ecc_notice_size = 0; |
74 | } else if (sscanf(name, "dump-type%u-%u-%d-%lu", | 64 | } else if (sscanf(name, "dump-type%u-%u-%d-%lu", |
75 | cb_data->type, &part, &cnt, &time) == 4) { | 65 | &record->type, &part, &cnt, &time) == 4) { |
76 | *cb_data->id = generic_id(time, part, cnt); | 66 | record->id = generic_id(time, part, cnt); |
77 | *cb_data->count = cnt; | 67 | record->count = cnt; |
78 | cb_data->timespec->tv_sec = time; | 68 | record->time.tv_sec = time; |
79 | cb_data->timespec->tv_nsec = 0; | 69 | record->time.tv_nsec = 0; |
80 | *cb_data->compressed = false; | 70 | record->compressed = false; |
81 | *cb_data->ecc_notice_size = 0; | 71 | record->ecc_notice_size = 0; |
82 | } else if (sscanf(name, "dump-type%u-%u-%lu", | 72 | } else if (sscanf(name, "dump-type%u-%u-%lu", |
83 | cb_data->type, &part, &time) == 3) { | 73 | &record->type, &part, &time) == 3) { |
84 | /* | 74 | /* |
85 | * Check if an old format, | 75 | * Check if an old format, |
86 | * which doesn't support holding | 76 | * which doesn't support holding |
87 | * multiple logs, remains. | 77 | * multiple logs, remains. |
88 | */ | 78 | */ |
89 | *cb_data->id = generic_id(time, part, 0); | 79 | record->id = generic_id(time, part, 0); |
90 | *cb_data->count = 0; | 80 | record->count = 0; |
91 | cb_data->timespec->tv_sec = time; | 81 | record->time.tv_sec = time; |
92 | cb_data->timespec->tv_nsec = 0; | 82 | record->time.tv_nsec = 0; |
93 | *cb_data->compressed = false; | 83 | record->compressed = false; |
94 | *cb_data->ecc_notice_size = 0; | 84 | record->ecc_notice_size = 0; |
95 | } else | 85 | } else |
96 | return 0; | 86 | return 0; |
97 | 87 | ||
@@ -99,7 +89,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) | |||
99 | __efivar_entry_get(entry, &entry->var.Attributes, | 89 | __efivar_entry_get(entry, &entry->var.Attributes, |
100 | &entry->var.DataSize, entry->var.Data); | 90 | &entry->var.DataSize, entry->var.Data); |
101 | size = entry->var.DataSize; | 91 | size = entry->var.DataSize; |
102 | memcpy(*cb_data->buf, entry->var.Data, | 92 | memcpy(record->buf, entry->var.Data, |
103 | (size_t)min_t(unsigned long, EFIVARS_DATA_SIZE_MAX, size)); | 93 | (size_t)min_t(unsigned long, EFIVARS_DATA_SIZE_MAX, size)); |
104 | 94 | ||
105 | return size; | 95 | return size; |
@@ -164,7 +154,7 @@ static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos, | |||
164 | /** | 154 | /** |
165 | * efi_pstore_sysfs_entry_iter | 155 | * efi_pstore_sysfs_entry_iter |
166 | * | 156 | * |
167 | * @data: function-specific data to pass to callback | 157 | * @record: pstore record to pass to callback |
168 | * @pos: entry to begin iterating from | 158 | * @pos: entry to begin iterating from |
169 | * | 159 | * |
170 | * You MUST call efivar_enter_iter_begin() before this function, and | 160 | * You MUST call efivar_enter_iter_begin() before this function, and |
@@ -175,7 +165,8 @@ static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos, | |||
175 | * the next entry of the last one passed to efi_pstore_read_func(). | 165 | * the next entry of the last one passed to efi_pstore_read_func(). |
176 | * To begin iterating from the beginning of the list @pos must be %NULL. | 166 | * To begin iterating from the beginning of the list @pos must be %NULL. |
177 | */ | 167 | */ |
178 | static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos) | 168 | static int efi_pstore_sysfs_entry_iter(struct pstore_record *record, |
169 | struct efivar_entry **pos) | ||
179 | { | 170 | { |
180 | struct efivar_entry *entry, *n; | 171 | struct efivar_entry *entry, *n; |
181 | struct list_head *head = &efivar_sysfs_list; | 172 | struct list_head *head = &efivar_sysfs_list; |
@@ -186,7 +177,7 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos) | |||
186 | list_for_each_entry_safe(entry, n, head, list) { | 177 | list_for_each_entry_safe(entry, n, head, list) { |
187 | efi_pstore_scan_sysfs_enter(entry, n, head); | 178 | efi_pstore_scan_sysfs_enter(entry, n, head); |
188 | 179 | ||
189 | size = efi_pstore_read_func(entry, data); | 180 | size = efi_pstore_read_func(entry, record); |
190 | ret = efi_pstore_scan_sysfs_exit(entry, n, head, | 181 | ret = efi_pstore_scan_sysfs_exit(entry, n, head, |
191 | size < 0); | 182 | size < 0); |
192 | if (ret) | 183 | if (ret) |
@@ -201,7 +192,7 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos) | |||
201 | list_for_each_entry_safe_from((*pos), n, head, list) { | 192 | list_for_each_entry_safe_from((*pos), n, head, list) { |
202 | efi_pstore_scan_sysfs_enter((*pos), n, head); | 193 | efi_pstore_scan_sysfs_enter((*pos), n, head); |
203 | 194 | ||
204 | size = efi_pstore_read_func((*pos), data); | 195 | size = efi_pstore_read_func((*pos), record); |
205 | ret = efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0); | 196 | ret = efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0); |
206 | if (ret) | 197 | if (ret) |
207 | return ret; | 198 | return ret; |
@@ -225,71 +216,57 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos) | |||
225 | * size < 0: Failed to get data of entry logging via efi_pstore_write(), | 216 | * size < 0: Failed to get data of entry logging via efi_pstore_write(), |
226 | * and pstore will stop reading entry. | 217 | * and pstore will stop reading entry. |
227 | */ | 218 | */ |
228 | static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, | 219 | static ssize_t efi_pstore_read(struct pstore_record *record) |
229 | int *count, struct timespec *timespec, | ||
230 | char **buf, bool *compressed, | ||
231 | ssize_t *ecc_notice_size, | ||
232 | struct pstore_info *psi) | ||
233 | { | 220 | { |
234 | struct pstore_read_data data; | 221 | struct efivar_entry *entry = (struct efivar_entry *)record->psi->data; |
235 | ssize_t size; | 222 | ssize_t size; |
236 | 223 | ||
237 | data.id = id; | 224 | record->buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL); |
238 | data.type = type; | 225 | if (!record->buf) |
239 | data.count = count; | ||
240 | data.timespec = timespec; | ||
241 | data.compressed = compressed; | ||
242 | data.ecc_notice_size = ecc_notice_size; | ||
243 | data.buf = buf; | ||
244 | |||
245 | *data.buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL); | ||
246 | if (!*data.buf) | ||
247 | return -ENOMEM; | 226 | return -ENOMEM; |
248 | 227 | ||
249 | if (efivar_entry_iter_begin()) { | 228 | if (efivar_entry_iter_begin()) { |
250 | kfree(*data.buf); | 229 | size = -EINTR; |
251 | return -EINTR; | 230 | goto out; |
252 | } | 231 | } |
253 | size = efi_pstore_sysfs_entry_iter(&data, | 232 | size = efi_pstore_sysfs_entry_iter(record, &entry); |
254 | (struct efivar_entry **)&psi->data); | ||
255 | efivar_entry_iter_end(); | 233 | efivar_entry_iter_end(); |
256 | if (size <= 0) | 234 | |
257 | kfree(*data.buf); | 235 | out: |
236 | if (size <= 0) { | ||
237 | kfree(record->buf); | ||
238 | record->buf = NULL; | ||
239 | } | ||
258 | return size; | 240 | return size; |
259 | } | 241 | } |
260 | 242 | ||
261 | static int efi_pstore_write(enum pstore_type_id type, | 243 | static int efi_pstore_write(struct pstore_record *record) |
262 | enum kmsg_dump_reason reason, u64 *id, | ||
263 | unsigned int part, int count, bool compressed, size_t size, | ||
264 | struct pstore_info *psi) | ||
265 | { | 244 | { |
266 | char name[DUMP_NAME_LEN]; | 245 | char name[DUMP_NAME_LEN]; |
267 | efi_char16_t efi_name[DUMP_NAME_LEN]; | 246 | efi_char16_t efi_name[DUMP_NAME_LEN]; |
268 | efi_guid_t vendor = LINUX_EFI_CRASH_GUID; | 247 | efi_guid_t vendor = LINUX_EFI_CRASH_GUID; |
269 | int i, ret = 0; | 248 | int i, ret = 0; |
270 | 249 | ||
271 | sprintf(name, "dump-type%u-%u-%d-%lu-%c", type, part, count, | 250 | snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lu-%c", |
272 | get_seconds(), compressed ? 'C' : 'D'); | 251 | record->type, record->part, record->count, |
252 | get_seconds(), record->compressed ? 'C' : 'D'); | ||
273 | 253 | ||
274 | for (i = 0; i < DUMP_NAME_LEN; i++) | 254 | for (i = 0; i < DUMP_NAME_LEN; i++) |
275 | efi_name[i] = name[i]; | 255 | efi_name[i] = name[i]; |
276 | 256 | ||
277 | ret = efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES, | 257 | ret = efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES, |
278 | !pstore_cannot_block_path(reason), | 258 | !pstore_cannot_block_path(record->reason), |
279 | size, psi->buf); | 259 | record->size, record->psi->buf); |
280 | 260 | ||
281 | if (reason == KMSG_DUMP_OOPS) | 261 | if (record->reason == KMSG_DUMP_OOPS) |
282 | efivar_run_worker(); | 262 | efivar_run_worker(); |
283 | 263 | ||
284 | *id = part; | 264 | record->id = record->part; |
285 | return ret; | 265 | return ret; |
286 | }; | 266 | }; |
287 | 267 | ||
288 | struct pstore_erase_data { | 268 | struct pstore_erase_data { |
289 | u64 id; | 269 | struct pstore_record *record; |
290 | enum pstore_type_id type; | ||
291 | int count; | ||
292 | struct timespec time; | ||
293 | efi_char16_t *name; | 270 | efi_char16_t *name; |
294 | }; | 271 | }; |
295 | 272 | ||
@@ -315,8 +292,9 @@ static int efi_pstore_erase_func(struct efivar_entry *entry, void *data) | |||
315 | * Check if an old format, which doesn't support | 292 | * Check if an old format, which doesn't support |
316 | * holding multiple logs, remains. | 293 | * holding multiple logs, remains. |
317 | */ | 294 | */ |
318 | sprintf(name_old, "dump-type%u-%u-%lu", ed->type, | 295 | snprintf(name_old, sizeof(name_old), "dump-type%u-%u-%lu", |
319 | (unsigned int)ed->id, ed->time.tv_sec); | 296 | ed->record->type, (unsigned int)ed->record->id, |
297 | ed->record->time.tv_sec); | ||
320 | 298 | ||
321 | for (i = 0; i < DUMP_NAME_LEN; i++) | 299 | for (i = 0; i < DUMP_NAME_LEN; i++) |
322 | efi_name_old[i] = name_old[i]; | 300 | efi_name_old[i] = name_old[i]; |
@@ -341,8 +319,7 @@ static int efi_pstore_erase_func(struct efivar_entry *entry, void *data) | |||
341 | return 1; | 319 | return 1; |
342 | } | 320 | } |
343 | 321 | ||
344 | static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, | 322 | static int efi_pstore_erase(struct pstore_record *record) |
345 | struct timespec time, struct pstore_info *psi) | ||
346 | { | 323 | { |
347 | struct pstore_erase_data edata; | 324 | struct pstore_erase_data edata; |
348 | struct efivar_entry *entry = NULL; | 325 | struct efivar_entry *entry = NULL; |
@@ -351,17 +328,16 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, | |||
351 | int found, i; | 328 | int found, i; |
352 | unsigned int part; | 329 | unsigned int part; |
353 | 330 | ||
354 | do_div(id, 1000); | 331 | do_div(record->id, 1000); |
355 | part = do_div(id, 100); | 332 | part = do_div(record->id, 100); |
356 | sprintf(name, "dump-type%u-%u-%d-%lu", type, part, count, time.tv_sec); | 333 | snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lu", |
334 | record->type, record->part, record->count, | ||
335 | record->time.tv_sec); | ||
357 | 336 | ||
358 | for (i = 0; i < DUMP_NAME_LEN; i++) | 337 | for (i = 0; i < DUMP_NAME_LEN; i++) |
359 | efi_name[i] = name[i]; | 338 | efi_name[i] = name[i]; |
360 | 339 | ||
361 | edata.id = part; | 340 | edata.record = record; |
362 | edata.type = type; | ||
363 | edata.count = count; | ||
364 | edata.time = time; | ||
365 | edata.name = efi_name; | 341 | edata.name = efi_name; |
366 | 342 | ||
367 | if (efivar_entry_iter_begin()) | 343 | if (efivar_entry_iter_begin()) |
diff --git a/fs/pstore/ftrace.c b/fs/pstore/ftrace.c index 899d0ba0bd6c..06aab07b6bb7 100644 --- a/fs/pstore/ftrace.c +++ b/fs/pstore/ftrace.c | |||
@@ -37,6 +37,12 @@ static void notrace pstore_ftrace_call(unsigned long ip, | |||
37 | { | 37 | { |
38 | unsigned long flags; | 38 | unsigned long flags; |
39 | struct pstore_ftrace_record rec = {}; | 39 | struct pstore_ftrace_record rec = {}; |
40 | struct pstore_record record = { | ||
41 | .type = PSTORE_TYPE_FTRACE, | ||
42 | .buf = (char *)&rec, | ||
43 | .size = sizeof(rec), | ||
44 | .psi = psinfo, | ||
45 | }; | ||
40 | 46 | ||
41 | if (unlikely(oops_in_progress)) | 47 | if (unlikely(oops_in_progress)) |
42 | return; | 48 | return; |
@@ -47,8 +53,7 @@ static void notrace pstore_ftrace_call(unsigned long ip, | |||
47 | rec.parent_ip = parent_ip; | 53 | rec.parent_ip = parent_ip; |
48 | pstore_ftrace_write_timestamp(&rec, pstore_ftrace_stamp++); | 54 | pstore_ftrace_write_timestamp(&rec, pstore_ftrace_stamp++); |
49 | pstore_ftrace_encode_cpu(&rec, raw_smp_processor_id()); | 55 | pstore_ftrace_encode_cpu(&rec, raw_smp_processor_id()); |
50 | psinfo->write_buf(PSTORE_TYPE_FTRACE, 0, NULL, 0, (void *)&rec, | 56 | psinfo->write(&record); |
51 | 0, sizeof(rec), psinfo); | ||
52 | 57 | ||
53 | local_irq_restore(flags); | 58 | local_irq_restore(flags); |
54 | } | 59 | } |
@@ -117,7 +122,7 @@ void pstore_register_ftrace(void) | |||
117 | { | 122 | { |
118 | struct dentry *file; | 123 | struct dentry *file; |
119 | 124 | ||
120 | if (!psinfo->write_buf) | 125 | if (!psinfo->write) |
121 | return; | 126 | return; |
122 | 127 | ||
123 | pstore_ftrace_dir = debugfs_create_dir("pstore", NULL); | 128 | pstore_ftrace_dir = debugfs_create_dir("pstore", NULL); |
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index 57c0646479f5..792a4e5f9226 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c | |||
@@ -47,12 +47,8 @@ static LIST_HEAD(allpstore); | |||
47 | 47 | ||
48 | struct pstore_private { | 48 | struct pstore_private { |
49 | struct list_head list; | 49 | struct list_head list; |
50 | struct pstore_info *psi; | 50 | struct pstore_record *record; |
51 | enum pstore_type_id type; | 51 | size_t total_size; |
52 | u64 id; | ||
53 | int count; | ||
54 | ssize_t size; | ||
55 | char data[]; | ||
56 | }; | 52 | }; |
57 | 53 | ||
58 | struct pstore_ftrace_seq_data { | 54 | struct pstore_ftrace_seq_data { |
@@ -63,6 +59,17 @@ struct pstore_ftrace_seq_data { | |||
63 | 59 | ||
64 | #define REC_SIZE sizeof(struct pstore_ftrace_record) | 60 | #define REC_SIZE sizeof(struct pstore_ftrace_record) |
65 | 61 | ||
62 | static void free_pstore_private(struct pstore_private *private) | ||
63 | { | ||
64 | if (!private) | ||
65 | return; | ||
66 | if (private->record) { | ||
67 | kfree(private->record->buf); | ||
68 | kfree(private->record); | ||
69 | } | ||
70 | kfree(private); | ||
71 | } | ||
72 | |||
66 | static void *pstore_ftrace_seq_start(struct seq_file *s, loff_t *pos) | 73 | static void *pstore_ftrace_seq_start(struct seq_file *s, loff_t *pos) |
67 | { | 74 | { |
68 | struct pstore_private *ps = s->private; | 75 | struct pstore_private *ps = s->private; |
@@ -72,9 +79,9 @@ static void *pstore_ftrace_seq_start(struct seq_file *s, loff_t *pos) | |||
72 | if (!data) | 79 | if (!data) |
73 | return NULL; | 80 | return NULL; |
74 | 81 | ||
75 | data->off = ps->size % REC_SIZE; | 82 | data->off = ps->total_size % REC_SIZE; |
76 | data->off += *pos * REC_SIZE; | 83 | data->off += *pos * REC_SIZE; |
77 | if (data->off + REC_SIZE > ps->size) { | 84 | if (data->off + REC_SIZE > ps->total_size) { |
78 | kfree(data); | 85 | kfree(data); |
79 | return NULL; | 86 | return NULL; |
80 | } | 87 | } |
@@ -94,7 +101,7 @@ static void *pstore_ftrace_seq_next(struct seq_file *s, void *v, loff_t *pos) | |||
94 | struct pstore_ftrace_seq_data *data = v; | 101 | struct pstore_ftrace_seq_data *data = v; |
95 | 102 | ||
96 | data->off += REC_SIZE; | 103 | data->off += REC_SIZE; |
97 | if (data->off + REC_SIZE > ps->size) | 104 | if (data->off + REC_SIZE > ps->total_size) |
98 | return NULL; | 105 | return NULL; |
99 | 106 | ||
100 | (*pos)++; | 107 | (*pos)++; |
@@ -105,7 +112,9 @@ static int pstore_ftrace_seq_show(struct seq_file *s, void *v) | |||
105 | { | 112 | { |
106 | struct pstore_private *ps = s->private; | 113 | struct pstore_private *ps = s->private; |
107 | struct pstore_ftrace_seq_data *data = v; | 114 | struct pstore_ftrace_seq_data *data = v; |
108 | struct pstore_ftrace_record *rec = (void *)(ps->data + data->off); | 115 | struct pstore_ftrace_record *rec; |
116 | |||
117 | rec = (struct pstore_ftrace_record *)(ps->record->buf + data->off); | ||
109 | 118 | ||
110 | seq_printf(s, "CPU:%d ts:%llu %08lx %08lx %pf <- %pF\n", | 119 | seq_printf(s, "CPU:%d ts:%llu %08lx %08lx %pf <- %pF\n", |
111 | pstore_ftrace_decode_cpu(rec), | 120 | pstore_ftrace_decode_cpu(rec), |
@@ -125,7 +134,7 @@ static const struct seq_operations pstore_ftrace_seq_ops = { | |||
125 | 134 | ||
126 | static int pstore_check_syslog_permissions(struct pstore_private *ps) | 135 | static int pstore_check_syslog_permissions(struct pstore_private *ps) |
127 | { | 136 | { |
128 | switch (ps->type) { | 137 | switch (ps->record->type) { |
129 | case PSTORE_TYPE_DMESG: | 138 | case PSTORE_TYPE_DMESG: |
130 | case PSTORE_TYPE_CONSOLE: | 139 | case PSTORE_TYPE_CONSOLE: |
131 | return check_syslog_permissions(SYSLOG_ACTION_READ_ALL, | 140 | return check_syslog_permissions(SYSLOG_ACTION_READ_ALL, |
@@ -141,9 +150,10 @@ static ssize_t pstore_file_read(struct file *file, char __user *userbuf, | |||
141 | struct seq_file *sf = file->private_data; | 150 | struct seq_file *sf = file->private_data; |
142 | struct pstore_private *ps = sf->private; | 151 | struct pstore_private *ps = sf->private; |
143 | 152 | ||
144 | if (ps->type == PSTORE_TYPE_FTRACE) | 153 | if (ps->record->type == PSTORE_TYPE_FTRACE) |
145 | return seq_read(file, userbuf, count, ppos); | 154 | return seq_read(file, userbuf, count, ppos); |
146 | return simple_read_from_buffer(userbuf, count, ppos, ps->data, ps->size); | 155 | return simple_read_from_buffer(userbuf, count, ppos, |
156 | ps->record->buf, ps->total_size); | ||
147 | } | 157 | } |
148 | 158 | ||
149 | static int pstore_file_open(struct inode *inode, struct file *file) | 159 | static int pstore_file_open(struct inode *inode, struct file *file) |
@@ -157,7 +167,7 @@ static int pstore_file_open(struct inode *inode, struct file *file) | |||
157 | if (err) | 167 | if (err) |
158 | return err; | 168 | return err; |
159 | 169 | ||
160 | if (ps->type == PSTORE_TYPE_FTRACE) | 170 | if (ps->record->type == PSTORE_TYPE_FTRACE) |
161 | sops = &pstore_ftrace_seq_ops; | 171 | sops = &pstore_ftrace_seq_ops; |
162 | 172 | ||
163 | err = seq_open(file, sops); | 173 | err = seq_open(file, sops); |
@@ -193,20 +203,19 @@ static const struct file_operations pstore_file_operations = { | |||
193 | static int pstore_unlink(struct inode *dir, struct dentry *dentry) | 203 | static int pstore_unlink(struct inode *dir, struct dentry *dentry) |
194 | { | 204 | { |
195 | struct pstore_private *p = d_inode(dentry)->i_private; | 205 | struct pstore_private *p = d_inode(dentry)->i_private; |
206 | struct pstore_record *record = p->record; | ||
196 | int err; | 207 | int err; |
197 | 208 | ||
198 | err = pstore_check_syslog_permissions(p); | 209 | err = pstore_check_syslog_permissions(p); |
199 | if (err) | 210 | if (err) |
200 | return err; | 211 | return err; |
201 | 212 | ||
202 | if (p->psi->erase) { | 213 | if (!record->psi->erase) |
203 | mutex_lock(&p->psi->read_mutex); | ||
204 | p->psi->erase(p->type, p->id, p->count, | ||
205 | d_inode(dentry)->i_ctime, p->psi); | ||
206 | mutex_unlock(&p->psi->read_mutex); | ||
207 | } else { | ||
208 | return -EPERM; | 214 | return -EPERM; |
209 | } | 215 | |
216 | mutex_lock(&record->psi->read_mutex); | ||
217 | record->psi->erase(record); | ||
218 | mutex_unlock(&record->psi->read_mutex); | ||
210 | 219 | ||
211 | return simple_unlink(dir, dentry); | 220 | return simple_unlink(dir, dentry); |
212 | } | 221 | } |
@@ -221,7 +230,7 @@ static void pstore_evict_inode(struct inode *inode) | |||
221 | spin_lock_irqsave(&allpstore_lock, flags); | 230 | spin_lock_irqsave(&allpstore_lock, flags); |
222 | list_del(&p->list); | 231 | list_del(&p->list); |
223 | spin_unlock_irqrestore(&allpstore_lock, flags); | 232 | spin_unlock_irqrestore(&allpstore_lock, flags); |
224 | kfree(p); | 233 | free_pstore_private(p); |
225 | } | 234 | } |
226 | } | 235 | } |
227 | 236 | ||
@@ -302,23 +311,23 @@ bool pstore_is_mounted(void) | |||
302 | * Load it up with "size" bytes of data from "buf". | 311 | * Load it up with "size" bytes of data from "buf". |
303 | * Set the mtime & ctime to the date that this record was originally stored. | 312 | * Set the mtime & ctime to the date that this record was originally stored. |
304 | */ | 313 | */ |
305 | int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, | 314 | int pstore_mkfile(struct dentry *root, struct pstore_record *record) |
306 | char *data, bool compressed, size_t size, | ||
307 | struct timespec time, struct pstore_info *psi) | ||
308 | { | 315 | { |
309 | struct dentry *root = pstore_sb->s_root; | ||
310 | struct dentry *dentry; | 316 | struct dentry *dentry; |
311 | struct inode *inode; | 317 | struct inode *inode; |
312 | int rc = 0; | 318 | int rc = 0; |
313 | char name[PSTORE_NAMELEN]; | 319 | char name[PSTORE_NAMELEN]; |
314 | struct pstore_private *private, *pos; | 320 | struct pstore_private *private, *pos; |
315 | unsigned long flags; | 321 | unsigned long flags; |
322 | size_t size = record->size + record->ecc_notice_size; | ||
323 | |||
324 | WARN_ON(!inode_is_locked(d_inode(root))); | ||
316 | 325 | ||
317 | spin_lock_irqsave(&allpstore_lock, flags); | 326 | spin_lock_irqsave(&allpstore_lock, flags); |
318 | list_for_each_entry(pos, &allpstore, list) { | 327 | list_for_each_entry(pos, &allpstore, list) { |
319 | if (pos->type == type && | 328 | if (pos->record->type == record->type && |
320 | pos->id == id && | 329 | pos->record->id == record->id && |
321 | pos->psi == psi) { | 330 | pos->record->psi == record->psi) { |
322 | rc = -EEXIST; | 331 | rc = -EEXIST; |
323 | break; | 332 | break; |
324 | } | 333 | } |
@@ -328,72 +337,74 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, | |||
328 | return rc; | 337 | return rc; |
329 | 338 | ||
330 | rc = -ENOMEM; | 339 | rc = -ENOMEM; |
331 | inode = pstore_get_inode(pstore_sb); | 340 | inode = pstore_get_inode(root->d_sb); |
332 | if (!inode) | 341 | if (!inode) |
333 | goto fail; | 342 | goto fail; |
334 | inode->i_mode = S_IFREG | 0444; | 343 | inode->i_mode = S_IFREG | 0444; |
335 | inode->i_fop = &pstore_file_operations; | 344 | inode->i_fop = &pstore_file_operations; |
336 | private = kmalloc(sizeof *private + size, GFP_KERNEL); | 345 | private = kzalloc(sizeof(*private), GFP_KERNEL); |
337 | if (!private) | 346 | if (!private) |
338 | goto fail_alloc; | 347 | goto fail_alloc; |
339 | private->type = type; | 348 | private->record = record; |
340 | private->id = id; | ||
341 | private->count = count; | ||
342 | private->psi = psi; | ||
343 | 349 | ||
344 | switch (type) { | 350 | switch (record->type) { |
345 | case PSTORE_TYPE_DMESG: | 351 | case PSTORE_TYPE_DMESG: |
346 | scnprintf(name, sizeof(name), "dmesg-%s-%lld%s", | 352 | scnprintf(name, sizeof(name), "dmesg-%s-%lld%s", |
347 | psname, id, compressed ? ".enc.z" : ""); | 353 | record->psi->name, record->id, |
354 | record->compressed ? ".enc.z" : ""); | ||
348 | break; | 355 | break; |
349 | case PSTORE_TYPE_CONSOLE: | 356 | case PSTORE_TYPE_CONSOLE: |
350 | scnprintf(name, sizeof(name), "console-%s-%lld", psname, id); | 357 | scnprintf(name, sizeof(name), "console-%s-%lld", |
358 | record->psi->name, record->id); | ||
351 | break; | 359 | break; |
352 | case PSTORE_TYPE_FTRACE: | 360 | case PSTORE_TYPE_FTRACE: |
353 | scnprintf(name, sizeof(name), "ftrace-%s-%lld", psname, id); | 361 | scnprintf(name, sizeof(name), "ftrace-%s-%lld", |
362 | record->psi->name, record->id); | ||
354 | break; | 363 | break; |
355 | case PSTORE_TYPE_MCE: | 364 | case PSTORE_TYPE_MCE: |
356 | scnprintf(name, sizeof(name), "mce-%s-%lld", psname, id); | 365 | scnprintf(name, sizeof(name), "mce-%s-%lld", |
366 | record->psi->name, record->id); | ||
357 | break; | 367 | break; |
358 | case PSTORE_TYPE_PPC_RTAS: | 368 | case PSTORE_TYPE_PPC_RTAS: |
359 | scnprintf(name, sizeof(name), "rtas-%s-%lld", psname, id); | 369 | scnprintf(name, sizeof(name), "rtas-%s-%lld", |
370 | record->psi->name, record->id); | ||
360 | break; | 371 | break; |
361 | case PSTORE_TYPE_PPC_OF: | 372 | case PSTORE_TYPE_PPC_OF: |
362 | scnprintf(name, sizeof(name), "powerpc-ofw-%s-%lld", | 373 | scnprintf(name, sizeof(name), "powerpc-ofw-%s-%lld", |
363 | psname, id); | 374 | record->psi->name, record->id); |
364 | break; | 375 | break; |
365 | case PSTORE_TYPE_PPC_COMMON: | 376 | case PSTORE_TYPE_PPC_COMMON: |
366 | scnprintf(name, sizeof(name), "powerpc-common-%s-%lld", | 377 | scnprintf(name, sizeof(name), "powerpc-common-%s-%lld", |
367 | psname, id); | 378 | record->psi->name, record->id); |
368 | break; | 379 | break; |
369 | case PSTORE_TYPE_PMSG: | 380 | case PSTORE_TYPE_PMSG: |
370 | scnprintf(name, sizeof(name), "pmsg-%s-%lld", psname, id); | 381 | scnprintf(name, sizeof(name), "pmsg-%s-%lld", |
382 | record->psi->name, record->id); | ||
371 | break; | 383 | break; |
372 | case PSTORE_TYPE_PPC_OPAL: | 384 | case PSTORE_TYPE_PPC_OPAL: |
373 | sprintf(name, "powerpc-opal-%s-%lld", psname, id); | 385 | scnprintf(name, sizeof(name), "powerpc-opal-%s-%lld", |
386 | record->psi->name, record->id); | ||
374 | break; | 387 | break; |
375 | case PSTORE_TYPE_UNKNOWN: | 388 | case PSTORE_TYPE_UNKNOWN: |
376 | scnprintf(name, sizeof(name), "unknown-%s-%lld", psname, id); | 389 | scnprintf(name, sizeof(name), "unknown-%s-%lld", |
390 | record->psi->name, record->id); | ||
377 | break; | 391 | break; |
378 | default: | 392 | default: |
379 | scnprintf(name, sizeof(name), "type%d-%s-%lld", | 393 | scnprintf(name, sizeof(name), "type%d-%s-%lld", |
380 | type, psname, id); | 394 | record->type, record->psi->name, record->id); |
381 | break; | 395 | break; |
382 | } | 396 | } |
383 | 397 | ||
384 | inode_lock(d_inode(root)); | ||
385 | |||
386 | dentry = d_alloc_name(root, name); | 398 | dentry = d_alloc_name(root, name); |
387 | if (!dentry) | 399 | if (!dentry) |
388 | goto fail_lockedalloc; | 400 | goto fail_private; |
389 | 401 | ||
390 | memcpy(private->data, data, size); | 402 | inode->i_size = private->total_size = size; |
391 | inode->i_size = private->size = size; | ||
392 | 403 | ||
393 | inode->i_private = private; | 404 | inode->i_private = private; |
394 | 405 | ||
395 | if (time.tv_sec) | 406 | if (record->time.tv_sec) |
396 | inode->i_mtime = inode->i_ctime = time; | 407 | inode->i_mtime = inode->i_ctime = record->time; |
397 | 408 | ||
398 | d_add(dentry, inode); | 409 | d_add(dentry, inode); |
399 | 410 | ||
@@ -401,13 +412,10 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, | |||
401 | list_add(&private->list, &allpstore); | 412 | list_add(&private->list, &allpstore); |
402 | spin_unlock_irqrestore(&allpstore_lock, flags); | 413 | spin_unlock_irqrestore(&allpstore_lock, flags); |
403 | 414 | ||
404 | inode_unlock(d_inode(root)); | ||
405 | |||
406 | return 0; | 415 | return 0; |
407 | 416 | ||
408 | fail_lockedalloc: | 417 | fail_private: |
409 | inode_unlock(d_inode(root)); | 418 | free_pstore_private(private); |
410 | kfree(private); | ||
411 | fail_alloc: | 419 | fail_alloc: |
412 | iput(inode); | 420 | iput(inode); |
413 | 421 | ||
@@ -415,6 +423,27 @@ fail: | |||
415 | return rc; | 423 | return rc; |
416 | } | 424 | } |
417 | 425 | ||
426 | /* | ||
427 | * Read all the records from the persistent store. Create | ||
428 | * files in our filesystem. Don't warn about -EEXIST errors | ||
429 | * when we are re-scanning the backing store looking to add new | ||
430 | * error records. | ||
431 | */ | ||
432 | void pstore_get_records(int quiet) | ||
433 | { | ||
434 | struct pstore_info *psi = psinfo; | ||
435 | struct dentry *root; | ||
436 | |||
437 | if (!psi || !pstore_sb) | ||
438 | return; | ||
439 | |||
440 | root = pstore_sb->s_root; | ||
441 | |||
442 | inode_lock(d_inode(root)); | ||
443 | pstore_get_backend_records(psi, root, quiet); | ||
444 | inode_unlock(d_inode(root)); | ||
445 | } | ||
446 | |||
418 | static int pstore_fill_super(struct super_block *sb, void *data, int silent) | 447 | static int pstore_fill_super(struct super_block *sb, void *data, int silent) |
419 | { | 448 | { |
420 | struct inode *inode; | 449 | struct inode *inode; |
diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h index da416e6591c9..c416e653dc4f 100644 --- a/fs/pstore/internal.h +++ b/fs/pstore/internal.h | |||
@@ -25,10 +25,10 @@ extern struct pstore_info *psinfo; | |||
25 | 25 | ||
26 | extern void pstore_set_kmsg_bytes(int); | 26 | extern void pstore_set_kmsg_bytes(int); |
27 | extern void pstore_get_records(int); | 27 | extern void pstore_get_records(int); |
28 | extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id, | 28 | extern void pstore_get_backend_records(struct pstore_info *psi, |
29 | int count, char *data, bool compressed, | 29 | struct dentry *root, int quiet); |
30 | size_t size, struct timespec time, | 30 | extern int pstore_mkfile(struct dentry *root, |
31 | struct pstore_info *psi); | 31 | struct pstore_record *record); |
32 | extern bool pstore_is_mounted(void); | 32 | extern bool pstore_is_mounted(void); |
33 | 33 | ||
34 | #endif | 34 | #endif |
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index efab7b64925b..d468eec9b8a6 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c | |||
@@ -267,7 +267,7 @@ static void free_zlib(void) | |||
267 | big_oops_buf_sz = 0; | 267 | big_oops_buf_sz = 0; |
268 | } | 268 | } |
269 | 269 | ||
270 | static struct pstore_zbackend backend_zlib = { | 270 | static const struct pstore_zbackend backend_zlib = { |
271 | .compress = compress_zlib, | 271 | .compress = compress_zlib, |
272 | .decompress = decompress_zlib, | 272 | .decompress = decompress_zlib, |
273 | .allocate = allocate_zlib, | 273 | .allocate = allocate_zlib, |
@@ -328,7 +328,7 @@ static void free_lzo(void) | |||
328 | big_oops_buf_sz = 0; | 328 | big_oops_buf_sz = 0; |
329 | } | 329 | } |
330 | 330 | ||
331 | static struct pstore_zbackend backend_lzo = { | 331 | static const struct pstore_zbackend backend_lzo = { |
332 | .compress = compress_lzo, | 332 | .compress = compress_lzo, |
333 | .decompress = decompress_lzo, | 333 | .decompress = decompress_lzo, |
334 | .allocate = allocate_lzo, | 334 | .allocate = allocate_lzo, |
@@ -393,7 +393,7 @@ static void free_lz4(void) | |||
393 | big_oops_buf_sz = 0; | 393 | big_oops_buf_sz = 0; |
394 | } | 394 | } |
395 | 395 | ||
396 | static struct pstore_zbackend backend_lz4 = { | 396 | static const struct pstore_zbackend backend_lz4 = { |
397 | .compress = compress_lz4, | 397 | .compress = compress_lz4, |
398 | .decompress = decompress_lz4, | 398 | .decompress = decompress_lz4, |
399 | .allocate = allocate_lz4, | 399 | .allocate = allocate_lz4, |
@@ -402,7 +402,7 @@ static struct pstore_zbackend backend_lz4 = { | |||
402 | }; | 402 | }; |
403 | #endif | 403 | #endif |
404 | 404 | ||
405 | static struct pstore_zbackend *zbackend = | 405 | static const struct pstore_zbackend *zbackend = |
406 | #if defined(CONFIG_PSTORE_ZLIB_COMPRESS) | 406 | #if defined(CONFIG_PSTORE_ZLIB_COMPRESS) |
407 | &backend_zlib; | 407 | &backend_zlib; |
408 | #elif defined(CONFIG_PSTORE_LZO_COMPRESS) | 408 | #elif defined(CONFIG_PSTORE_LZO_COMPRESS) |
@@ -484,7 +484,6 @@ static void pstore_dump(struct kmsg_dumper *dumper, | |||
484 | { | 484 | { |
485 | unsigned long total = 0; | 485 | unsigned long total = 0; |
486 | const char *why; | 486 | const char *why; |
487 | u64 id; | ||
488 | unsigned int part = 1; | 487 | unsigned int part = 1; |
489 | unsigned long flags = 0; | 488 | unsigned long flags = 0; |
490 | int is_locked; | 489 | int is_locked; |
@@ -506,48 +505,59 @@ static void pstore_dump(struct kmsg_dumper *dumper, | |||
506 | oopscount++; | 505 | oopscount++; |
507 | while (total < kmsg_bytes) { | 506 | while (total < kmsg_bytes) { |
508 | char *dst; | 507 | char *dst; |
509 | unsigned long size; | 508 | size_t dst_size; |
510 | int hsize; | 509 | int header_size; |
511 | int zipped_len = -1; | 510 | int zipped_len = -1; |
512 | size_t len; | 511 | size_t dump_size; |
513 | bool compressed = false; | 512 | struct pstore_record record = { |
514 | size_t total_len; | 513 | .type = PSTORE_TYPE_DMESG, |
514 | .count = oopscount, | ||
515 | .reason = reason, | ||
516 | .part = part, | ||
517 | .compressed = false, | ||
518 | .buf = psinfo->buf, | ||
519 | .psi = psinfo, | ||
520 | }; | ||
515 | 521 | ||
516 | if (big_oops_buf && is_locked) { | 522 | if (big_oops_buf && is_locked) { |
517 | dst = big_oops_buf; | 523 | dst = big_oops_buf; |
518 | size = big_oops_buf_sz; | 524 | dst_size = big_oops_buf_sz; |
519 | } else { | 525 | } else { |
520 | dst = psinfo->buf; | 526 | dst = psinfo->buf; |
521 | size = psinfo->bufsize; | 527 | dst_size = psinfo->bufsize; |
522 | } | 528 | } |
523 | 529 | ||
524 | hsize = sprintf(dst, "%s#%d Part%u\n", why, oopscount, part); | 530 | /* Write dump header. */ |
525 | size -= hsize; | 531 | header_size = snprintf(dst, dst_size, "%s#%d Part%u\n", why, |
532 | oopscount, part); | ||
533 | dst_size -= header_size; | ||
526 | 534 | ||
527 | if (!kmsg_dump_get_buffer(dumper, true, dst + hsize, | 535 | /* Write dump contents. */ |
528 | size, &len)) | 536 | if (!kmsg_dump_get_buffer(dumper, true, dst + header_size, |
537 | dst_size, &dump_size)) | ||
529 | break; | 538 | break; |
530 | 539 | ||
531 | if (big_oops_buf && is_locked) { | 540 | if (big_oops_buf && is_locked) { |
532 | zipped_len = pstore_compress(dst, psinfo->buf, | 541 | zipped_len = pstore_compress(dst, psinfo->buf, |
533 | hsize + len, psinfo->bufsize); | 542 | header_size + dump_size, |
543 | psinfo->bufsize); | ||
534 | 544 | ||
535 | if (zipped_len > 0) { | 545 | if (zipped_len > 0) { |
536 | compressed = true; | 546 | record.compressed = true; |
537 | total_len = zipped_len; | 547 | record.size = zipped_len; |
538 | } else { | 548 | } else { |
539 | total_len = copy_kmsg_to_buffer(hsize, len); | 549 | record.size = copy_kmsg_to_buffer(header_size, |
550 | dump_size); | ||
540 | } | 551 | } |
541 | } else { | 552 | } else { |
542 | total_len = hsize + len; | 553 | record.size = header_size + dump_size; |
543 | } | 554 | } |
544 | 555 | ||
545 | ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part, | 556 | ret = psinfo->write(&record); |
546 | oopscount, compressed, total_len, psinfo); | ||
547 | if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) | 557 | if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) |
548 | pstore_new_entry = 1; | 558 | pstore_new_entry = 1; |
549 | 559 | ||
550 | total += total_len; | 560 | total += record.size; |
551 | part++; | 561 | part++; |
552 | } | 562 | } |
553 | if (is_locked) | 563 | if (is_locked) |
@@ -577,8 +587,11 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c) | |||
577 | const char *e = s + c; | 587 | const char *e = s + c; |
578 | 588 | ||
579 | while (s < e) { | 589 | while (s < e) { |
590 | struct pstore_record record = { | ||
591 | .type = PSTORE_TYPE_CONSOLE, | ||
592 | .psi = psinfo, | ||
593 | }; | ||
580 | unsigned long flags; | 594 | unsigned long flags; |
581 | u64 id; | ||
582 | 595 | ||
583 | if (c > psinfo->bufsize) | 596 | if (c > psinfo->bufsize) |
584 | c = psinfo->bufsize; | 597 | c = psinfo->bufsize; |
@@ -589,8 +602,9 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c) | |||
589 | } else { | 602 | } else { |
590 | spin_lock_irqsave(&psinfo->buf_lock, flags); | 603 | spin_lock_irqsave(&psinfo->buf_lock, flags); |
591 | } | 604 | } |
592 | psinfo->write_buf(PSTORE_TYPE_CONSOLE, 0, &id, 0, | 605 | record.buf = (char *)s; |
593 | s, 0, c, psinfo); | 606 | record.size = c; |
607 | psinfo->write(&record); | ||
594 | spin_unlock_irqrestore(&psinfo->buf_lock, flags); | 608 | spin_unlock_irqrestore(&psinfo->buf_lock, flags); |
595 | s += c; | 609 | s += c; |
596 | c = e - s; | 610 | c = e - s; |
@@ -618,48 +632,30 @@ static void pstore_register_console(void) {} | |||
618 | static void pstore_unregister_console(void) {} | 632 | static void pstore_unregister_console(void) {} |
619 | #endif | 633 | #endif |
620 | 634 | ||
621 | static int pstore_write_compat(enum pstore_type_id type, | 635 | static int pstore_write_user_compat(struct pstore_record *record, |
622 | enum kmsg_dump_reason reason, | 636 | const char __user *buf) |
623 | u64 *id, unsigned int part, int count, | 637 | { |
624 | bool compressed, size_t size, | 638 | int ret = 0; |
625 | struct pstore_info *psi) | 639 | |
626 | { | 640 | if (record->buf) |
627 | return psi->write_buf(type, reason, id, part, psinfo->buf, compressed, | 641 | return -EINVAL; |
628 | size, psi); | 642 | |
629 | } | 643 | record->buf = kmalloc(record->size, GFP_KERNEL); |
630 | 644 | if (!record->buf) | |
631 | static int pstore_write_buf_user_compat(enum pstore_type_id type, | 645 | return -ENOMEM; |
632 | enum kmsg_dump_reason reason, | 646 | |
633 | u64 *id, unsigned int part, | 647 | if (unlikely(copy_from_user(record->buf, buf, record->size))) { |
634 | const char __user *buf, | 648 | ret = -EFAULT; |
635 | bool compressed, size_t size, | 649 | goto out; |
636 | struct pstore_info *psi) | ||
637 | { | ||
638 | unsigned long flags = 0; | ||
639 | size_t i, bufsize = size; | ||
640 | long ret = 0; | ||
641 | |||
642 | if (unlikely(!access_ok(VERIFY_READ, buf, size))) | ||
643 | return -EFAULT; | ||
644 | if (bufsize > psinfo->bufsize) | ||
645 | bufsize = psinfo->bufsize; | ||
646 | spin_lock_irqsave(&psinfo->buf_lock, flags); | ||
647 | for (i = 0; i < size; ) { | ||
648 | size_t c = min(size - i, bufsize); | ||
649 | |||
650 | ret = __copy_from_user(psinfo->buf, buf + i, c); | ||
651 | if (unlikely(ret != 0)) { | ||
652 | ret = -EFAULT; | ||
653 | break; | ||
654 | } | ||
655 | ret = psi->write_buf(type, reason, id, part, psinfo->buf, | ||
656 | compressed, c, psi); | ||
657 | if (unlikely(ret < 0)) | ||
658 | break; | ||
659 | i += c; | ||
660 | } | 650 | } |
661 | spin_unlock_irqrestore(&psinfo->buf_lock, flags); | 651 | |
662 | return unlikely(ret < 0) ? ret : size; | 652 | ret = record->psi->write(record); |
653 | |||
654 | out: | ||
655 | kfree(record->buf); | ||
656 | record->buf = NULL; | ||
657 | |||
658 | return unlikely(ret < 0) ? ret : record->size; | ||
663 | } | 659 | } |
664 | 660 | ||
665 | /* | 661 | /* |
@@ -673,19 +669,35 @@ int pstore_register(struct pstore_info *psi) | |||
673 | { | 669 | { |
674 | struct module *owner = psi->owner; | 670 | struct module *owner = psi->owner; |
675 | 671 | ||
676 | if (backend && strcmp(backend, psi->name)) | 672 | if (backend && strcmp(backend, psi->name)) { |
673 | pr_warn("ignoring unexpected backend '%s'\n", psi->name); | ||
677 | return -EPERM; | 674 | return -EPERM; |
675 | } | ||
676 | |||
677 | /* Sanity check flags. */ | ||
678 | if (!psi->flags) { | ||
679 | pr_warn("backend '%s' must support at least one frontend\n", | ||
680 | psi->name); | ||
681 | return -EINVAL; | ||
682 | } | ||
683 | |||
684 | /* Check for required functions. */ | ||
685 | if (!psi->read || !psi->write) { | ||
686 | pr_warn("backend '%s' must implement read() and write()\n", | ||
687 | psi->name); | ||
688 | return -EINVAL; | ||
689 | } | ||
678 | 690 | ||
679 | spin_lock(&pstore_lock); | 691 | spin_lock(&pstore_lock); |
680 | if (psinfo) { | 692 | if (psinfo) { |
693 | pr_warn("backend '%s' already loaded: ignoring '%s'\n", | ||
694 | psinfo->name, psi->name); | ||
681 | spin_unlock(&pstore_lock); | 695 | spin_unlock(&pstore_lock); |
682 | return -EBUSY; | 696 | return -EBUSY; |
683 | } | 697 | } |
684 | 698 | ||
685 | if (!psi->write) | 699 | if (!psi->write_user) |
686 | psi->write = pstore_write_compat; | 700 | psi->write_user = pstore_write_user_compat; |
687 | if (!psi->write_buf_user) | ||
688 | psi->write_buf_user = pstore_write_buf_user_compat; | ||
689 | psinfo = psi; | 701 | psinfo = psi; |
690 | mutex_init(&psinfo->read_mutex); | 702 | mutex_init(&psinfo->read_mutex); |
691 | spin_unlock(&pstore_lock); | 703 | spin_unlock(&pstore_lock); |
@@ -709,6 +721,7 @@ int pstore_register(struct pstore_info *psi) | |||
709 | if (psi->flags & PSTORE_FLAGS_PMSG) | 721 | if (psi->flags & PSTORE_FLAGS_PMSG) |
710 | pstore_register_pmsg(); | 722 | pstore_register_pmsg(); |
711 | 723 | ||
724 | /* Start watching for new records, if desired. */ | ||
712 | if (pstore_update_ms >= 0) { | 725 | if (pstore_update_ms >= 0) { |
713 | pstore_timer.expires = jiffies + | 726 | pstore_timer.expires = jiffies + |
714 | msecs_to_jiffies(pstore_update_ms); | 727 | msecs_to_jiffies(pstore_update_ms); |
@@ -721,16 +734,21 @@ int pstore_register(struct pstore_info *psi) | |||
721 | */ | 734 | */ |
722 | backend = psi->name; | 735 | backend = psi->name; |
723 | 736 | ||
724 | module_put(owner); | ||
725 | |||
726 | pr_info("Registered %s as persistent store backend\n", psi->name); | 737 | pr_info("Registered %s as persistent store backend\n", psi->name); |
727 | 738 | ||
739 | module_put(owner); | ||
740 | |||
728 | return 0; | 741 | return 0; |
729 | } | 742 | } |
730 | EXPORT_SYMBOL_GPL(pstore_register); | 743 | EXPORT_SYMBOL_GPL(pstore_register); |
731 | 744 | ||
732 | void pstore_unregister(struct pstore_info *psi) | 745 | void pstore_unregister(struct pstore_info *psi) |
733 | { | 746 | { |
747 | /* Stop timer and make sure all work has finished. */ | ||
748 | pstore_update_ms = -1; | ||
749 | del_timer_sync(&pstore_timer); | ||
750 | flush_work(&pstore_work); | ||
751 | |||
734 | if (psi->flags & PSTORE_FLAGS_PMSG) | 752 | if (psi->flags & PSTORE_FLAGS_PMSG) |
735 | pstore_unregister_pmsg(); | 753 | pstore_unregister_pmsg(); |
736 | if (psi->flags & PSTORE_FLAGS_FTRACE) | 754 | if (psi->flags & PSTORE_FLAGS_FTRACE) |
@@ -747,66 +765,99 @@ void pstore_unregister(struct pstore_info *psi) | |||
747 | } | 765 | } |
748 | EXPORT_SYMBOL_GPL(pstore_unregister); | 766 | EXPORT_SYMBOL_GPL(pstore_unregister); |
749 | 767 | ||
768 | static void decompress_record(struct pstore_record *record) | ||
769 | { | ||
770 | int unzipped_len; | ||
771 | char *decompressed; | ||
772 | |||
773 | /* Only PSTORE_TYPE_DMESG support compression. */ | ||
774 | if (!record->compressed || record->type != PSTORE_TYPE_DMESG) { | ||
775 | pr_warn("ignored compressed record type %d\n", record->type); | ||
776 | return; | ||
777 | } | ||
778 | |||
779 | /* No compression method has created the common buffer. */ | ||
780 | if (!big_oops_buf) { | ||
781 | pr_warn("no decompression buffer allocated\n"); | ||
782 | return; | ||
783 | } | ||
784 | |||
785 | unzipped_len = pstore_decompress(record->buf, big_oops_buf, | ||
786 | record->size, big_oops_buf_sz); | ||
787 | if (unzipped_len <= 0) { | ||
788 | pr_err("decompression failed: %d\n", unzipped_len); | ||
789 | return; | ||
790 | } | ||
791 | |||
792 | /* Build new buffer for decompressed contents. */ | ||
793 | decompressed = kmalloc(unzipped_len + record->ecc_notice_size, | ||
794 | GFP_KERNEL); | ||
795 | if (!decompressed) { | ||
796 | pr_err("decompression ran out of memory\n"); | ||
797 | return; | ||
798 | } | ||
799 | memcpy(decompressed, big_oops_buf, unzipped_len); | ||
800 | |||
801 | /* Append ECC notice to decompressed buffer. */ | ||
802 | memcpy(decompressed + unzipped_len, record->buf + record->size, | ||
803 | record->ecc_notice_size); | ||
804 | |||
805 | /* Swap out compresed contents with decompressed contents. */ | ||
806 | kfree(record->buf); | ||
807 | record->buf = decompressed; | ||
808 | record->size = unzipped_len; | ||
809 | record->compressed = false; | ||
810 | } | ||
811 | |||
750 | /* | 812 | /* |
751 | * Read all the records from the persistent store. Create | 813 | * Read all the records from one persistent store backend. Create |
752 | * files in our filesystem. Don't warn about -EEXIST errors | 814 | * files in our filesystem. Don't warn about -EEXIST errors |
753 | * when we are re-scanning the backing store looking to add new | 815 | * when we are re-scanning the backing store looking to add new |
754 | * error records. | 816 | * error records. |
755 | */ | 817 | */ |
756 | void pstore_get_records(int quiet) | 818 | void pstore_get_backend_records(struct pstore_info *psi, |
757 | { | 819 | struct dentry *root, int quiet) |
758 | struct pstore_info *psi = psinfo; | 820 | { |
759 | char *buf = NULL; | 821 | int failed = 0; |
760 | ssize_t size; | 822 | |
761 | u64 id; | 823 | if (!psi || !root) |
762 | int count; | ||
763 | enum pstore_type_id type; | ||
764 | struct timespec time; | ||
765 | int failed = 0, rc; | ||
766 | bool compressed; | ||
767 | int unzipped_len = -1; | ||
768 | ssize_t ecc_notice_size = 0; | ||
769 | |||
770 | if (!psi) | ||
771 | return; | 824 | return; |
772 | 825 | ||
773 | mutex_lock(&psi->read_mutex); | 826 | mutex_lock(&psi->read_mutex); |
774 | if (psi->open && psi->open(psi)) | 827 | if (psi->open && psi->open(psi)) |
775 | goto out; | 828 | goto out; |
776 | 829 | ||
777 | while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed, | 830 | /* |
778 | &ecc_notice_size, psi)) > 0) { | 831 | * Backend callback read() allocates record.buf. decompress_record() |
779 | if (compressed && (type == PSTORE_TYPE_DMESG)) { | 832 | * may reallocate record.buf. On success, pstore_mkfile() will keep |
780 | if (big_oops_buf) | 833 | * the record.buf, so free it only on failure. |
781 | unzipped_len = pstore_decompress(buf, | 834 | */ |
782 | big_oops_buf, size, | 835 | for (;;) { |
783 | big_oops_buf_sz); | 836 | struct pstore_record *record; |
784 | 837 | int rc; | |
785 | if (unzipped_len > 0) { | 838 | |
786 | if (ecc_notice_size) | 839 | record = kzalloc(sizeof(*record), GFP_KERNEL); |
787 | memcpy(big_oops_buf + unzipped_len, | 840 | if (!record) { |
788 | buf + size, ecc_notice_size); | 841 | pr_err("out of memory creating record\n"); |
789 | kfree(buf); | 842 | break; |
790 | buf = big_oops_buf; | 843 | } |
791 | size = unzipped_len; | 844 | record->psi = psi; |
792 | compressed = false; | 845 | |
793 | } else { | 846 | record->size = psi->read(record); |
794 | pr_err("decompression failed;returned %d\n", | 847 | |
795 | unzipped_len); | 848 | /* No more records left in backend? */ |
796 | compressed = true; | 849 | if (record->size <= 0) |
797 | } | 850 | break; |
851 | |||
852 | decompress_record(record); | ||
853 | rc = pstore_mkfile(root, record); | ||
854 | if (rc) { | ||
855 | /* pstore_mkfile() did not take record, so free it. */ | ||
856 | kfree(record->buf); | ||
857 | kfree(record); | ||
858 | if (rc != -EEXIST || !quiet) | ||
859 | failed++; | ||
798 | } | 860 | } |
799 | rc = pstore_mkfile(type, psi->name, id, count, buf, | ||
800 | compressed, size + ecc_notice_size, | ||
801 | time, psi); | ||
802 | if (unzipped_len < 0) { | ||
803 | /* Free buffer other than big oops */ | ||
804 | kfree(buf); | ||
805 | buf = NULL; | ||
806 | } else | ||
807 | unzipped_len = -1; | ||
808 | if (rc && (rc != -EEXIST || !quiet)) | ||
809 | failed++; | ||
810 | } | 861 | } |
811 | if (psi->close) | 862 | if (psi->close) |
812 | psi->close(psi); | 863 | psi->close(psi); |
@@ -830,7 +881,9 @@ static void pstore_timefunc(unsigned long dummy) | |||
830 | schedule_work(&pstore_work); | 881 | schedule_work(&pstore_work); |
831 | } | 882 | } |
832 | 883 | ||
833 | mod_timer(&pstore_timer, jiffies + msecs_to_jiffies(pstore_update_ms)); | 884 | if (pstore_update_ms >= 0) |
885 | mod_timer(&pstore_timer, | ||
886 | jiffies + msecs_to_jiffies(pstore_update_ms)); | ||
834 | } | 887 | } |
835 | 888 | ||
836 | module_param(backend, charp, 0444); | 889 | module_param(backend, charp, 0444); |
diff --git a/fs/pstore/pmsg.c b/fs/pstore/pmsg.c index 78f6176c020f..209755e0d7c8 100644 --- a/fs/pstore/pmsg.c +++ b/fs/pstore/pmsg.c | |||
@@ -15,7 +15,6 @@ | |||
15 | #include <linux/device.h> | 15 | #include <linux/device.h> |
16 | #include <linux/fs.h> | 16 | #include <linux/fs.h> |
17 | #include <linux/uaccess.h> | 17 | #include <linux/uaccess.h> |
18 | #include <linux/vmalloc.h> | ||
19 | #include "internal.h" | 18 | #include "internal.h" |
20 | 19 | ||
21 | static DEFINE_MUTEX(pmsg_lock); | 20 | static DEFINE_MUTEX(pmsg_lock); |
@@ -23,19 +22,22 @@ static DEFINE_MUTEX(pmsg_lock); | |||
23 | static ssize_t write_pmsg(struct file *file, const char __user *buf, | 22 | static ssize_t write_pmsg(struct file *file, const char __user *buf, |
24 | size_t count, loff_t *ppos) | 23 | size_t count, loff_t *ppos) |
25 | { | 24 | { |
26 | u64 id; | 25 | struct pstore_record record = { |
26 | .type = PSTORE_TYPE_PMSG, | ||
27 | .size = count, | ||
28 | .psi = psinfo, | ||
29 | }; | ||
27 | int ret; | 30 | int ret; |
28 | 31 | ||
29 | if (!count) | 32 | if (!count) |
30 | return 0; | 33 | return 0; |
31 | 34 | ||
32 | /* check outside lock, page in any data. write_buf_user also checks */ | 35 | /* check outside lock, page in any data. write_user also checks */ |
33 | if (!access_ok(VERIFY_READ, buf, count)) | 36 | if (!access_ok(VERIFY_READ, buf, count)) |
34 | return -EFAULT; | 37 | return -EFAULT; |
35 | 38 | ||
36 | mutex_lock(&pmsg_lock); | 39 | mutex_lock(&pmsg_lock); |
37 | ret = psinfo->write_buf_user(PSTORE_TYPE_PMSG, 0, &id, 0, buf, 0, count, | 40 | ret = psinfo->write_user(&record, buf); |
38 | psinfo); | ||
39 | mutex_unlock(&pmsg_lock); | 41 | mutex_unlock(&pmsg_lock); |
40 | return ret ? ret : count; | 42 | return ret ? ret : count; |
41 | } | 43 | } |
diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 11f918d34b1e..5523df7f17ef 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c | |||
@@ -235,35 +235,34 @@ static ssize_t ftrace_log_combine(struct persistent_ram_zone *dest, | |||
235 | return 0; | 235 | return 0; |
236 | } | 236 | } |
237 | 237 | ||
238 | static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, | 238 | static ssize_t ramoops_pstore_read(struct pstore_record *record) |
239 | int *count, struct timespec *time, | ||
240 | char **buf, bool *compressed, | ||
241 | ssize_t *ecc_notice_size, | ||
242 | struct pstore_info *psi) | ||
243 | { | 239 | { |
244 | ssize_t size = 0; | 240 | ssize_t size = 0; |
245 | struct ramoops_context *cxt = psi->data; | 241 | struct ramoops_context *cxt = record->psi->data; |
246 | struct persistent_ram_zone *prz = NULL; | 242 | struct persistent_ram_zone *prz = NULL; |
247 | int header_length = 0; | 243 | int header_length = 0; |
248 | bool free_prz = false; | 244 | bool free_prz = false; |
249 | 245 | ||
250 | /* Ramoops headers provide time stamps for PSTORE_TYPE_DMESG, but | 246 | /* |
247 | * Ramoops headers provide time stamps for PSTORE_TYPE_DMESG, but | ||
251 | * PSTORE_TYPE_CONSOLE and PSTORE_TYPE_FTRACE don't currently have | 248 | * PSTORE_TYPE_CONSOLE and PSTORE_TYPE_FTRACE don't currently have |
252 | * valid time stamps, so it is initialized to zero. | 249 | * valid time stamps, so it is initialized to zero. |
253 | */ | 250 | */ |
254 | time->tv_sec = 0; | 251 | record->time.tv_sec = 0; |
255 | time->tv_nsec = 0; | 252 | record->time.tv_nsec = 0; |
256 | *compressed = false; | 253 | record->compressed = false; |
257 | 254 | ||
258 | /* Find the next valid persistent_ram_zone for DMESG */ | 255 | /* Find the next valid persistent_ram_zone for DMESG */ |
259 | while (cxt->dump_read_cnt < cxt->max_dump_cnt && !prz) { | 256 | while (cxt->dump_read_cnt < cxt->max_dump_cnt && !prz) { |
260 | prz = ramoops_get_next_prz(cxt->dprzs, &cxt->dump_read_cnt, | 257 | prz = ramoops_get_next_prz(cxt->dprzs, &cxt->dump_read_cnt, |
261 | cxt->max_dump_cnt, id, type, | 258 | cxt->max_dump_cnt, &record->id, |
259 | &record->type, | ||
262 | PSTORE_TYPE_DMESG, 1); | 260 | PSTORE_TYPE_DMESG, 1); |
263 | if (!prz_ok(prz)) | 261 | if (!prz_ok(prz)) |
264 | continue; | 262 | continue; |
265 | header_length = ramoops_read_kmsg_hdr(persistent_ram_old(prz), | 263 | header_length = ramoops_read_kmsg_hdr(persistent_ram_old(prz), |
266 | time, compressed); | 264 | &record->time, |
265 | &record->compressed); | ||
267 | /* Clear and skip this DMESG record if it has no valid header */ | 266 | /* Clear and skip this DMESG record if it has no valid header */ |
268 | if (!header_length) { | 267 | if (!header_length) { |
269 | persistent_ram_free_old(prz); | 268 | persistent_ram_free_old(prz); |
@@ -274,18 +273,20 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, | |||
274 | 273 | ||
275 | if (!prz_ok(prz)) | 274 | if (!prz_ok(prz)) |
276 | prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt, | 275 | prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt, |
277 | 1, id, type, PSTORE_TYPE_CONSOLE, 0); | 276 | 1, &record->id, &record->type, |
277 | PSTORE_TYPE_CONSOLE, 0); | ||
278 | 278 | ||
279 | if (!prz_ok(prz)) | 279 | if (!prz_ok(prz)) |
280 | prz = ramoops_get_next_prz(&cxt->mprz, &cxt->pmsg_read_cnt, | 280 | prz = ramoops_get_next_prz(&cxt->mprz, &cxt->pmsg_read_cnt, |
281 | 1, id, type, PSTORE_TYPE_PMSG, 0); | 281 | 1, &record->id, &record->type, |
282 | PSTORE_TYPE_PMSG, 0); | ||
282 | 283 | ||
283 | /* ftrace is last since it may want to dynamically allocate memory. */ | 284 | /* ftrace is last since it may want to dynamically allocate memory. */ |
284 | if (!prz_ok(prz)) { | 285 | if (!prz_ok(prz)) { |
285 | if (!(cxt->flags & RAMOOPS_FLAG_FTRACE_PER_CPU)) { | 286 | if (!(cxt->flags & RAMOOPS_FLAG_FTRACE_PER_CPU)) { |
286 | prz = ramoops_get_next_prz(cxt->fprzs, | 287 | prz = ramoops_get_next_prz(cxt->fprzs, |
287 | &cxt->ftrace_read_cnt, 1, id, type, | 288 | &cxt->ftrace_read_cnt, 1, &record->id, |
288 | PSTORE_TYPE_FTRACE, 0); | 289 | &record->type, PSTORE_TYPE_FTRACE, 0); |
289 | } else { | 290 | } else { |
290 | /* | 291 | /* |
291 | * Build a new dummy record which combines all the | 292 | * Build a new dummy record which combines all the |
@@ -302,8 +303,10 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, | |||
302 | while (cxt->ftrace_read_cnt < cxt->max_ftrace_cnt) { | 303 | while (cxt->ftrace_read_cnt < cxt->max_ftrace_cnt) { |
303 | prz_next = ramoops_get_next_prz(cxt->fprzs, | 304 | prz_next = ramoops_get_next_prz(cxt->fprzs, |
304 | &cxt->ftrace_read_cnt, | 305 | &cxt->ftrace_read_cnt, |
305 | cxt->max_ftrace_cnt, id, | 306 | cxt->max_ftrace_cnt, |
306 | type, PSTORE_TYPE_FTRACE, 0); | 307 | &record->id, |
308 | &record->type, | ||
309 | PSTORE_TYPE_FTRACE, 0); | ||
307 | 310 | ||
308 | if (!prz_ok(prz_next)) | 311 | if (!prz_ok(prz_next)) |
309 | continue; | 312 | continue; |
@@ -316,7 +319,7 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, | |||
316 | if (size) | 319 | if (size) |
317 | goto out; | 320 | goto out; |
318 | } | 321 | } |
319 | *id = 0; | 322 | record->id = 0; |
320 | prz = tmp_prz; | 323 | prz = tmp_prz; |
321 | } | 324 | } |
322 | } | 325 | } |
@@ -329,17 +332,19 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, | |||
329 | size = persistent_ram_old_size(prz) - header_length; | 332 | size = persistent_ram_old_size(prz) - header_length; |
330 | 333 | ||
331 | /* ECC correction notice */ | 334 | /* ECC correction notice */ |
332 | *ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0); | 335 | record->ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0); |
333 | 336 | ||
334 | *buf = kmalloc(size + *ecc_notice_size + 1, GFP_KERNEL); | 337 | record->buf = kmalloc(size + record->ecc_notice_size + 1, GFP_KERNEL); |
335 | if (*buf == NULL) { | 338 | if (record->buf == NULL) { |
336 | size = -ENOMEM; | 339 | size = -ENOMEM; |
337 | goto out; | 340 | goto out; |
338 | } | 341 | } |
339 | 342 | ||
340 | memcpy(*buf, (char *)persistent_ram_old(prz) + header_length, size); | 343 | memcpy(record->buf, (char *)persistent_ram_old(prz) + header_length, |
344 | size); | ||
341 | 345 | ||
342 | persistent_ram_ecc_string(prz, *buf + size, *ecc_notice_size + 1); | 346 | persistent_ram_ecc_string(prz, record->buf + size, |
347 | record->ecc_notice_size + 1); | ||
343 | 348 | ||
344 | out: | 349 | out: |
345 | if (free_prz) { | 350 | if (free_prz) { |
@@ -373,23 +378,18 @@ static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz, | |||
373 | return len; | 378 | return len; |
374 | } | 379 | } |
375 | 380 | ||
376 | static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, | 381 | static int notrace ramoops_pstore_write(struct pstore_record *record) |
377 | enum kmsg_dump_reason reason, | ||
378 | u64 *id, unsigned int part, | ||
379 | const char *buf, | ||
380 | bool compressed, size_t size, | ||
381 | struct pstore_info *psi) | ||
382 | { | 382 | { |
383 | struct ramoops_context *cxt = psi->data; | 383 | struct ramoops_context *cxt = record->psi->data; |
384 | struct persistent_ram_zone *prz; | 384 | struct persistent_ram_zone *prz; |
385 | size_t hlen; | 385 | size_t size, hlen; |
386 | 386 | ||
387 | if (type == PSTORE_TYPE_CONSOLE) { | 387 | if (record->type == PSTORE_TYPE_CONSOLE) { |
388 | if (!cxt->cprz) | 388 | if (!cxt->cprz) |
389 | return -ENOMEM; | 389 | return -ENOMEM; |
390 | persistent_ram_write(cxt->cprz, buf, size); | 390 | persistent_ram_write(cxt->cprz, record->buf, record->size); |
391 | return 0; | 391 | return 0; |
392 | } else if (type == PSTORE_TYPE_FTRACE) { | 392 | } else if (record->type == PSTORE_TYPE_FTRACE) { |
393 | int zonenum; | 393 | int zonenum; |
394 | 394 | ||
395 | if (!cxt->fprzs) | 395 | if (!cxt->fprzs) |
@@ -402,33 +402,36 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, | |||
402 | else | 402 | else |
403 | zonenum = 0; | 403 | zonenum = 0; |
404 | 404 | ||
405 | persistent_ram_write(cxt->fprzs[zonenum], buf, size); | 405 | persistent_ram_write(cxt->fprzs[zonenum], record->buf, |
406 | record->size); | ||
406 | return 0; | 407 | return 0; |
407 | } else if (type == PSTORE_TYPE_PMSG) { | 408 | } else if (record->type == PSTORE_TYPE_PMSG) { |
408 | pr_warn_ratelimited("PMSG shouldn't call %s\n", __func__); | 409 | pr_warn_ratelimited("PMSG shouldn't call %s\n", __func__); |
409 | return -EINVAL; | 410 | return -EINVAL; |
410 | } | 411 | } |
411 | 412 | ||
412 | if (type != PSTORE_TYPE_DMESG) | 413 | if (record->type != PSTORE_TYPE_DMESG) |
413 | return -EINVAL; | 414 | return -EINVAL; |
414 | 415 | ||
415 | /* Out of the various dmesg dump types, ramoops is currently designed | 416 | /* |
417 | * Out of the various dmesg dump types, ramoops is currently designed | ||
416 | * to only store crash logs, rather than storing general kernel logs. | 418 | * to only store crash logs, rather than storing general kernel logs. |
417 | */ | 419 | */ |
418 | if (reason != KMSG_DUMP_OOPS && | 420 | if (record->reason != KMSG_DUMP_OOPS && |
419 | reason != KMSG_DUMP_PANIC) | 421 | record->reason != KMSG_DUMP_PANIC) |
420 | return -EINVAL; | 422 | return -EINVAL; |
421 | 423 | ||
422 | /* Skip Oopes when configured to do so. */ | 424 | /* Skip Oopes when configured to do so. */ |
423 | if (reason == KMSG_DUMP_OOPS && !cxt->dump_oops) | 425 | if (record->reason == KMSG_DUMP_OOPS && !cxt->dump_oops) |
424 | return -EINVAL; | 426 | return -EINVAL; |
425 | 427 | ||
426 | /* Explicitly only take the first part of any new crash. | 428 | /* |
429 | * Explicitly only take the first part of any new crash. | ||
427 | * If our buffer is larger than kmsg_bytes, this can never happen, | 430 | * If our buffer is larger than kmsg_bytes, this can never happen, |
428 | * and if our buffer is smaller than kmsg_bytes, we don't want the | 431 | * and if our buffer is smaller than kmsg_bytes, we don't want the |
429 | * report split across multiple records. | 432 | * report split across multiple records. |
430 | */ | 433 | */ |
431 | if (part != 1) | 434 | if (record->part != 1) |
432 | return -ENOSPC; | 435 | return -ENOSPC; |
433 | 436 | ||
434 | if (!cxt->dprzs) | 437 | if (!cxt->dprzs) |
@@ -436,53 +439,50 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, | |||
436 | 439 | ||
437 | prz = cxt->dprzs[cxt->dump_write_cnt]; | 440 | prz = cxt->dprzs[cxt->dump_write_cnt]; |
438 | 441 | ||
439 | hlen = ramoops_write_kmsg_hdr(prz, compressed); | 442 | /* Build header and append record contents. */ |
443 | hlen = ramoops_write_kmsg_hdr(prz, record->compressed); | ||
444 | size = record->size; | ||
440 | if (size + hlen > prz->buffer_size) | 445 | if (size + hlen > prz->buffer_size) |
441 | size = prz->buffer_size - hlen; | 446 | size = prz->buffer_size - hlen; |
442 | persistent_ram_write(prz, buf, size); | 447 | persistent_ram_write(prz, record->buf, size); |
443 | 448 | ||
444 | cxt->dump_write_cnt = (cxt->dump_write_cnt + 1) % cxt->max_dump_cnt; | 449 | cxt->dump_write_cnt = (cxt->dump_write_cnt + 1) % cxt->max_dump_cnt; |
445 | 450 | ||
446 | return 0; | 451 | return 0; |
447 | } | 452 | } |
448 | 453 | ||
449 | static int notrace ramoops_pstore_write_buf_user(enum pstore_type_id type, | 454 | static int notrace ramoops_pstore_write_user(struct pstore_record *record, |
450 | enum kmsg_dump_reason reason, | 455 | const char __user *buf) |
451 | u64 *id, unsigned int part, | ||
452 | const char __user *buf, | ||
453 | bool compressed, size_t size, | ||
454 | struct pstore_info *psi) | ||
455 | { | 456 | { |
456 | if (type == PSTORE_TYPE_PMSG) { | 457 | if (record->type == PSTORE_TYPE_PMSG) { |
457 | struct ramoops_context *cxt = psi->data; | 458 | struct ramoops_context *cxt = record->psi->data; |
458 | 459 | ||
459 | if (!cxt->mprz) | 460 | if (!cxt->mprz) |
460 | return -ENOMEM; | 461 | return -ENOMEM; |
461 | return persistent_ram_write_user(cxt->mprz, buf, size); | 462 | return persistent_ram_write_user(cxt->mprz, buf, record->size); |
462 | } | 463 | } |
463 | 464 | ||
464 | return -EINVAL; | 465 | return -EINVAL; |
465 | } | 466 | } |
466 | 467 | ||
467 | static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count, | 468 | static int ramoops_pstore_erase(struct pstore_record *record) |
468 | struct timespec time, struct pstore_info *psi) | ||
469 | { | 469 | { |
470 | struct ramoops_context *cxt = psi->data; | 470 | struct ramoops_context *cxt = record->psi->data; |
471 | struct persistent_ram_zone *prz; | 471 | struct persistent_ram_zone *prz; |
472 | 472 | ||
473 | switch (type) { | 473 | switch (record->type) { |
474 | case PSTORE_TYPE_DMESG: | 474 | case PSTORE_TYPE_DMESG: |
475 | if (id >= cxt->max_dump_cnt) | 475 | if (record->id >= cxt->max_dump_cnt) |
476 | return -EINVAL; | 476 | return -EINVAL; |
477 | prz = cxt->dprzs[id]; | 477 | prz = cxt->dprzs[record->id]; |
478 | break; | 478 | break; |
479 | case PSTORE_TYPE_CONSOLE: | 479 | case PSTORE_TYPE_CONSOLE: |
480 | prz = cxt->cprz; | 480 | prz = cxt->cprz; |
481 | break; | 481 | break; |
482 | case PSTORE_TYPE_FTRACE: | 482 | case PSTORE_TYPE_FTRACE: |
483 | if (id >= cxt->max_ftrace_cnt) | 483 | if (record->id >= cxt->max_ftrace_cnt) |
484 | return -EINVAL; | 484 | return -EINVAL; |
485 | prz = cxt->fprzs[id]; | 485 | prz = cxt->fprzs[record->id]; |
486 | break; | 486 | break; |
487 | case PSTORE_TYPE_PMSG: | 487 | case PSTORE_TYPE_PMSG: |
488 | prz = cxt->mprz; | 488 | prz = cxt->mprz; |
@@ -503,8 +503,8 @@ static struct ramoops_context oops_cxt = { | |||
503 | .name = "ramoops", | 503 | .name = "ramoops", |
504 | .open = ramoops_pstore_open, | 504 | .open = ramoops_pstore_open, |
505 | .read = ramoops_pstore_read, | 505 | .read = ramoops_pstore_read, |
506 | .write_buf = ramoops_pstore_write_buf, | 506 | .write = ramoops_pstore_write, |
507 | .write_buf_user = ramoops_pstore_write_buf_user, | 507 | .write_user = ramoops_pstore_write_user, |
508 | .erase = ramoops_pstore_erase, | 508 | .erase = ramoops_pstore_erase, |
509 | }, | 509 | }, |
510 | }; | 510 | }; |
diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c index bc927e30bdcc..e11672aa4575 100644 --- a/fs/pstore/ram_core.c +++ b/fs/pstore/ram_core.c | |||
@@ -532,7 +532,7 @@ struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, | |||
532 | } | 532 | } |
533 | 533 | ||
534 | /* Initialize general buffer state. */ | 534 | /* Initialize general buffer state. */ |
535 | prz->buffer_lock = __RAW_SPIN_LOCK_UNLOCKED(buffer_lock); | 535 | raw_spin_lock_init(&prz->buffer_lock); |
536 | prz->flags = flags; | 536 | prz->flags = flags; |
537 | 537 | ||
538 | ret = persistent_ram_buffer_map(start, size, prz, memtype); | 538 | ret = persistent_ram_buffer_map(start, size, prz, memtype); |
diff --git a/include/linux/pstore.h b/include/linux/pstore.h index 0da29cae009b..e2233f50f428 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h | |||
@@ -30,7 +30,9 @@ | |||
30 | #include <linux/time.h> | 30 | #include <linux/time.h> |
31 | #include <linux/types.h> | 31 | #include <linux/types.h> |
32 | 32 | ||
33 | /* types */ | 33 | struct module; |
34 | |||
35 | /* pstore record types (see fs/pstore/inode.c for filename templates) */ | ||
34 | enum pstore_type_id { | 36 | enum pstore_type_id { |
35 | PSTORE_TYPE_DMESG = 0, | 37 | PSTORE_TYPE_DMESG = 0, |
36 | PSTORE_TYPE_MCE = 1, | 38 | PSTORE_TYPE_MCE = 1, |
@@ -45,42 +47,146 @@ enum pstore_type_id { | |||
45 | PSTORE_TYPE_UNKNOWN = 255 | 47 | PSTORE_TYPE_UNKNOWN = 255 |
46 | }; | 48 | }; |
47 | 49 | ||
48 | struct module; | 50 | struct pstore_info; |
51 | /** | ||
52 | * struct pstore_record - details of a pstore record entry | ||
53 | * @psi: pstore backend driver information | ||
54 | * @type: pstore record type | ||
55 | * @id: per-type unique identifier for record | ||
56 | * @time: timestamp of the record | ||
57 | * @buf: pointer to record contents | ||
58 | * @size: size of @buf | ||
59 | * @ecc_notice_size: | ||
60 | * ECC information for @buf | ||
61 | * | ||
62 | * Valid for PSTORE_TYPE_DMESG @type: | ||
63 | * | ||
64 | * @count: Oops count since boot | ||
65 | * @reason: kdump reason for notification | ||
66 | * @part: position in a multipart record | ||
67 | * @compressed: whether the buffer is compressed | ||
68 | * | ||
69 | */ | ||
70 | struct pstore_record { | ||
71 | struct pstore_info *psi; | ||
72 | enum pstore_type_id type; | ||
73 | u64 id; | ||
74 | struct timespec time; | ||
75 | char *buf; | ||
76 | ssize_t size; | ||
77 | ssize_t ecc_notice_size; | ||
49 | 78 | ||
79 | int count; | ||
80 | enum kmsg_dump_reason reason; | ||
81 | unsigned int part; | ||
82 | bool compressed; | ||
83 | }; | ||
84 | |||
85 | /** | ||
86 | * struct pstore_info - backend pstore driver structure | ||
87 | * | ||
88 | * @owner: module which is repsonsible for this backend driver | ||
89 | * @name: name of the backend driver | ||
90 | * | ||
91 | * @buf_lock: spinlock to serialize access to @buf | ||
92 | * @buf: preallocated crash dump buffer | ||
93 | * @bufsize: size of @buf available for crash dump writes | ||
94 | * | ||
95 | * @read_mutex: serializes @open, @read, @close, and @erase callbacks | ||
96 | * @flags: bitfield of frontends the backend can accept writes for | ||
97 | * @data: backend-private pointer passed back during callbacks | ||
98 | * | ||
99 | * Callbacks: | ||
100 | * | ||
101 | * @open: | ||
102 | * Notify backend that pstore is starting a full read of backend | ||
103 | * records. Followed by one or more @read calls, and a final @close. | ||
104 | * | ||
105 | * @psi: in: pointer to the struct pstore_info for the backend | ||
106 | * | ||
107 | * Returns 0 on success, and non-zero on error. | ||
108 | * | ||
109 | * @close: | ||
110 | * Notify backend that pstore has finished a full read of backend | ||
111 | * records. Always preceded by an @open call and one or more @read | ||
112 | * calls. | ||
113 | * | ||
114 | * @psi: in: pointer to the struct pstore_info for the backend | ||
115 | * | ||
116 | * Returns 0 on success, and non-zero on error. (Though pstore will | ||
117 | * ignore the error.) | ||
118 | * | ||
119 | * @read: | ||
120 | * Read next available backend record. Called after a successful | ||
121 | * @open. | ||
122 | * | ||
123 | * @record: | ||
124 | * pointer to record to populate. @buf should be allocated | ||
125 | * by the backend and filled. At least @type and @id should | ||
126 | * be populated, since these are used when creating pstorefs | ||
127 | * file names. | ||
128 | * | ||
129 | * Returns record size on success, zero when no more records are | ||
130 | * available, or negative on error. | ||
131 | * | ||
132 | * @write: | ||
133 | * A newly generated record needs to be written to backend storage. | ||
134 | * | ||
135 | * @record: | ||
136 | * pointer to record metadata. When @type is PSTORE_TYPE_DMESG, | ||
137 | * @buf will be pointing to the preallocated @psi.buf, since | ||
138 | * memory allocation may be broken during an Oops. Regardless, | ||
139 | * @buf must be proccesed or copied before returning. The | ||
140 | * backend is also expected to write @id with something that | ||
141 | 8 can help identify this record to a future @erase callback. | ||
142 | * | ||
143 | * Returns 0 on success, and non-zero on error. | ||
144 | * | ||
145 | * @write_user: | ||
146 | * Perform a frontend write to a backend record, using a specified | ||
147 | * buffer that is coming directly from userspace, instead of the | ||
148 | * @record @buf. | ||
149 | * | ||
150 | * @record: pointer to record metadata. | ||
151 | * @buf: pointer to userspace contents to write to backend | ||
152 | * | ||
153 | * Returns 0 on success, and non-zero on error. | ||
154 | * | ||
155 | * @erase: | ||
156 | * Delete a record from backend storage. Different backends | ||
157 | * identify records differently, so entire original record is | ||
158 | * passed back to assist in identification of what the backend | ||
159 | * should remove from storage. | ||
160 | * | ||
161 | * @record: pointer to record metadata. | ||
162 | * | ||
163 | * Returns 0 on success, and non-zero on error. | ||
164 | * | ||
165 | */ | ||
50 | struct pstore_info { | 166 | struct pstore_info { |
51 | struct module *owner; | 167 | struct module *owner; |
52 | char *name; | 168 | char *name; |
53 | spinlock_t buf_lock; /* serialize access to 'buf' */ | 169 | |
170 | spinlock_t buf_lock; | ||
54 | char *buf; | 171 | char *buf; |
55 | size_t bufsize; | 172 | size_t bufsize; |
56 | struct mutex read_mutex; /* serialize open/read/close */ | 173 | |
174 | struct mutex read_mutex; | ||
175 | |||
57 | int flags; | 176 | int flags; |
177 | void *data; | ||
178 | |||
58 | int (*open)(struct pstore_info *psi); | 179 | int (*open)(struct pstore_info *psi); |
59 | int (*close)(struct pstore_info *psi); | 180 | int (*close)(struct pstore_info *psi); |
60 | ssize_t (*read)(u64 *id, enum pstore_type_id *type, | 181 | ssize_t (*read)(struct pstore_record *record); |
61 | int *count, struct timespec *time, char **buf, | 182 | int (*write)(struct pstore_record *record); |
62 | bool *compressed, ssize_t *ecc_notice_size, | 183 | int (*write_user)(struct pstore_record *record, |
63 | struct pstore_info *psi); | 184 | const char __user *buf); |
64 | int (*write)(enum pstore_type_id type, | 185 | int (*erase)(struct pstore_record *record); |
65 | enum kmsg_dump_reason reason, u64 *id, | ||
66 | unsigned int part, int count, bool compressed, | ||
67 | size_t size, struct pstore_info *psi); | ||
68 | int (*write_buf)(enum pstore_type_id type, | ||
69 | enum kmsg_dump_reason reason, u64 *id, | ||
70 | unsigned int part, const char *buf, bool compressed, | ||
71 | size_t size, struct pstore_info *psi); | ||
72 | int (*write_buf_user)(enum pstore_type_id type, | ||
73 | enum kmsg_dump_reason reason, u64 *id, | ||
74 | unsigned int part, const char __user *buf, | ||
75 | bool compressed, size_t size, struct pstore_info *psi); | ||
76 | int (*erase)(enum pstore_type_id type, u64 id, | ||
77 | int count, struct timespec time, | ||
78 | struct pstore_info *psi); | ||
79 | void *data; | ||
80 | }; | 186 | }; |
81 | 187 | ||
188 | /* Supported frontends */ | ||
82 | #define PSTORE_FLAGS_DMESG (1 << 0) | 189 | #define PSTORE_FLAGS_DMESG (1 << 0) |
83 | #define PSTORE_FLAGS_FRAGILE PSTORE_FLAGS_DMESG | ||
84 | #define PSTORE_FLAGS_CONSOLE (1 << 1) | 190 | #define PSTORE_FLAGS_CONSOLE (1 << 1) |
85 | #define PSTORE_FLAGS_FTRACE (1 << 2) | 191 | #define PSTORE_FLAGS_FTRACE (1 << 2) |
86 | #define PSTORE_FLAGS_PMSG (1 << 3) | 192 | #define PSTORE_FLAGS_PMSG (1 << 3) |