diff options
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh_driver.c | 91 |
1 files changed, 70 insertions, 21 deletions
diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index 37c2cf743133..a3fefb61097c 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/delay.h> | 25 | #include <linux/delay.h> |
26 | #include <linux/interrupt.h> | 26 | #include <linux/interrupt.h> |
27 | #include <linux/irq.h> | 27 | #include <linux/irq.h> |
28 | #include <linux/module.h> | ||
28 | #include <linux/pci.h> | 29 | #include <linux/pci.h> |
29 | #include <asm/eeh.h> | 30 | #include <asm/eeh.h> |
30 | #include <asm/eeh_event.h> | 31 | #include <asm/eeh_event.h> |
@@ -47,6 +48,41 @@ static inline const char *eeh_pcid_name(struct pci_dev *pdev) | |||
47 | return ""; | 48 | return ""; |
48 | } | 49 | } |
49 | 50 | ||
51 | /** | ||
52 | * eeh_pcid_get - Get the PCI device driver | ||
53 | * @pdev: PCI device | ||
54 | * | ||
55 | * The function is used to retrieve the PCI device driver for | ||
56 | * the indicated PCI device. Besides, we will increase the reference | ||
57 | * of the PCI device driver to prevent that being unloaded on | ||
58 | * the fly. Otherwise, kernel crash would be seen. | ||
59 | */ | ||
60 | static inline struct pci_driver *eeh_pcid_get(struct pci_dev *pdev) | ||
61 | { | ||
62 | if (!pdev || !pdev->driver) | ||
63 | return NULL; | ||
64 | |||
65 | if (!try_module_get(pdev->driver->driver.owner)) | ||
66 | return NULL; | ||
67 | |||
68 | return pdev->driver; | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * eeh_pcid_put - Dereference on the PCI device driver | ||
73 | * @pdev: PCI device | ||
74 | * | ||
75 | * The function is called to do dereference on the PCI device | ||
76 | * driver of the indicated PCI device. | ||
77 | */ | ||
78 | static inline void eeh_pcid_put(struct pci_dev *pdev) | ||
79 | { | ||
80 | if (!pdev || !pdev->driver) | ||
81 | return; | ||
82 | |||
83 | module_put(pdev->driver->driver.owner); | ||
84 | } | ||
85 | |||
50 | #if 0 | 86 | #if 0 |
51 | static void print_device_node_tree(struct pci_dn *pdn, int dent) | 87 | static void print_device_node_tree(struct pci_dn *pdn, int dent) |
52 | { | 88 | { |
@@ -128,23 +164,24 @@ static void *eeh_report_error(void *data, void *userdata) | |||
128 | struct eeh_dev *edev = (struct eeh_dev *)data; | 164 | struct eeh_dev *edev = (struct eeh_dev *)data; |
129 | struct pci_dev *dev = eeh_dev_to_pci_dev(edev); | 165 | struct pci_dev *dev = eeh_dev_to_pci_dev(edev); |
130 | enum pci_ers_result rc, *res = userdata; | 166 | enum pci_ers_result rc, *res = userdata; |
131 | struct pci_driver *driver = dev->driver; | 167 | struct pci_driver *driver; |
132 | 168 | ||
133 | /* We might not have the associated PCI device, | 169 | /* We might not have the associated PCI device, |
134 | * then we should continue for next one. | 170 | * then we should continue for next one. |
135 | */ | 171 | */ |
136 | if (!dev) return NULL; | 172 | if (!dev) return NULL; |
137 | |||
138 | dev->error_state = pci_channel_io_frozen; | 173 | dev->error_state = pci_channel_io_frozen; |
139 | 174 | ||
140 | if (!driver) | 175 | driver = eeh_pcid_get(dev); |
141 | return NULL; | 176 | if (!driver) return NULL; |
142 | 177 | ||
143 | eeh_disable_irq(dev); | 178 | eeh_disable_irq(dev); |
144 | 179 | ||
145 | if (!driver->err_handler || | 180 | if (!driver->err_handler || |
146 | !driver->err_handler->error_detected) | 181 | !driver->err_handler->error_detected) { |
182 | eeh_pcid_put(dev); | ||
147 | return NULL; | 183 | return NULL; |
184 | } | ||
148 | 185 | ||
149 | rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen); | 186 | rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen); |
150 | 187 | ||
@@ -152,6 +189,7 @@ static void *eeh_report_error(void *data, void *userdata) | |||
152 | if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; | 189 | if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; |
153 | if (*res == PCI_ERS_RESULT_NONE) *res = rc; | 190 | if (*res == PCI_ERS_RESULT_NONE) *res = rc; |
154 | 191 | ||
192 | eeh_pcid_put(dev); | ||
155 | return NULL; | 193 | return NULL; |
156 | } | 194 | } |
157 | 195 | ||
@@ -171,12 +209,14 @@ static void *eeh_report_mmio_enabled(void *data, void *userdata) | |||
171 | enum pci_ers_result rc, *res = userdata; | 209 | enum pci_ers_result rc, *res = userdata; |
172 | struct pci_driver *driver; | 210 | struct pci_driver *driver; |
173 | 211 | ||
174 | if (!dev) return NULL; | 212 | driver = eeh_pcid_get(dev); |
213 | if (!driver) return NULL; | ||
175 | 214 | ||
176 | if (!(driver = dev->driver) || | 215 | if (!driver->err_handler || |
177 | !driver->err_handler || | 216 | !driver->err_handler->mmio_enabled) { |
178 | !driver->err_handler->mmio_enabled) | 217 | eeh_pcid_put(dev); |
179 | return NULL; | 218 | return NULL; |
219 | } | ||
180 | 220 | ||
181 | rc = driver->err_handler->mmio_enabled(dev); | 221 | rc = driver->err_handler->mmio_enabled(dev); |
182 | 222 | ||
@@ -184,6 +224,7 @@ static void *eeh_report_mmio_enabled(void *data, void *userdata) | |||
184 | if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; | 224 | if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; |
185 | if (*res == PCI_ERS_RESULT_NONE) *res = rc; | 225 | if (*res == PCI_ERS_RESULT_NONE) *res = rc; |
186 | 226 | ||
227 | eeh_pcid_put(dev); | ||
187 | return NULL; | 228 | return NULL; |
188 | } | 229 | } |
189 | 230 | ||
@@ -204,16 +245,19 @@ static void *eeh_report_reset(void *data, void *userdata) | |||
204 | enum pci_ers_result rc, *res = userdata; | 245 | enum pci_ers_result rc, *res = userdata; |
205 | struct pci_driver *driver; | 246 | struct pci_driver *driver; |
206 | 247 | ||
207 | if (!dev || !(driver = dev->driver)) | 248 | if (!dev) return NULL; |
208 | return NULL; | ||
209 | |||
210 | dev->error_state = pci_channel_io_normal; | 249 | dev->error_state = pci_channel_io_normal; |
211 | 250 | ||
251 | driver = eeh_pcid_get(dev); | ||
252 | if (!driver) return NULL; | ||
253 | |||
212 | eeh_enable_irq(dev); | 254 | eeh_enable_irq(dev); |
213 | 255 | ||
214 | if (!driver->err_handler || | 256 | if (!driver->err_handler || |
215 | !driver->err_handler->slot_reset) | 257 | !driver->err_handler->slot_reset) { |
258 | eeh_pcid_put(dev); | ||
216 | return NULL; | 259 | return NULL; |
260 | } | ||
217 | 261 | ||
218 | rc = driver->err_handler->slot_reset(dev); | 262 | rc = driver->err_handler->slot_reset(dev); |
219 | if ((*res == PCI_ERS_RESULT_NONE) || | 263 | if ((*res == PCI_ERS_RESULT_NONE) || |
@@ -221,6 +265,7 @@ static void *eeh_report_reset(void *data, void *userdata) | |||
221 | if (*res == PCI_ERS_RESULT_DISCONNECT && | 265 | if (*res == PCI_ERS_RESULT_DISCONNECT && |
222 | rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; | 266 | rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; |
223 | 267 | ||
268 | eeh_pcid_put(dev); | ||
224 | return NULL; | 269 | return NULL; |
225 | } | 270 | } |
226 | 271 | ||
@@ -240,20 +285,22 @@ static void *eeh_report_resume(void *data, void *userdata) | |||
240 | struct pci_driver *driver; | 285 | struct pci_driver *driver; |
241 | 286 | ||
242 | if (!dev) return NULL; | 287 | if (!dev) return NULL; |
243 | |||
244 | dev->error_state = pci_channel_io_normal; | 288 | dev->error_state = pci_channel_io_normal; |
245 | 289 | ||
246 | if (!(driver = dev->driver)) | 290 | driver = eeh_pcid_get(dev); |
247 | return NULL; | 291 | if (!driver) return NULL; |
248 | 292 | ||
249 | eeh_enable_irq(dev); | 293 | eeh_enable_irq(dev); |
250 | 294 | ||
251 | if (!driver->err_handler || | 295 | if (!driver->err_handler || |
252 | !driver->err_handler->resume) | 296 | !driver->err_handler->resume) { |
297 | eeh_pcid_put(dev); | ||
253 | return NULL; | 298 | return NULL; |
299 | } | ||
254 | 300 | ||
255 | driver->err_handler->resume(dev); | 301 | driver->err_handler->resume(dev); |
256 | 302 | ||
303 | eeh_pcid_put(dev); | ||
257 | return NULL; | 304 | return NULL; |
258 | } | 305 | } |
259 | 306 | ||
@@ -272,20 +319,22 @@ static void *eeh_report_failure(void *data, void *userdata) | |||
272 | struct pci_driver *driver; | 319 | struct pci_driver *driver; |
273 | 320 | ||
274 | if (!dev) return NULL; | 321 | if (!dev) return NULL; |
275 | |||
276 | dev->error_state = pci_channel_io_perm_failure; | 322 | dev->error_state = pci_channel_io_perm_failure; |
277 | 323 | ||
278 | if (!(driver = dev->driver)) | 324 | driver = eeh_pcid_get(dev); |
279 | return NULL; | 325 | if (!driver) return NULL; |
280 | 326 | ||
281 | eeh_disable_irq(dev); | 327 | eeh_disable_irq(dev); |
282 | 328 | ||
283 | if (!driver->err_handler || | 329 | if (!driver->err_handler || |
284 | !driver->err_handler->error_detected) | 330 | !driver->err_handler->error_detected) { |
331 | eeh_pcid_put(dev); | ||
285 | return NULL; | 332 | return NULL; |
333 | } | ||
286 | 334 | ||
287 | driver->err_handler->error_detected(dev, pci_channel_io_perm_failure); | 335 | driver->err_handler->error_detected(dev, pci_channel_io_perm_failure); |
288 | 336 | ||
337 | eeh_pcid_put(dev); | ||
289 | return NULL; | 338 | return NULL; |
290 | } | 339 | } |
291 | 340 | ||