diff options
author | Huang Ying <ying.huang@intel.com> | 2011-12-07 22:25:45 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2012-01-17 03:54:33 -0500 |
commit | 46d12f0bcb17b2de89a059114349d472b7eb1783 (patch) | |
tree | a87b3aff8d51c483c2c48e51106fa9a2f9b50a68 /drivers | |
parent | 5ba82ab534a325d310fe02af1c149f1072792c7b (diff) |
ACPI, APEI, Printk queued error record before panic
Because printk is not safe inside NMI handler, the recoverable error
records received in NMI handler will be queued to be printked in a
delayed IRQ context via irq_work. If a fatal error occurs after the
recoverable error and before the irq_work processed, we lost a error
report.
To solve the issue, the queued error records are printked in NMI
handler if system will go panic.
Signed-off-by: Huang Ying <ying.huang@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/apei/ghes.c | 53 |
1 files changed, 44 insertions, 9 deletions
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 9dcb2d86aeab..aaf36090de1e 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c | |||
@@ -740,26 +740,34 @@ static int ghes_notify_sci(struct notifier_block *this, | |||
740 | return ret; | 740 | return ret; |
741 | } | 741 | } |
742 | 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 | |||
743 | static void ghes_proc_in_irq(struct irq_work *irq_work) | 757 | static void ghes_proc_in_irq(struct irq_work *irq_work) |
744 | { | 758 | { |
745 | struct llist_node *llnode, *next, *tail = NULL; | 759 | struct llist_node *llnode, *next; |
746 | struct ghes_estatus_node *estatus_node; | 760 | struct ghes_estatus_node *estatus_node; |
747 | struct acpi_hest_generic *generic; | 761 | struct acpi_hest_generic *generic; |
748 | struct acpi_hest_generic_status *estatus; | 762 | struct acpi_hest_generic_status *estatus; |
749 | u32 len, node_len; | 763 | u32 len, node_len; |
750 | 764 | ||
765 | llnode = llist_del_all(&ghes_estatus_llist); | ||
751 | /* | 766 | /* |
752 | * Because the time order of estatus in list is reversed, | 767 | * Because the time order of estatus in list is reversed, |
753 | * revert it back to proper order. | 768 | * revert it back to proper order. |
754 | */ | 769 | */ |
755 | llnode = llist_del_all(&ghes_estatus_llist); | 770 | llnode = llist_nodes_reverse(llnode); |
756 | while (llnode) { | ||
757 | next = llnode->next; | ||
758 | llnode->next = tail; | ||
759 | tail = llnode; | ||
760 | llnode = next; | ||
761 | } | ||
762 | llnode = tail; | ||
763 | while (llnode) { | 771 | while (llnode) { |
764 | next = llnode->next; | 772 | next = llnode->next; |
765 | estatus_node = llist_entry(llnode, struct ghes_estatus_node, | 773 | estatus_node = llist_entry(llnode, struct ghes_estatus_node, |
@@ -779,6 +787,32 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) | |||
779 | } | 787 | } |
780 | } | 788 | } |
781 | 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 | |||
782 | 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) |
783 | { | 817 | { |
784 | struct ghes *ghes, *ghes_global = NULL; | 818 | struct ghes *ghes, *ghes_global = NULL; |
@@ -804,6 +838,7 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) | |||
804 | 838 | ||
805 | if (sev_global >= GHES_SEV_PANIC) { | 839 | if (sev_global >= GHES_SEV_PANIC) { |
806 | oops_begin(); | 840 | oops_begin(); |
841 | ghes_print_queued_estatus(); | ||
807 | __ghes_print_estatus(KERN_EMERG, ghes_global->generic, | 842 | __ghes_print_estatus(KERN_EMERG, ghes_global->generic, |
808 | ghes_global->estatus); | 843 | ghes_global->estatus); |
809 | /* reboot to log the error! */ | 844 | /* reboot to log the error! */ |