diff options
Diffstat (limited to 'drivers/acpi/apei/ghes.c')
-rw-r--r-- | drivers/acpi/apei/ghes.c | 102 |
1 files changed, 83 insertions, 19 deletions
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index b8e08cb67a18..b3207e16670e 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/module.h> | 33 | #include <linux/module.h> |
34 | #include <linux/init.h> | 34 | #include <linux/init.h> |
35 | #include <linux/acpi.h> | 35 | #include <linux/acpi.h> |
36 | #include <linux/acpi_io.h> | ||
36 | #include <linux/io.h> | 37 | #include <linux/io.h> |
37 | #include <linux/interrupt.h> | 38 | #include <linux/interrupt.h> |
38 | #include <linux/timer.h> | 39 | #include <linux/timer.h> |
@@ -45,8 +46,9 @@ | |||
45 | #include <linux/irq_work.h> | 46 | #include <linux/irq_work.h> |
46 | #include <linux/llist.h> | 47 | #include <linux/llist.h> |
47 | #include <linux/genalloc.h> | 48 | #include <linux/genalloc.h> |
49 | #include <linux/pci.h> | ||
50 | #include <linux/aer.h> | ||
48 | #include <acpi/apei.h> | 51 | #include <acpi/apei.h> |
49 | #include <acpi/atomicio.h> | ||
50 | #include <acpi/hed.h> | 52 | #include <acpi/hed.h> |
51 | #include <asm/mce.h> | 53 | #include <asm/mce.h> |
52 | #include <asm/tlbflush.h> | 54 | #include <asm/tlbflush.h> |
@@ -299,7 +301,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic) | |||
299 | if (!ghes) | 301 | if (!ghes) |
300 | return ERR_PTR(-ENOMEM); | 302 | return ERR_PTR(-ENOMEM); |
301 | ghes->generic = generic; | 303 | ghes->generic = generic; |
302 | rc = acpi_pre_map_gar(&generic->error_status_address); | 304 | rc = acpi_os_map_generic_address(&generic->error_status_address); |
303 | if (rc) | 305 | if (rc) |
304 | goto err_free; | 306 | goto err_free; |
305 | error_block_length = generic->error_block_length; | 307 | error_block_length = generic->error_block_length; |
@@ -319,7 +321,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic) | |||
319 | return ghes; | 321 | return ghes; |
320 | 322 | ||
321 | err_unmap: | 323 | err_unmap: |
322 | acpi_post_unmap_gar(&generic->error_status_address); | 324 | acpi_os_unmap_generic_address(&generic->error_status_address); |
323 | err_free: | 325 | err_free: |
324 | kfree(ghes); | 326 | kfree(ghes); |
325 | return ERR_PTR(rc); | 327 | return ERR_PTR(rc); |
@@ -328,7 +330,7 @@ err_free: | |||
328 | static void ghes_fini(struct ghes *ghes) | 330 | static void ghes_fini(struct ghes *ghes) |
329 | { | 331 | { |
330 | kfree(ghes->estatus); | 332 | kfree(ghes->estatus); |
331 | acpi_post_unmap_gar(&ghes->generic->error_status_address); | 333 | acpi_os_unmap_generic_address(&ghes->generic->error_status_address); |
332 | } | 334 | } |
333 | 335 | ||
334 | enum { | 336 | enum { |
@@ -399,7 +401,7 @@ static int ghes_read_estatus(struct ghes *ghes, int silent) | |||
399 | u32 len; | 401 | u32 len; |
400 | int rc; | 402 | int rc; |
401 | 403 | ||
402 | rc = acpi_atomic_read(&buf_paddr, &g->error_status_address); | 404 | rc = apei_read(&buf_paddr, &g->error_status_address); |
403 | if (rc) { | 405 | if (rc) { |
404 | if (!silent && printk_ratelimit()) | 406 | if (!silent && printk_ratelimit()) |
405 | pr_warning(FW_WARN GHES_PFX | 407 | pr_warning(FW_WARN GHES_PFX |
@@ -476,6 +478,27 @@ static void ghes_do_proc(const struct acpi_hest_generic_status *estatus) | |||
476 | } | 478 | } |
477 | #endif | 479 | #endif |
478 | } | 480 | } |
481 | #ifdef CONFIG_ACPI_APEI_PCIEAER | ||
482 | else if (!uuid_le_cmp(*(uuid_le *)gdata->section_type, | ||
483 | CPER_SEC_PCIE)) { | ||
484 | struct cper_sec_pcie *pcie_err; | ||
485 | pcie_err = (struct cper_sec_pcie *)(gdata+1); | ||
486 | if (sev == GHES_SEV_RECOVERABLE && | ||
487 | sec_sev == GHES_SEV_RECOVERABLE && | ||
488 | pcie_err->validation_bits & CPER_PCIE_VALID_DEVICE_ID && | ||
489 | pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO) { | ||
490 | unsigned int devfn; | ||
491 | int aer_severity; | ||
492 | devfn = PCI_DEVFN(pcie_err->device_id.device, | ||
493 | pcie_err->device_id.function); | ||
494 | aer_severity = cper_severity_to_aer(sev); | ||
495 | aer_recover_queue(pcie_err->device_id.segment, | ||
496 | pcie_err->device_id.bus, | ||
497 | devfn, aer_severity); | ||
498 | } | ||
499 | |||
500 | } | ||
501 | #endif | ||
479 | } | 502 | } |
480 | } | 503 | } |
481 | 504 | ||
@@ -483,16 +506,22 @@ static void __ghes_print_estatus(const char *pfx, | |||
483 | const struct acpi_hest_generic *generic, | 506 | const struct acpi_hest_generic *generic, |
484 | const struct acpi_hest_generic_status *estatus) | 507 | const struct acpi_hest_generic_status *estatus) |
485 | { | 508 | { |
509 | static atomic_t seqno; | ||
510 | unsigned int curr_seqno; | ||
511 | char pfx_seq[64]; | ||
512 | |||
486 | if (pfx == NULL) { | 513 | if (pfx == NULL) { |
487 | if (ghes_severity(estatus->error_severity) <= | 514 | if (ghes_severity(estatus->error_severity) <= |
488 | GHES_SEV_CORRECTED) | 515 | GHES_SEV_CORRECTED) |
489 | pfx = KERN_WARNING HW_ERR; | 516 | pfx = KERN_WARNING; |
490 | else | 517 | else |
491 | pfx = KERN_ERR HW_ERR; | 518 | pfx = KERN_ERR; |
492 | } | 519 | } |
520 | curr_seqno = atomic_inc_return(&seqno); | ||
521 | snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}" HW_ERR, pfx, curr_seqno); | ||
493 | printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n", | 522 | printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n", |
494 | pfx, generic->header.source_id); | 523 | pfx_seq, generic->header.source_id); |
495 | apei_estatus_print(pfx, estatus); | 524 | apei_estatus_print(pfx_seq, estatus); |
496 | } | 525 | } |
497 | 526 | ||
498 | static int ghes_print_estatus(const char *pfx, | 527 | static int ghes_print_estatus(const char *pfx, |
@@ -711,26 +740,34 @@ static int ghes_notify_sci(struct notifier_block *this, | |||
711 | return ret; | 740 | return ret; |
712 | } | 741 | } |
713 | 742 | ||
743 | static struct llist_node *llist_nodes_reverse(struct llist_node *llnode) | ||
744 | { | ||
745 | struct llist_node *next, *tail = NULL; | ||
746 | |||
747 | while (llnode) { | ||
748 | next = llnode->next; | ||
749 | llnode->next = tail; | ||
750 | tail = llnode; | ||
751 | llnode = next; | ||
752 | } | ||
753 | |||
754 | return tail; | ||
755 | } | ||
756 | |||
714 | static void ghes_proc_in_irq(struct irq_work *irq_work) | 757 | static void ghes_proc_in_irq(struct irq_work *irq_work) |
715 | { | 758 | { |
716 | struct llist_node *llnode, *next, *tail = NULL; | 759 | struct llist_node *llnode, *next; |
717 | struct ghes_estatus_node *estatus_node; | 760 | struct ghes_estatus_node *estatus_node; |
718 | struct acpi_hest_generic *generic; | 761 | struct acpi_hest_generic *generic; |
719 | struct acpi_hest_generic_status *estatus; | 762 | struct acpi_hest_generic_status *estatus; |
720 | u32 len, node_len; | 763 | u32 len, node_len; |
721 | 764 | ||
765 | llnode = llist_del_all(&ghes_estatus_llist); | ||
722 | /* | 766 | /* |
723 | * Because the time order of estatus in list is reversed, | 767 | * Because the time order of estatus in list is reversed, |
724 | * revert it back to proper order. | 768 | * revert it back to proper order. |
725 | */ | 769 | */ |
726 | llnode = llist_del_all(&ghes_estatus_llist); | 770 | llnode = llist_nodes_reverse(llnode); |
727 | while (llnode) { | ||
728 | next = llnode->next; | ||
729 | llnode->next = tail; | ||
730 | tail = llnode; | ||
731 | llnode = next; | ||
732 | } | ||
733 | llnode = tail; | ||
734 | while (llnode) { | 771 | while (llnode) { |
735 | next = llnode->next; | 772 | next = llnode->next; |
736 | estatus_node = llist_entry(llnode, struct ghes_estatus_node, | 773 | estatus_node = llist_entry(llnode, struct ghes_estatus_node, |
@@ -750,6 +787,32 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) | |||
750 | } | 787 | } |
751 | } | 788 | } |
752 | 789 | ||
790 | static void ghes_print_queued_estatus(void) | ||
791 | { | ||
792 | struct llist_node *llnode; | ||
793 | struct ghes_estatus_node *estatus_node; | ||
794 | struct acpi_hest_generic *generic; | ||
795 | struct acpi_hest_generic_status *estatus; | ||
796 | u32 len, node_len; | ||
797 | |||
798 | llnode = llist_del_all(&ghes_estatus_llist); | ||
799 | /* | ||
800 | * Because the time order of estatus in list is reversed, | ||
801 | * revert it back to proper order. | ||
802 | */ | ||
803 | llnode = llist_nodes_reverse(llnode); | ||
804 | while (llnode) { | ||
805 | estatus_node = llist_entry(llnode, struct ghes_estatus_node, | ||
806 | llnode); | ||
807 | estatus = GHES_ESTATUS_FROM_NODE(estatus_node); | ||
808 | len = apei_estatus_len(estatus); | ||
809 | node_len = GHES_ESTATUS_NODE_LEN(len); | ||
810 | generic = estatus_node->generic; | ||
811 | ghes_print_estatus(NULL, generic, estatus); | ||
812 | llnode = llnode->next; | ||
813 | } | ||
814 | } | ||
815 | |||
753 | static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) | 816 | static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) |
754 | { | 817 | { |
755 | struct ghes *ghes, *ghes_global = NULL; | 818 | struct ghes *ghes, *ghes_global = NULL; |
@@ -775,7 +838,8 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) | |||
775 | 838 | ||
776 | if (sev_global >= GHES_SEV_PANIC) { | 839 | if (sev_global >= GHES_SEV_PANIC) { |
777 | oops_begin(); | 840 | oops_begin(); |
778 | __ghes_print_estatus(KERN_EMERG HW_ERR, ghes_global->generic, | 841 | ghes_print_queued_estatus(); |
842 | __ghes_print_estatus(KERN_EMERG, ghes_global->generic, | ||
779 | ghes_global->estatus); | 843 | ghes_global->estatus); |
780 | /* reboot to log the error! */ | 844 | /* reboot to log the error! */ |
781 | if (panic_timeout == 0) | 845 | if (panic_timeout == 0) |