diff options
author | Gavin Shan <shangw@linux.vnet.ibm.com> | 2012-02-27 15:04:00 -0500 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2012-03-08 19:11:01 -0500 |
commit | 8d633291b4fc0539ecad31f972447104be6953ec (patch) | |
tree | 7be9e4d58a8289ba843e632974873bead839a458 /arch/powerpc | |
parent | 2652481f75186940c4608f68c9fd76b32ec9b159 (diff) |
powerpc/eeh: pseries platform EEH error log retrieval
On RTAS compliant pSeries platform, one dedicated RTAS call has
been introduced to retrieve EEH temporary or permanent error log.
The patch implements the function of retriving EEH error log through
RTAS call. Besides, it has been abstracted by struct eeh_ops::get_log
so that EEH core components could support multiple platforms in future.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/include/asm/eeh.h | 2 | ||||
-rw-r--r-- | arch/powerpc/include/asm/ppc-pci.h | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh.c | 63 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh_driver.c | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh_pseries.c | 47 |
5 files changed, 51 insertions, 67 deletions
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 894ea6c662cc..ad8f31834e00 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h | |||
@@ -52,6 +52,8 @@ struct device_node; | |||
52 | #define EEH_RESET_DEACTIVATE 0 /* Deactivate the PE reset */ | 52 | #define EEH_RESET_DEACTIVATE 0 /* Deactivate the PE reset */ |
53 | #define EEH_RESET_HOT 1 /* Hot reset */ | 53 | #define EEH_RESET_HOT 1 /* Hot reset */ |
54 | #define EEH_RESET_FUNDAMENTAL 3 /* Fundamental reset */ | 54 | #define EEH_RESET_FUNDAMENTAL 3 /* Fundamental reset */ |
55 | #define EEH_LOG_TEMP 1 /* EEH temporary error log */ | ||
56 | #define EEH_LOG_PERM 2 /* EEH permanent error log */ | ||
55 | 57 | ||
56 | struct eeh_ops { | 58 | struct eeh_ops { |
57 | char *name; | 59 | char *name; |
diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h index 1cfb2b09bbd9..bd1a84f65292 100644 --- a/arch/powerpc/include/asm/ppc-pci.h +++ b/arch/powerpc/include/asm/ppc-pci.h | |||
@@ -53,8 +53,6 @@ void pci_addr_cache_insert_device(struct pci_dev *dev); | |||
53 | void pci_addr_cache_remove_device(struct pci_dev *dev); | 53 | void pci_addr_cache_remove_device(struct pci_dev *dev); |
54 | void pci_addr_cache_build(void); | 54 | void pci_addr_cache_build(void); |
55 | struct pci_dev *pci_get_device_by_addr(unsigned long addr); | 55 | struct pci_dev *pci_get_device_by_addr(unsigned long addr); |
56 | #define EEH_LOG_TEMP_FAILURE 1 | ||
57 | #define EEH_LOG_PERM_FAILURE 2 | ||
58 | void eeh_slot_error_detail (struct pci_dn *pdn, int severity); | 56 | void eeh_slot_error_detail (struct pci_dn *pdn, int severity); |
59 | int eeh_pci_enable(struct pci_dn *pdn, int function); | 57 | int eeh_pci_enable(struct pci_dn *pdn, int function); |
60 | int eeh_reset_pe(struct pci_dn *); | 58 | int eeh_reset_pe(struct pci_dn *); |
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 4f329f548816..39fcecb1c16b 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c | |||
@@ -87,7 +87,6 @@ | |||
87 | #define PCI_BUS_RESET_WAIT_MSEC (60*1000) | 87 | #define PCI_BUS_RESET_WAIT_MSEC (60*1000) |
88 | 88 | ||
89 | /* RTAS tokens */ | 89 | /* RTAS tokens */ |
90 | static int ibm_slot_error_detail; | ||
91 | static int ibm_configure_bridge; | 90 | static int ibm_configure_bridge; |
92 | static int ibm_configure_pe; | 91 | static int ibm_configure_pe; |
93 | 92 | ||
@@ -100,14 +99,6 @@ EXPORT_SYMBOL(eeh_subsystem_enabled); | |||
100 | /* Lock to avoid races due to multiple reports of an error */ | 99 | /* Lock to avoid races due to multiple reports of an error */ |
101 | static DEFINE_RAW_SPINLOCK(confirm_error_lock); | 100 | static DEFINE_RAW_SPINLOCK(confirm_error_lock); |
102 | 101 | ||
103 | /* Buffer for reporting slot-error-detail rtas calls. Its here | ||
104 | * in BSS, and not dynamically alloced, so that it ends up in | ||
105 | * RMO where RTAS can access it. | ||
106 | */ | ||
107 | static unsigned char slot_errbuf[RTAS_ERROR_LOG_MAX]; | ||
108 | static DEFINE_SPINLOCK(slot_errbuf_lock); | ||
109 | static int eeh_error_buf_size; | ||
110 | |||
111 | /* Buffer for reporting pci register dumps. Its here in BSS, and | 102 | /* Buffer for reporting pci register dumps. Its here in BSS, and |
112 | * not dynamically alloced, so that it ends up in RMO where RTAS | 103 | * not dynamically alloced, so that it ends up in RMO where RTAS |
113 | * can access it. | 104 | * can access it. |
@@ -127,46 +118,6 @@ static unsigned long slot_resets; | |||
127 | #define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE) | 118 | #define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE) |
128 | 119 | ||
129 | /** | 120 | /** |
130 | * eeh_rtas_slot_error_detail - Retrieve error log through RTAS call | ||
131 | * @pdn: device node | ||
132 | * @severity: temporary or permanent error log | ||
133 | * @driver_log: driver log to be combined with the retrieved error log | ||
134 | * @loglen: length of driver log | ||
135 | * | ||
136 | * This routine should be called to retrieve error log through the dedicated | ||
137 | * RTAS call. | ||
138 | */ | ||
139 | static void eeh_rtas_slot_error_detail(struct pci_dn *pdn, int severity, | ||
140 | char *driver_log, size_t loglen) | ||
141 | { | ||
142 | int config_addr; | ||
143 | unsigned long flags; | ||
144 | int rc; | ||
145 | |||
146 | /* Log the error with the rtas logger */ | ||
147 | spin_lock_irqsave(&slot_errbuf_lock, flags); | ||
148 | memset(slot_errbuf, 0, eeh_error_buf_size); | ||
149 | |||
150 | /* Use PE configuration address, if present */ | ||
151 | config_addr = pdn->eeh_config_addr; | ||
152 | if (pdn->eeh_pe_config_addr) | ||
153 | config_addr = pdn->eeh_pe_config_addr; | ||
154 | |||
155 | rc = rtas_call(ibm_slot_error_detail, | ||
156 | 8, 1, NULL, config_addr, | ||
157 | BUID_HI(pdn->phb->buid), | ||
158 | BUID_LO(pdn->phb->buid), | ||
159 | virt_to_phys(driver_log), loglen, | ||
160 | virt_to_phys(slot_errbuf), | ||
161 | eeh_error_buf_size, | ||
162 | severity); | ||
163 | |||
164 | if (rc == 0) | ||
165 | log_error(slot_errbuf, ERR_TYPE_RTAS_LOG, 0); | ||
166 | spin_unlock_irqrestore(&slot_errbuf_lock, flags); | ||
167 | } | ||
168 | |||
169 | /** | ||
170 | * eeh_gather_pci_data - Copy assorted PCI config space registers to buff | 121 | * eeh_gather_pci_data - Copy assorted PCI config space registers to buff |
171 | * @pdn: device to report data for | 122 | * @pdn: device to report data for |
172 | * @buf: point to buffer in which to log | 123 | * @buf: point to buffer in which to log |
@@ -282,7 +233,7 @@ void eeh_slot_error_detail(struct pci_dn *pdn, int severity) | |||
282 | eeh_restore_bars(pdn); | 233 | eeh_restore_bars(pdn); |
283 | loglen = eeh_gather_pci_data(pdn, pci_regs_buf, EEH_PCI_REGS_LOG_LEN); | 234 | loglen = eeh_gather_pci_data(pdn, pci_regs_buf, EEH_PCI_REGS_LOG_LEN); |
284 | 235 | ||
285 | eeh_rtas_slot_error_detail(pdn, severity, pci_regs_buf, loglen); | 236 | eeh_ops->get_log(pdn->node, severity, pci_regs_buf, loglen); |
286 | } | 237 | } |
287 | 238 | ||
288 | /** | 239 | /** |
@@ -1071,26 +1022,14 @@ void __init eeh_init(void) | |||
1071 | } | 1022 | } |
1072 | 1023 | ||
1073 | raw_spin_lock_init(&confirm_error_lock); | 1024 | raw_spin_lock_init(&confirm_error_lock); |
1074 | spin_lock_init(&slot_errbuf_lock); | ||
1075 | 1025 | ||
1076 | np = of_find_node_by_path("/rtas"); | 1026 | np = of_find_node_by_path("/rtas"); |
1077 | if (np == NULL) | 1027 | if (np == NULL) |
1078 | return; | 1028 | return; |
1079 | 1029 | ||
1080 | ibm_slot_error_detail = rtas_token("ibm,slot-error-detail"); | ||
1081 | ibm_configure_bridge = rtas_token("ibm,configure-bridge"); | 1030 | ibm_configure_bridge = rtas_token("ibm,configure-bridge"); |
1082 | ibm_configure_pe = rtas_token("ibm,configure-pe"); | 1031 | ibm_configure_pe = rtas_token("ibm,configure-pe"); |
1083 | 1032 | ||
1084 | eeh_error_buf_size = rtas_token("rtas-error-log-max"); | ||
1085 | if (eeh_error_buf_size == RTAS_UNKNOWN_SERVICE) { | ||
1086 | eeh_error_buf_size = 1024; | ||
1087 | } | ||
1088 | if (eeh_error_buf_size > RTAS_ERROR_LOG_MAX) { | ||
1089 | printk(KERN_WARNING "EEH: rtas-error-log-max is bigger than allocated " | ||
1090 | "buffer ! (%d vs %d)", eeh_error_buf_size, RTAS_ERROR_LOG_MAX); | ||
1091 | eeh_error_buf_size = RTAS_ERROR_LOG_MAX; | ||
1092 | } | ||
1093 | |||
1094 | /* Enable EEH for all adapters. Note that eeh requires buid's */ | 1033 | /* Enable EEH for all adapters. Note that eeh requires buid's */ |
1095 | for (phb = of_find_node_by_name(NULL, "pci"); phb; | 1034 | for (phb = of_find_node_by_name(NULL, "pci"); phb; |
1096 | phb = of_find_node_by_name(phb, "pci")) { | 1035 | phb = of_find_node_by_name(phb, "pci")) { |
diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index 584defe14930..68403573a1f4 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c | |||
@@ -406,7 +406,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event) | |||
406 | * don't post the error log until after all dev drivers | 406 | * don't post the error log until after all dev drivers |
407 | * have been informed. | 407 | * have been informed. |
408 | */ | 408 | */ |
409 | eeh_slot_error_detail(frozen_pdn, EEH_LOG_TEMP_FAILURE); | 409 | eeh_slot_error_detail(frozen_pdn, EEH_LOG_TEMP); |
410 | 410 | ||
411 | /* If all device drivers were EEH-unaware, then shut | 411 | /* If all device drivers were EEH-unaware, then shut |
412 | * down all of the device drivers, and hope they | 412 | * down all of the device drivers, and hope they |
@@ -497,7 +497,7 @@ hard_fail: | |||
497 | location, drv_str, pci_str); | 497 | location, drv_str, pci_str); |
498 | 498 | ||
499 | perm_error: | 499 | perm_error: |
500 | eeh_slot_error_detail(frozen_pdn, EEH_LOG_PERM_FAILURE); | 500 | eeh_slot_error_detail(frozen_pdn, EEH_LOG_PERM); |
501 | 501 | ||
502 | /* Notify all devices that they're about to go down. */ | 502 | /* Notify all devices that they're about to go down. */ |
503 | pci_walk_bus(frozen_bus, eeh_report_failure, NULL); | 503 | pci_walk_bus(frozen_bus, eeh_report_failure, NULL); |
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index 6643e0677e95..7c8434f902bc 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c | |||
@@ -56,6 +56,15 @@ static int ibm_get_config_addr_info2; | |||
56 | static int ibm_configure_bridge; | 56 | static int ibm_configure_bridge; |
57 | static int ibm_configure_pe; | 57 | static int ibm_configure_pe; |
58 | 58 | ||
59 | /* | ||
60 | * Buffer for reporting slot-error-detail rtas calls. Its here | ||
61 | * in BSS, and not dynamically alloced, so that it ends up in | ||
62 | * RMO where RTAS can access it. | ||
63 | */ | ||
64 | static unsigned char slot_errbuf[RTAS_ERROR_LOG_MAX]; | ||
65 | static DEFINE_SPINLOCK(slot_errbuf_lock); | ||
66 | static int eeh_error_buf_size; | ||
67 | |||
59 | /** | 68 | /** |
60 | * pseries_eeh_init - EEH platform dependent initialization | 69 | * pseries_eeh_init - EEH platform dependent initialization |
61 | * | 70 | * |
@@ -107,6 +116,19 @@ static int pseries_eeh_init(void) | |||
107 | return -EINVAL; | 116 | return -EINVAL; |
108 | } | 117 | } |
109 | 118 | ||
119 | /* Initialize error log lock and size */ | ||
120 | spin_lock_init(&slot_errbuf_lock); | ||
121 | eeh_error_buf_size = rtas_token("rtas-error-log-max"); | ||
122 | if (eeh_error_buf_size == RTAS_UNKNOWN_SERVICE) { | ||
123 | pr_warning("%s: unknown EEH error log size\n", | ||
124 | __func__); | ||
125 | eeh_error_buf_size = 1024; | ||
126 | } else if (eeh_error_buf_size > RTAS_ERROR_LOG_MAX) { | ||
127 | pr_warning("%s: EEH error log size %d exceeds the maximal %d\n", | ||
128 | __func__, eeh_error_buf_size, RTAS_ERROR_LOG_MAX); | ||
129 | eeh_error_buf_size = RTAS_ERROR_LOG_MAX; | ||
130 | } | ||
131 | |||
110 | return 0; | 132 | return 0; |
111 | } | 133 | } |
112 | 134 | ||
@@ -415,7 +437,30 @@ static int pseries_eeh_wait_state(struct device_node *dn, int max_wait) | |||
415 | */ | 437 | */ |
416 | static int pseries_eeh_get_log(struct device_node *dn, int severity, char *drv_log, unsigned long len) | 438 | static int pseries_eeh_get_log(struct device_node *dn, int severity, char *drv_log, unsigned long len) |
417 | { | 439 | { |
418 | return 0; | 440 | struct pci_dn *pdn; |
441 | int config_addr; | ||
442 | unsigned long flags; | ||
443 | int ret; | ||
444 | |||
445 | pdn = PCI_DN(dn); | ||
446 | spin_lock_irqsave(&slot_errbuf_lock, flags); | ||
447 | memset(slot_errbuf, 0, eeh_error_buf_size); | ||
448 | |||
449 | /* Figure out the PE address */ | ||
450 | config_addr = pdn->eeh_config_addr; | ||
451 | if (pdn->eeh_pe_config_addr) | ||
452 | config_addr = pdn->eeh_pe_config_addr; | ||
453 | |||
454 | ret = rtas_call(ibm_slot_error_detail, 8, 1, NULL, config_addr, | ||
455 | BUID_HI(pdn->phb->buid), BUID_LO(pdn->phb->buid), | ||
456 | virt_to_phys(drv_log), len, | ||
457 | virt_to_phys(slot_errbuf), eeh_error_buf_size, | ||
458 | severity); | ||
459 | if (!ret) | ||
460 | log_error(slot_errbuf, ERR_TYPE_RTAS_LOG, 0); | ||
461 | spin_unlock_irqrestore(&slot_errbuf_lock, flags); | ||
462 | |||
463 | return ret; | ||
419 | } | 464 | } |
420 | 465 | ||
421 | /** | 466 | /** |