diff options
Diffstat (limited to 'drivers/acpi/apei/ghes.c')
-rw-r--r-- | drivers/acpi/apei/ghes.c | 71 |
1 files changed, 21 insertions, 50 deletions
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 7ae2750bb457..d668a8ae602b 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c | |||
@@ -48,8 +48,8 @@ | |||
48 | #include <linux/genalloc.h> | 48 | #include <linux/genalloc.h> |
49 | #include <linux/pci.h> | 49 | #include <linux/pci.h> |
50 | #include <linux/aer.h> | 50 | #include <linux/aer.h> |
51 | #include <acpi/apei.h> | 51 | |
52 | #include <acpi/hed.h> | 52 | #include <acpi/ghes.h> |
53 | #include <asm/mce.h> | 53 | #include <asm/mce.h> |
54 | #include <asm/tlbflush.h> | 54 | #include <asm/tlbflush.h> |
55 | #include <asm/nmi.h> | 55 | #include <asm/nmi.h> |
@@ -84,42 +84,6 @@ | |||
84 | ((struct acpi_hest_generic_status *) \ | 84 | ((struct acpi_hest_generic_status *) \ |
85 | ((struct ghes_estatus_node *)(estatus_node) + 1)) | 85 | ((struct ghes_estatus_node *)(estatus_node) + 1)) |
86 | 86 | ||
87 | /* | ||
88 | * One struct ghes is created for each generic hardware error source. | ||
89 | * It provides the context for APEI hardware error timer/IRQ/SCI/NMI | ||
90 | * handler. | ||
91 | * | ||
92 | * estatus: memory buffer for error status block, allocated during | ||
93 | * HEST parsing. | ||
94 | */ | ||
95 | #define GHES_TO_CLEAR 0x0001 | ||
96 | #define GHES_EXITING 0x0002 | ||
97 | |||
98 | struct ghes { | ||
99 | struct acpi_hest_generic *generic; | ||
100 | struct acpi_hest_generic_status *estatus; | ||
101 | u64 buffer_paddr; | ||
102 | unsigned long flags; | ||
103 | union { | ||
104 | struct list_head list; | ||
105 | struct timer_list timer; | ||
106 | unsigned int irq; | ||
107 | }; | ||
108 | }; | ||
109 | |||
110 | struct ghes_estatus_node { | ||
111 | struct llist_node llnode; | ||
112 | struct acpi_hest_generic *generic; | ||
113 | }; | ||
114 | |||
115 | struct ghes_estatus_cache { | ||
116 | u32 estatus_len; | ||
117 | atomic_t count; | ||
118 | struct acpi_hest_generic *generic; | ||
119 | unsigned long long time_in; | ||
120 | struct rcu_head rcu; | ||
121 | }; | ||
122 | |||
123 | bool ghes_disable; | 87 | bool ghes_disable; |
124 | module_param_named(disable, ghes_disable, bool, 0); | 88 | module_param_named(disable, ghes_disable, bool, 0); |
125 | 89 | ||
@@ -333,13 +297,6 @@ static void ghes_fini(struct ghes *ghes) | |||
333 | apei_unmap_generic_address(&ghes->generic->error_status_address); | 297 | apei_unmap_generic_address(&ghes->generic->error_status_address); |
334 | } | 298 | } |
335 | 299 | ||
336 | enum { | ||
337 | GHES_SEV_NO = 0x0, | ||
338 | GHES_SEV_CORRECTED = 0x1, | ||
339 | GHES_SEV_RECOVERABLE = 0x2, | ||
340 | GHES_SEV_PANIC = 0x3, | ||
341 | }; | ||
342 | |||
343 | static inline int ghes_severity(int severity) | 300 | static inline int ghes_severity(int severity) |
344 | { | 301 | { |
345 | switch (severity) { | 302 | switch (severity) { |
@@ -452,7 +409,8 @@ static void ghes_clear_estatus(struct ghes *ghes) | |||
452 | ghes->flags &= ~GHES_TO_CLEAR; | 409 | ghes->flags &= ~GHES_TO_CLEAR; |
453 | } | 410 | } |
454 | 411 | ||
455 | static void ghes_do_proc(const struct acpi_hest_generic_status *estatus) | 412 | static void ghes_do_proc(struct ghes *ghes, |
413 | const struct acpi_hest_generic_status *estatus) | ||
456 | { | 414 | { |
457 | int sev, sec_sev; | 415 | int sev, sec_sev; |
458 | struct acpi_hest_generic_data *gdata; | 416 | struct acpi_hest_generic_data *gdata; |
@@ -464,6 +422,8 @@ static void ghes_do_proc(const struct acpi_hest_generic_status *estatus) | |||
464 | CPER_SEC_PLATFORM_MEM)) { | 422 | CPER_SEC_PLATFORM_MEM)) { |
465 | struct cper_sec_mem_err *mem_err; | 423 | struct cper_sec_mem_err *mem_err; |
466 | mem_err = (struct cper_sec_mem_err *)(gdata+1); | 424 | mem_err = (struct cper_sec_mem_err *)(gdata+1); |
425 | ghes_edac_report_mem_error(ghes, sev, mem_err); | ||
426 | |||
467 | #ifdef CONFIG_X86_MCE | 427 | #ifdef CONFIG_X86_MCE |
468 | apei_mce_report_mem_error(sev == GHES_SEV_CORRECTED, | 428 | apei_mce_report_mem_error(sev == GHES_SEV_CORRECTED, |
469 | mem_err); | 429 | mem_err); |
@@ -682,7 +642,7 @@ static int ghes_proc(struct ghes *ghes) | |||
682 | if (ghes_print_estatus(NULL, ghes->generic, ghes->estatus)) | 642 | if (ghes_print_estatus(NULL, ghes->generic, ghes->estatus)) |
683 | ghes_estatus_cache_add(ghes->generic, ghes->estatus); | 643 | ghes_estatus_cache_add(ghes->generic, ghes->estatus); |
684 | } | 644 | } |
685 | ghes_do_proc(ghes->estatus); | 645 | ghes_do_proc(ghes, ghes->estatus); |
686 | out: | 646 | out: |
687 | ghes_clear_estatus(ghes); | 647 | ghes_clear_estatus(ghes); |
688 | return 0; | 648 | return 0; |
@@ -775,7 +735,7 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) | |||
775 | estatus = GHES_ESTATUS_FROM_NODE(estatus_node); | 735 | estatus = GHES_ESTATUS_FROM_NODE(estatus_node); |
776 | len = apei_estatus_len(estatus); | 736 | len = apei_estatus_len(estatus); |
777 | node_len = GHES_ESTATUS_NODE_LEN(len); | 737 | node_len = GHES_ESTATUS_NODE_LEN(len); |
778 | ghes_do_proc(estatus); | 738 | ghes_do_proc(estatus_node->ghes, estatus); |
779 | if (!ghes_estatus_cached(estatus)) { | 739 | if (!ghes_estatus_cached(estatus)) { |
780 | generic = estatus_node->generic; | 740 | generic = estatus_node->generic; |
781 | if (ghes_print_estatus(NULL, generic, estatus)) | 741 | if (ghes_print_estatus(NULL, generic, estatus)) |
@@ -864,6 +824,7 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) | |||
864 | estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool, | 824 | estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool, |
865 | node_len); | 825 | node_len); |
866 | if (estatus_node) { | 826 | if (estatus_node) { |
827 | estatus_node->ghes = ghes; | ||
867 | estatus_node->generic = ghes->generic; | 828 | estatus_node->generic = ghes->generic; |
868 | estatus = GHES_ESTATUS_FROM_NODE(estatus_node); | 829 | estatus = GHES_ESTATUS_FROM_NODE(estatus_node); |
869 | memcpy(estatus, ghes->estatus, len); | 830 | memcpy(estatus, ghes->estatus, len); |
@@ -942,6 +903,11 @@ static int ghes_probe(struct platform_device *ghes_dev) | |||
942 | ghes = NULL; | 903 | ghes = NULL; |
943 | goto err; | 904 | goto err; |
944 | } | 905 | } |
906 | |||
907 | rc = ghes_edac_register(ghes, &ghes_dev->dev); | ||
908 | if (rc < 0) | ||
909 | goto err; | ||
910 | |||
945 | switch (generic->notify.type) { | 911 | switch (generic->notify.type) { |
946 | case ACPI_HEST_NOTIFY_POLLED: | 912 | case ACPI_HEST_NOTIFY_POLLED: |
947 | ghes->timer.function = ghes_poll_func; | 913 | ghes->timer.function = ghes_poll_func; |
@@ -954,13 +920,13 @@ static int ghes_probe(struct platform_device *ghes_dev) | |||
954 | if (acpi_gsi_to_irq(generic->notify.vector, &ghes->irq)) { | 920 | if (acpi_gsi_to_irq(generic->notify.vector, &ghes->irq)) { |
955 | pr_err(GHES_PFX "Failed to map GSI to IRQ for generic hardware error source: %d\n", | 921 | pr_err(GHES_PFX "Failed to map GSI to IRQ for generic hardware error source: %d\n", |
956 | generic->header.source_id); | 922 | generic->header.source_id); |
957 | goto err; | 923 | goto err_edac_unreg; |
958 | } | 924 | } |
959 | if (request_irq(ghes->irq, ghes_irq_func, | 925 | if (request_irq(ghes->irq, ghes_irq_func, |
960 | 0, "GHES IRQ", ghes)) { | 926 | 0, "GHES IRQ", ghes)) { |
961 | pr_err(GHES_PFX "Failed to register IRQ for generic hardware error source: %d\n", | 927 | pr_err(GHES_PFX "Failed to register IRQ for generic hardware error source: %d\n", |
962 | generic->header.source_id); | 928 | generic->header.source_id); |
963 | goto err; | 929 | goto err_edac_unreg; |
964 | } | 930 | } |
965 | break; | 931 | break; |
966 | case ACPI_HEST_NOTIFY_SCI: | 932 | case ACPI_HEST_NOTIFY_SCI: |
@@ -986,6 +952,8 @@ static int ghes_probe(struct platform_device *ghes_dev) | |||
986 | platform_set_drvdata(ghes_dev, ghes); | 952 | platform_set_drvdata(ghes_dev, ghes); |
987 | 953 | ||
988 | return 0; | 954 | return 0; |
955 | err_edac_unreg: | ||
956 | ghes_edac_unregister(ghes); | ||
989 | err: | 957 | err: |
990 | if (ghes) { | 958 | if (ghes) { |
991 | ghes_fini(ghes); | 959 | ghes_fini(ghes); |
@@ -1038,6 +1006,9 @@ static int ghes_remove(struct platform_device *ghes_dev) | |||
1038 | } | 1006 | } |
1039 | 1007 | ||
1040 | ghes_fini(ghes); | 1008 | ghes_fini(ghes); |
1009 | |||
1010 | ghes_edac_unregister(ghes); | ||
1011 | |||
1041 | kfree(ghes); | 1012 | kfree(ghes); |
1042 | 1013 | ||
1043 | platform_set_drvdata(ghes_dev, NULL); | 1014 | platform_set_drvdata(ghes_dev, NULL); |