diff options
Diffstat (limited to 'drivers/acpi/apei/ghes.c')
-rw-r--r-- | drivers/acpi/apei/ghes.c | 205 |
1 files changed, 126 insertions, 79 deletions
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index dab7cb7349df..fc5f780bb61d 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c | |||
@@ -47,11 +47,11 @@ | |||
47 | #include <linux/genalloc.h> | 47 | #include <linux/genalloc.h> |
48 | #include <linux/pci.h> | 48 | #include <linux/pci.h> |
49 | #include <linux/aer.h> | 49 | #include <linux/aer.h> |
50 | #include <linux/nmi.h> | ||
50 | 51 | ||
51 | #include <acpi/ghes.h> | 52 | #include <acpi/ghes.h> |
52 | #include <asm/mce.h> | 53 | #include <acpi/apei.h> |
53 | #include <asm/tlbflush.h> | 54 | #include <asm/tlbflush.h> |
54 | #include <asm/nmi.h> | ||
55 | 55 | ||
56 | #include "apei-internal.h" | 56 | #include "apei-internal.h" |
57 | 57 | ||
@@ -74,20 +74,18 @@ | |||
74 | #define GHES_ESTATUS_CACHE_LEN(estatus_len) \ | 74 | #define GHES_ESTATUS_CACHE_LEN(estatus_len) \ |
75 | (sizeof(struct ghes_estatus_cache) + (estatus_len)) | 75 | (sizeof(struct ghes_estatus_cache) + (estatus_len)) |
76 | #define GHES_ESTATUS_FROM_CACHE(estatus_cache) \ | 76 | #define GHES_ESTATUS_FROM_CACHE(estatus_cache) \ |
77 | ((struct acpi_generic_status *) \ | 77 | ((struct acpi_hest_generic_status *) \ |
78 | ((struct ghes_estatus_cache *)(estatus_cache) + 1)) | 78 | ((struct ghes_estatus_cache *)(estatus_cache) + 1)) |
79 | 79 | ||
80 | #define GHES_ESTATUS_NODE_LEN(estatus_len) \ | 80 | #define GHES_ESTATUS_NODE_LEN(estatus_len) \ |
81 | (sizeof(struct ghes_estatus_node) + (estatus_len)) | 81 | (sizeof(struct ghes_estatus_node) + (estatus_len)) |
82 | #define GHES_ESTATUS_FROM_NODE(estatus_node) \ | 82 | #define GHES_ESTATUS_FROM_NODE(estatus_node) \ |
83 | ((struct acpi_generic_status *) \ | 83 | ((struct acpi_hest_generic_status *) \ |
84 | ((struct ghes_estatus_node *)(estatus_node) + 1)) | 84 | ((struct ghes_estatus_node *)(estatus_node) + 1)) |
85 | 85 | ||
86 | bool ghes_disable; | 86 | bool ghes_disable; |
87 | module_param_named(disable, ghes_disable, bool, 0); | 87 | module_param_named(disable, ghes_disable, bool, 0); |
88 | 88 | ||
89 | static int ghes_panic_timeout __read_mostly = 30; | ||
90 | |||
91 | /* | 89 | /* |
92 | * All error sources notified with SCI shares one notifier function, | 90 | * All error sources notified with SCI shares one notifier function, |
93 | * so they need to be linked and checked one by one. This is applied | 91 | * so they need to be linked and checked one by one. This is applied |
@@ -97,16 +95,9 @@ static int ghes_panic_timeout __read_mostly = 30; | |||
97 | * list changing, not for traversing. | 95 | * list changing, not for traversing. |
98 | */ | 96 | */ |
99 | static LIST_HEAD(ghes_sci); | 97 | static LIST_HEAD(ghes_sci); |
100 | static LIST_HEAD(ghes_nmi); | ||
101 | static DEFINE_MUTEX(ghes_list_mutex); | 98 | static DEFINE_MUTEX(ghes_list_mutex); |
102 | 99 | ||
103 | /* | 100 | /* |
104 | * NMI may be triggered on any CPU, so ghes_nmi_lock is used for | ||
105 | * mutual exclusion. | ||
106 | */ | ||
107 | static DEFINE_RAW_SPINLOCK(ghes_nmi_lock); | ||
108 | |||
109 | /* | ||
110 | * Because the memory area used to transfer hardware error information | 101 | * Because the memory area used to transfer hardware error information |
111 | * from BIOS to Linux can be determined only in NMI, IRQ or timer | 102 | * from BIOS to Linux can be determined only in NMI, IRQ or timer |
112 | * handler, but general ioremap can not be used in atomic context, so | 103 | * handler, but general ioremap can not be used in atomic context, so |
@@ -114,12 +105,16 @@ static DEFINE_RAW_SPINLOCK(ghes_nmi_lock); | |||
114 | */ | 105 | */ |
115 | 106 | ||
116 | /* | 107 | /* |
117 | * Two virtual pages are used, one for NMI context, the other for | 108 | * Two virtual pages are used, one for IRQ/PROCESS context, the other for |
118 | * IRQ/PROCESS context | 109 | * NMI context (optionally). |
119 | */ | 110 | */ |
120 | #define GHES_IOREMAP_PAGES 2 | 111 | #ifdef CONFIG_HAVE_ACPI_APEI_NMI |
121 | #define GHES_IOREMAP_NMI_PAGE(base) (base) | 112 | #define GHES_IOREMAP_PAGES 2 |
122 | #define GHES_IOREMAP_IRQ_PAGE(base) ((base) + PAGE_SIZE) | 113 | #else |
114 | #define GHES_IOREMAP_PAGES 1 | ||
115 | #endif | ||
116 | #define GHES_IOREMAP_IRQ_PAGE(base) (base) | ||
117 | #define GHES_IOREMAP_NMI_PAGE(base) ((base) + PAGE_SIZE) | ||
123 | 118 | ||
124 | /* virtual memory area for atomic ioremap */ | 119 | /* virtual memory area for atomic ioremap */ |
125 | static struct vm_struct *ghes_ioremap_area; | 120 | static struct vm_struct *ghes_ioremap_area; |
@@ -130,18 +125,8 @@ static struct vm_struct *ghes_ioremap_area; | |||
130 | static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); | 125 | static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); |
131 | static DEFINE_SPINLOCK(ghes_ioremap_lock_irq); | 126 | static DEFINE_SPINLOCK(ghes_ioremap_lock_irq); |
132 | 127 | ||
133 | /* | ||
134 | * printk is not safe in NMI context. So in NMI handler, we allocate | ||
135 | * required memory from lock-less memory allocator | ||
136 | * (ghes_estatus_pool), save estatus into it, put them into lock-less | ||
137 | * list (ghes_estatus_llist), then delay printk into IRQ context via | ||
138 | * irq_work (ghes_proc_irq_work). ghes_estatus_size_request record | ||
139 | * required pool size by all NMI error source. | ||
140 | */ | ||
141 | static struct gen_pool *ghes_estatus_pool; | 128 | static struct gen_pool *ghes_estatus_pool; |
142 | static unsigned long ghes_estatus_pool_size_request; | 129 | static unsigned long ghes_estatus_pool_size_request; |
143 | static struct llist_head ghes_estatus_llist; | ||
144 | static struct irq_work ghes_proc_irq_work; | ||
145 | 130 | ||
146 | struct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE]; | 131 | struct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE]; |
147 | static atomic_t ghes_estatus_cache_alloced; | 132 | static atomic_t ghes_estatus_cache_alloced; |
@@ -192,7 +177,7 @@ static void ghes_iounmap_nmi(void __iomem *vaddr_ptr) | |||
192 | 177 | ||
193 | BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_NMI_PAGE(base)); | 178 | BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_NMI_PAGE(base)); |
194 | unmap_kernel_range_noflush(vaddr, PAGE_SIZE); | 179 | unmap_kernel_range_noflush(vaddr, PAGE_SIZE); |
195 | __flush_tlb_one(vaddr); | 180 | arch_apei_flush_tlb_one(vaddr); |
196 | } | 181 | } |
197 | 182 | ||
198 | static void ghes_iounmap_irq(void __iomem *vaddr_ptr) | 183 | static void ghes_iounmap_irq(void __iomem *vaddr_ptr) |
@@ -202,7 +187,7 @@ static void ghes_iounmap_irq(void __iomem *vaddr_ptr) | |||
202 | 187 | ||
203 | BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_IRQ_PAGE(base)); | 188 | BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_IRQ_PAGE(base)); |
204 | unmap_kernel_range_noflush(vaddr, PAGE_SIZE); | 189 | unmap_kernel_range_noflush(vaddr, PAGE_SIZE); |
205 | __flush_tlb_one(vaddr); | 190 | arch_apei_flush_tlb_one(vaddr); |
206 | } | 191 | } |
207 | 192 | ||
208 | static int ghes_estatus_pool_init(void) | 193 | static int ghes_estatus_pool_init(void) |
@@ -249,11 +234,6 @@ static int ghes_estatus_pool_expand(unsigned long len) | |||
249 | return 0; | 234 | return 0; |
250 | } | 235 | } |
251 | 236 | ||
252 | static void ghes_estatus_pool_shrink(unsigned long len) | ||
253 | { | ||
254 | ghes_estatus_pool_size_request -= PAGE_ALIGN(len); | ||
255 | } | ||
256 | |||
257 | static struct ghes *ghes_new(struct acpi_hest_generic *generic) | 237 | static struct ghes *ghes_new(struct acpi_hest_generic *generic) |
258 | { | 238 | { |
259 | struct ghes *ghes; | 239 | struct ghes *ghes; |
@@ -408,7 +388,7 @@ static void ghes_clear_estatus(struct ghes *ghes) | |||
408 | ghes->flags &= ~GHES_TO_CLEAR; | 388 | ghes->flags &= ~GHES_TO_CLEAR; |
409 | } | 389 | } |
410 | 390 | ||
411 | static void ghes_handle_memory_failure(struct acpi_generic_data *gdata, int sev) | 391 | static void ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, int sev) |
412 | { | 392 | { |
413 | #ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE | 393 | #ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE |
414 | unsigned long pfn; | 394 | unsigned long pfn; |
@@ -441,10 +421,10 @@ static void ghes_handle_memory_failure(struct acpi_generic_data *gdata, int sev) | |||
441 | } | 421 | } |
442 | 422 | ||
443 | static void ghes_do_proc(struct ghes *ghes, | 423 | static void ghes_do_proc(struct ghes *ghes, |
444 | const struct acpi_generic_status *estatus) | 424 | const struct acpi_hest_generic_status *estatus) |
445 | { | 425 | { |
446 | int sev, sec_sev; | 426 | int sev, sec_sev; |
447 | struct acpi_generic_data *gdata; | 427 | struct acpi_hest_generic_data *gdata; |
448 | 428 | ||
449 | sev = ghes_severity(estatus->error_severity); | 429 | sev = ghes_severity(estatus->error_severity); |
450 | apei_estatus_for_each_section(estatus, gdata) { | 430 | apei_estatus_for_each_section(estatus, gdata) { |
@@ -455,9 +435,7 @@ static void ghes_do_proc(struct ghes *ghes, | |||
455 | mem_err = (struct cper_sec_mem_err *)(gdata+1); | 435 | mem_err = (struct cper_sec_mem_err *)(gdata+1); |
456 | ghes_edac_report_mem_error(ghes, sev, mem_err); | 436 | ghes_edac_report_mem_error(ghes, sev, mem_err); |
457 | 437 | ||
458 | #ifdef CONFIG_X86_MCE | 438 | arch_apei_report_mem_error(sev, mem_err); |
459 | apei_mce_report_mem_error(sev, mem_err); | ||
460 | #endif | ||
461 | ghes_handle_memory_failure(gdata, sev); | 439 | ghes_handle_memory_failure(gdata, sev); |
462 | } | 440 | } |
463 | #ifdef CONFIG_ACPI_APEI_PCIEAER | 441 | #ifdef CONFIG_ACPI_APEI_PCIEAER |
@@ -498,7 +476,7 @@ static void ghes_do_proc(struct ghes *ghes, | |||
498 | 476 | ||
499 | static void __ghes_print_estatus(const char *pfx, | 477 | static void __ghes_print_estatus(const char *pfx, |
500 | const struct acpi_hest_generic *generic, | 478 | const struct acpi_hest_generic *generic, |
501 | const struct acpi_generic_status *estatus) | 479 | const struct acpi_hest_generic_status *estatus) |
502 | { | 480 | { |
503 | static atomic_t seqno; | 481 | static atomic_t seqno; |
504 | unsigned int curr_seqno; | 482 | unsigned int curr_seqno; |
@@ -520,7 +498,7 @@ static void __ghes_print_estatus(const char *pfx, | |||
520 | 498 | ||
521 | static int ghes_print_estatus(const char *pfx, | 499 | static int ghes_print_estatus(const char *pfx, |
522 | const struct acpi_hest_generic *generic, | 500 | const struct acpi_hest_generic *generic, |
523 | const struct acpi_generic_status *estatus) | 501 | const struct acpi_hest_generic_status *estatus) |
524 | { | 502 | { |
525 | /* Not more than 2 messages every 5 seconds */ | 503 | /* Not more than 2 messages every 5 seconds */ |
526 | static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2); | 504 | static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2); |
@@ -542,13 +520,13 @@ static int ghes_print_estatus(const char *pfx, | |||
542 | * GHES error status reporting throttle, to report more kinds of | 520 | * GHES error status reporting throttle, to report more kinds of |
543 | * errors, instead of just most frequently occurred errors. | 521 | * errors, instead of just most frequently occurred errors. |
544 | */ | 522 | */ |
545 | static int ghes_estatus_cached(struct acpi_generic_status *estatus) | 523 | static int ghes_estatus_cached(struct acpi_hest_generic_status *estatus) |
546 | { | 524 | { |
547 | u32 len; | 525 | u32 len; |
548 | int i, cached = 0; | 526 | int i, cached = 0; |
549 | unsigned long long now; | 527 | unsigned long long now; |
550 | struct ghes_estatus_cache *cache; | 528 | struct ghes_estatus_cache *cache; |
551 | struct acpi_generic_status *cache_estatus; | 529 | struct acpi_hest_generic_status *cache_estatus; |
552 | 530 | ||
553 | len = cper_estatus_len(estatus); | 531 | len = cper_estatus_len(estatus); |
554 | rcu_read_lock(); | 532 | rcu_read_lock(); |
@@ -573,12 +551,12 @@ static int ghes_estatus_cached(struct acpi_generic_status *estatus) | |||
573 | 551 | ||
574 | static struct ghes_estatus_cache *ghes_estatus_cache_alloc( | 552 | static struct ghes_estatus_cache *ghes_estatus_cache_alloc( |
575 | struct acpi_hest_generic *generic, | 553 | struct acpi_hest_generic *generic, |
576 | struct acpi_generic_status *estatus) | 554 | struct acpi_hest_generic_status *estatus) |
577 | { | 555 | { |
578 | int alloced; | 556 | int alloced; |
579 | u32 len, cache_len; | 557 | u32 len, cache_len; |
580 | struct ghes_estatus_cache *cache; | 558 | struct ghes_estatus_cache *cache; |
581 | struct acpi_generic_status *cache_estatus; | 559 | struct acpi_hest_generic_status *cache_estatus; |
582 | 560 | ||
583 | alloced = atomic_add_return(1, &ghes_estatus_cache_alloced); | 561 | alloced = atomic_add_return(1, &ghes_estatus_cache_alloced); |
584 | if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) { | 562 | if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) { |
@@ -621,7 +599,7 @@ static void ghes_estatus_cache_rcu_free(struct rcu_head *head) | |||
621 | 599 | ||
622 | static void ghes_estatus_cache_add( | 600 | static void ghes_estatus_cache_add( |
623 | struct acpi_hest_generic *generic, | 601 | struct acpi_hest_generic *generic, |
624 | struct acpi_generic_status *estatus) | 602 | struct acpi_hest_generic_status *estatus) |
625 | { | 603 | { |
626 | int i, slot = -1, count; | 604 | int i, slot = -1, count; |
627 | unsigned long long now, duration, period, max_period = 0; | 605 | unsigned long long now, duration, period, max_period = 0; |
@@ -734,6 +712,32 @@ static int ghes_notify_sci(struct notifier_block *this, | |||
734 | return ret; | 712 | return ret; |
735 | } | 713 | } |
736 | 714 | ||
715 | static struct notifier_block ghes_notifier_sci = { | ||
716 | .notifier_call = ghes_notify_sci, | ||
717 | }; | ||
718 | |||
719 | #ifdef CONFIG_HAVE_ACPI_APEI_NMI | ||
720 | /* | ||
721 | * printk is not safe in NMI context. So in NMI handler, we allocate | ||
722 | * required memory from lock-less memory allocator | ||
723 | * (ghes_estatus_pool), save estatus into it, put them into lock-less | ||
724 | * list (ghes_estatus_llist), then delay printk into IRQ context via | ||
725 | * irq_work (ghes_proc_irq_work). ghes_estatus_size_request record | ||
726 | * required pool size by all NMI error source. | ||
727 | */ | ||
728 | static struct llist_head ghes_estatus_llist; | ||
729 | static struct irq_work ghes_proc_irq_work; | ||
730 | |||
731 | /* | ||
732 | * NMI may be triggered on any CPU, so ghes_nmi_lock is used for | ||
733 | * mutual exclusion. | ||
734 | */ | ||
735 | static DEFINE_RAW_SPINLOCK(ghes_nmi_lock); | ||
736 | |||
737 | static LIST_HEAD(ghes_nmi); | ||
738 | |||
739 | static int ghes_panic_timeout __read_mostly = 30; | ||
740 | |||
737 | static struct llist_node *llist_nodes_reverse(struct llist_node *llnode) | 741 | static struct llist_node *llist_nodes_reverse(struct llist_node *llnode) |
738 | { | 742 | { |
739 | struct llist_node *next, *tail = NULL; | 743 | struct llist_node *next, *tail = NULL; |
@@ -753,7 +757,7 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) | |||
753 | struct llist_node *llnode, *next; | 757 | struct llist_node *llnode, *next; |
754 | struct ghes_estatus_node *estatus_node; | 758 | struct ghes_estatus_node *estatus_node; |
755 | struct acpi_hest_generic *generic; | 759 | struct acpi_hest_generic *generic; |
756 | struct acpi_generic_status *estatus; | 760 | struct acpi_hest_generic_status *estatus; |
757 | u32 len, node_len; | 761 | u32 len, node_len; |
758 | 762 | ||
759 | llnode = llist_del_all(&ghes_estatus_llist); | 763 | llnode = llist_del_all(&ghes_estatus_llist); |
@@ -786,7 +790,7 @@ static void ghes_print_queued_estatus(void) | |||
786 | struct llist_node *llnode; | 790 | struct llist_node *llnode; |
787 | struct ghes_estatus_node *estatus_node; | 791 | struct ghes_estatus_node *estatus_node; |
788 | struct acpi_hest_generic *generic; | 792 | struct acpi_hest_generic *generic; |
789 | struct acpi_generic_status *estatus; | 793 | struct acpi_hest_generic_status *estatus; |
790 | u32 len, node_len; | 794 | u32 len, node_len; |
791 | 795 | ||
792 | llnode = llist_del_all(&ghes_estatus_llist); | 796 | llnode = llist_del_all(&ghes_estatus_llist); |
@@ -845,7 +849,7 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) | |||
845 | #ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG | 849 | #ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG |
846 | u32 len, node_len; | 850 | u32 len, node_len; |
847 | struct ghes_estatus_node *estatus_node; | 851 | struct ghes_estatus_node *estatus_node; |
848 | struct acpi_generic_status *estatus; | 852 | struct acpi_hest_generic_status *estatus; |
849 | #endif | 853 | #endif |
850 | if (!(ghes->flags & GHES_TO_CLEAR)) | 854 | if (!(ghes->flags & GHES_TO_CLEAR)) |
851 | continue; | 855 | continue; |
@@ -877,10 +881,6 @@ out: | |||
877 | return ret; | 881 | return ret; |
878 | } | 882 | } |
879 | 883 | ||
880 | static struct notifier_block ghes_notifier_sci = { | ||
881 | .notifier_call = ghes_notify_sci, | ||
882 | }; | ||
883 | |||
884 | static unsigned long ghes_esource_prealloc_size( | 884 | static unsigned long ghes_esource_prealloc_size( |
885 | const struct acpi_hest_generic *generic) | 885 | const struct acpi_hest_generic *generic) |
886 | { | 886 | { |
@@ -896,11 +896,71 @@ static unsigned long ghes_esource_prealloc_size( | |||
896 | return prealloc_size; | 896 | return prealloc_size; |
897 | } | 897 | } |
898 | 898 | ||
899 | static void ghes_estatus_pool_shrink(unsigned long len) | ||
900 | { | ||
901 | ghes_estatus_pool_size_request -= PAGE_ALIGN(len); | ||
902 | } | ||
903 | |||
904 | static void ghes_nmi_add(struct ghes *ghes) | ||
905 | { | ||
906 | unsigned long len; | ||
907 | |||
908 | len = ghes_esource_prealloc_size(ghes->generic); | ||
909 | ghes_estatus_pool_expand(len); | ||
910 | mutex_lock(&ghes_list_mutex); | ||
911 | if (list_empty(&ghes_nmi)) | ||
912 | register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes"); | ||
913 | list_add_rcu(&ghes->list, &ghes_nmi); | ||
914 | mutex_unlock(&ghes_list_mutex); | ||
915 | } | ||
916 | |||
917 | static void ghes_nmi_remove(struct ghes *ghes) | ||
918 | { | ||
919 | unsigned long len; | ||
920 | |||
921 | mutex_lock(&ghes_list_mutex); | ||
922 | list_del_rcu(&ghes->list); | ||
923 | if (list_empty(&ghes_nmi)) | ||
924 | unregister_nmi_handler(NMI_LOCAL, "ghes"); | ||
925 | mutex_unlock(&ghes_list_mutex); | ||
926 | /* | ||
927 | * To synchronize with NMI handler, ghes can only be | ||
928 | * freed after NMI handler finishes. | ||
929 | */ | ||
930 | synchronize_rcu(); | ||
931 | len = ghes_esource_prealloc_size(ghes->generic); | ||
932 | ghes_estatus_pool_shrink(len); | ||
933 | } | ||
934 | |||
935 | static void ghes_nmi_init_cxt(void) | ||
936 | { | ||
937 | init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq); | ||
938 | } | ||
939 | #else /* CONFIG_HAVE_ACPI_APEI_NMI */ | ||
940 | static inline void ghes_nmi_add(struct ghes *ghes) | ||
941 | { | ||
942 | pr_err(GHES_PFX "ID: %d, trying to add NMI notification which is not supported!\n", | ||
943 | ghes->generic->header.source_id); | ||
944 | BUG(); | ||
945 | } | ||
946 | |||
947 | static inline void ghes_nmi_remove(struct ghes *ghes) | ||
948 | { | ||
949 | pr_err(GHES_PFX "ID: %d, trying to remove NMI notification which is not supported!\n", | ||
950 | ghes->generic->header.source_id); | ||
951 | BUG(); | ||
952 | } | ||
953 | |||
954 | static inline void ghes_nmi_init_cxt(void) | ||
955 | { | ||
956 | } | ||
957 | #endif /* CONFIG_HAVE_ACPI_APEI_NMI */ | ||
958 | |||
899 | static int ghes_probe(struct platform_device *ghes_dev) | 959 | static int ghes_probe(struct platform_device *ghes_dev) |
900 | { | 960 | { |
901 | struct acpi_hest_generic *generic; | 961 | struct acpi_hest_generic *generic; |
902 | struct ghes *ghes = NULL; | 962 | struct ghes *ghes = NULL; |
903 | unsigned long len; | 963 | |
904 | int rc = -EINVAL; | 964 | int rc = -EINVAL; |
905 | 965 | ||
906 | generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data; | 966 | generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data; |
@@ -911,7 +971,13 @@ static int ghes_probe(struct platform_device *ghes_dev) | |||
911 | case ACPI_HEST_NOTIFY_POLLED: | 971 | case ACPI_HEST_NOTIFY_POLLED: |
912 | case ACPI_HEST_NOTIFY_EXTERNAL: | 972 | case ACPI_HEST_NOTIFY_EXTERNAL: |
913 | case ACPI_HEST_NOTIFY_SCI: | 973 | case ACPI_HEST_NOTIFY_SCI: |
974 | break; | ||
914 | case ACPI_HEST_NOTIFY_NMI: | 975 | case ACPI_HEST_NOTIFY_NMI: |
976 | if (!IS_ENABLED(CONFIG_HAVE_ACPI_APEI_NMI)) { | ||
977 | pr_warn(GHES_PFX "Generic hardware error source: %d notified via NMI interrupt is not supported!\n", | ||
978 | generic->header.source_id); | ||
979 | goto err; | ||
980 | } | ||
915 | break; | 981 | break; |
916 | case ACPI_HEST_NOTIFY_LOCAL: | 982 | case ACPI_HEST_NOTIFY_LOCAL: |
917 | pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n", | 983 | pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n", |
@@ -925,7 +991,7 @@ static int ghes_probe(struct platform_device *ghes_dev) | |||
925 | 991 | ||
926 | rc = -EIO; | 992 | rc = -EIO; |
927 | if (generic->error_block_length < | 993 | if (generic->error_block_length < |
928 | sizeof(struct acpi_generic_status)) { | 994 | sizeof(struct acpi_hest_generic_status)) { |
929 | pr_warning(FW_BUG GHES_PFX "Invalid error block length: %u for generic hardware error source: %d\n", | 995 | pr_warning(FW_BUG GHES_PFX "Invalid error block length: %u for generic hardware error source: %d\n", |
930 | generic->error_block_length, | 996 | generic->error_block_length, |
931 | generic->header.source_id); | 997 | generic->header.source_id); |
@@ -972,14 +1038,7 @@ static int ghes_probe(struct platform_device *ghes_dev) | |||
972 | mutex_unlock(&ghes_list_mutex); | 1038 | mutex_unlock(&ghes_list_mutex); |
973 | break; | 1039 | break; |
974 | case ACPI_HEST_NOTIFY_NMI: | 1040 | case ACPI_HEST_NOTIFY_NMI: |
975 | len = ghes_esource_prealloc_size(generic); | 1041 | ghes_nmi_add(ghes); |
976 | ghes_estatus_pool_expand(len); | ||
977 | mutex_lock(&ghes_list_mutex); | ||
978 | if (list_empty(&ghes_nmi)) | ||
979 | register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, | ||
980 | "ghes"); | ||
981 | list_add_rcu(&ghes->list, &ghes_nmi); | ||
982 | mutex_unlock(&ghes_list_mutex); | ||
983 | break; | 1042 | break; |
984 | default: | 1043 | default: |
985 | BUG(); | 1044 | BUG(); |
@@ -1001,7 +1060,6 @@ static int ghes_remove(struct platform_device *ghes_dev) | |||
1001 | { | 1060 | { |
1002 | struct ghes *ghes; | 1061 | struct ghes *ghes; |
1003 | struct acpi_hest_generic *generic; | 1062 | struct acpi_hest_generic *generic; |
1004 | unsigned long len; | ||
1005 | 1063 | ||
1006 | ghes = platform_get_drvdata(ghes_dev); | 1064 | ghes = platform_get_drvdata(ghes_dev); |
1007 | generic = ghes->generic; | 1065 | generic = ghes->generic; |
@@ -1022,18 +1080,7 @@ static int ghes_remove(struct platform_device *ghes_dev) | |||
1022 | mutex_unlock(&ghes_list_mutex); | 1080 | mutex_unlock(&ghes_list_mutex); |
1023 | break; | 1081 | break; |
1024 | case ACPI_HEST_NOTIFY_NMI: | 1082 | case ACPI_HEST_NOTIFY_NMI: |
1025 | mutex_lock(&ghes_list_mutex); | 1083 | ghes_nmi_remove(ghes); |
1026 | list_del_rcu(&ghes->list); | ||
1027 | if (list_empty(&ghes_nmi)) | ||
1028 | unregister_nmi_handler(NMI_LOCAL, "ghes"); | ||
1029 | mutex_unlock(&ghes_list_mutex); | ||
1030 | /* | ||
1031 | * To synchronize with NMI handler, ghes can only be | ||
1032 | * freed after NMI handler finishes. | ||
1033 | */ | ||
1034 | synchronize_rcu(); | ||
1035 | len = ghes_esource_prealloc_size(generic); | ||
1036 | ghes_estatus_pool_shrink(len); | ||
1037 | break; | 1084 | break; |
1038 | default: | 1085 | default: |
1039 | BUG(); | 1086 | BUG(); |
@@ -1077,7 +1124,7 @@ static int __init ghes_init(void) | |||
1077 | return -EINVAL; | 1124 | return -EINVAL; |
1078 | } | 1125 | } |
1079 | 1126 | ||
1080 | init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq); | 1127 | ghes_nmi_init_cxt(); |
1081 | 1128 | ||
1082 | rc = ghes_ioremap_init(); | 1129 | rc = ghes_ioremap_init(); |
1083 | if (rc) | 1130 | if (rc) |