diff options
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r-- | arch/powerpc/kernel/eeh.c | 59 | ||||
-rw-r--r-- | arch/powerpc/kernel/eeh_driver.c | 90 |
2 files changed, 143 insertions, 6 deletions
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 693690827785..3350b8490dbc 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c | |||
@@ -1193,6 +1193,60 @@ int eeh_unfreeze_pe(struct eeh_pe *pe, bool sw_state) | |||
1193 | return ret; | 1193 | return ret; |
1194 | } | 1194 | } |
1195 | 1195 | ||
1196 | |||
1197 | static struct pci_device_id eeh_reset_ids[] = { | ||
1198 | { PCI_DEVICE(0x19a2, 0x0710) }, /* Emulex, BE */ | ||
1199 | { PCI_DEVICE(0x10df, 0xe220) }, /* Emulex, Lancer */ | ||
1200 | { 0 } | ||
1201 | }; | ||
1202 | |||
1203 | static int eeh_pe_change_owner(struct eeh_pe *pe) | ||
1204 | { | ||
1205 | struct eeh_dev *edev, *tmp; | ||
1206 | struct pci_dev *pdev; | ||
1207 | struct pci_device_id *id; | ||
1208 | int flags, ret; | ||
1209 | |||
1210 | /* Check PE state */ | ||
1211 | flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE); | ||
1212 | ret = eeh_ops->get_state(pe, NULL); | ||
1213 | if (ret < 0 || ret == EEH_STATE_NOT_SUPPORT) | ||
1214 | return 0; | ||
1215 | |||
1216 | /* Unfrozen PE, nothing to do */ | ||
1217 | if ((ret & flags) == flags) | ||
1218 | return 0; | ||
1219 | |||
1220 | /* Frozen PE, check if it needs PE level reset */ | ||
1221 | eeh_pe_for_each_dev(pe, edev, tmp) { | ||
1222 | pdev = eeh_dev_to_pci_dev(edev); | ||
1223 | if (!pdev) | ||
1224 | continue; | ||
1225 | |||
1226 | for (id = &eeh_reset_ids[0]; id->vendor != 0; id++) { | ||
1227 | if (id->vendor != PCI_ANY_ID && | ||
1228 | id->vendor != pdev->vendor) | ||
1229 | continue; | ||
1230 | if (id->device != PCI_ANY_ID && | ||
1231 | id->device != pdev->device) | ||
1232 | continue; | ||
1233 | if (id->subvendor != PCI_ANY_ID && | ||
1234 | id->subvendor != pdev->subsystem_vendor) | ||
1235 | continue; | ||
1236 | if (id->subdevice != PCI_ANY_ID && | ||
1237 | id->subdevice != pdev->subsystem_device) | ||
1238 | continue; | ||
1239 | |||
1240 | goto reset; | ||
1241 | } | ||
1242 | } | ||
1243 | |||
1244 | return eeh_unfreeze_pe(pe, true); | ||
1245 | |||
1246 | reset: | ||
1247 | return eeh_pe_reset_and_recover(pe); | ||
1248 | } | ||
1249 | |||
1196 | /** | 1250 | /** |
1197 | * eeh_dev_open - Increase count of pass through devices for PE | 1251 | * eeh_dev_open - Increase count of pass through devices for PE |
1198 | * @pdev: PCI device | 1252 | * @pdev: PCI device |
@@ -1224,7 +1278,7 @@ int eeh_dev_open(struct pci_dev *pdev) | |||
1224 | * in frozen PE won't work properly. Clear the frozen state | 1278 | * in frozen PE won't work properly. Clear the frozen state |
1225 | * in advance. | 1279 | * in advance. |
1226 | */ | 1280 | */ |
1227 | ret = eeh_unfreeze_pe(edev->pe, true); | 1281 | ret = eeh_pe_change_owner(edev->pe); |
1228 | if (ret) | 1282 | if (ret) |
1229 | goto out; | 1283 | goto out; |
1230 | 1284 | ||
@@ -1265,6 +1319,7 @@ void eeh_dev_release(struct pci_dev *pdev) | |||
1265 | /* Decrease PE's pass through count */ | 1319 | /* Decrease PE's pass through count */ |
1266 | atomic_dec(&edev->pe->pass_dev_cnt); | 1320 | atomic_dec(&edev->pe->pass_dev_cnt); |
1267 | WARN_ON(atomic_read(&edev->pe->pass_dev_cnt) < 0); | 1321 | WARN_ON(atomic_read(&edev->pe->pass_dev_cnt) < 0); |
1322 | eeh_pe_change_owner(edev->pe); | ||
1268 | out: | 1323 | out: |
1269 | mutex_unlock(&eeh_dev_mutex); | 1324 | mutex_unlock(&eeh_dev_mutex); |
1270 | } | 1325 | } |
@@ -1345,7 +1400,7 @@ int eeh_pe_set_option(struct eeh_pe *pe, int option) | |||
1345 | switch (option) { | 1400 | switch (option) { |
1346 | case EEH_OPT_ENABLE: | 1401 | case EEH_OPT_ENABLE: |
1347 | if (eeh_enabled()) { | 1402 | if (eeh_enabled()) { |
1348 | ret = eeh_unfreeze_pe(pe, true); | 1403 | ret = eeh_pe_change_owner(pe); |
1349 | break; | 1404 | break; |
1350 | } | 1405 | } |
1351 | ret = -EIO; | 1406 | ret = -EIO; |
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c index 948e6f99089f..3fd514f8e4b2 100644 --- a/arch/powerpc/kernel/eeh_driver.c +++ b/arch/powerpc/kernel/eeh_driver.c | |||
@@ -180,6 +180,22 @@ static bool eeh_dev_removed(struct eeh_dev *edev) | |||
180 | return false; | 180 | return false; |
181 | } | 181 | } |
182 | 182 | ||
183 | static void *eeh_dev_save_state(void *data, void *userdata) | ||
184 | { | ||
185 | struct eeh_dev *edev = data; | ||
186 | struct pci_dev *pdev; | ||
187 | |||
188 | if (!edev) | ||
189 | return NULL; | ||
190 | |||
191 | pdev = eeh_dev_to_pci_dev(edev); | ||
192 | if (!pdev) | ||
193 | return NULL; | ||
194 | |||
195 | pci_save_state(pdev); | ||
196 | return NULL; | ||
197 | } | ||
198 | |||
183 | /** | 199 | /** |
184 | * eeh_report_error - Report pci error to each device driver | 200 | * eeh_report_error - Report pci error to each device driver |
185 | * @data: eeh device | 201 | * @data: eeh device |
@@ -303,6 +319,22 @@ static void *eeh_report_reset(void *data, void *userdata) | |||
303 | return NULL; | 319 | return NULL; |
304 | } | 320 | } |
305 | 321 | ||
322 | static void *eeh_dev_restore_state(void *data, void *userdata) | ||
323 | { | ||
324 | struct eeh_dev *edev = data; | ||
325 | struct pci_dev *pdev; | ||
326 | |||
327 | if (!edev) | ||
328 | return NULL; | ||
329 | |||
330 | pdev = eeh_dev_to_pci_dev(edev); | ||
331 | if (!pdev) | ||
332 | return NULL; | ||
333 | |||
334 | pci_restore_state(pdev); | ||
335 | return NULL; | ||
336 | } | ||
337 | |||
306 | /** | 338 | /** |
307 | * eeh_report_resume - Tell device to resume normal operations | 339 | * eeh_report_resume - Tell device to resume normal operations |
308 | * @data: eeh device | 340 | * @data: eeh device |
@@ -450,10 +482,11 @@ static void *eeh_pe_detach_dev(void *data, void *userdata) | |||
450 | static void *__eeh_clear_pe_frozen_state(void *data, void *flag) | 482 | static void *__eeh_clear_pe_frozen_state(void *data, void *flag) |
451 | { | 483 | { |
452 | struct eeh_pe *pe = (struct eeh_pe *)data; | 484 | struct eeh_pe *pe = (struct eeh_pe *)data; |
485 | bool *clear_sw_state = flag; | ||
453 | int i, rc = 1; | 486 | int i, rc = 1; |
454 | 487 | ||
455 | for (i = 0; rc && i < 3; i++) | 488 | for (i = 0; rc && i < 3; i++) |
456 | rc = eeh_unfreeze_pe(pe, false); | 489 | rc = eeh_unfreeze_pe(pe, clear_sw_state); |
457 | 490 | ||
458 | /* Stop immediately on any errors */ | 491 | /* Stop immediately on any errors */ |
459 | if (rc) { | 492 | if (rc) { |
@@ -465,17 +498,66 @@ static void *__eeh_clear_pe_frozen_state(void *data, void *flag) | |||
465 | return NULL; | 498 | return NULL; |
466 | } | 499 | } |
467 | 500 | ||
468 | static int eeh_clear_pe_frozen_state(struct eeh_pe *pe) | 501 | static int eeh_clear_pe_frozen_state(struct eeh_pe *pe, |
502 | bool clear_sw_state) | ||
469 | { | 503 | { |
470 | void *rc; | 504 | void *rc; |
471 | 505 | ||
472 | rc = eeh_pe_traverse(pe, __eeh_clear_pe_frozen_state, NULL); | 506 | rc = eeh_pe_traverse(pe, __eeh_clear_pe_frozen_state, &clear_sw_state); |
473 | if (!rc) | 507 | if (!rc) |
474 | eeh_pe_state_clear(pe, EEH_PE_ISOLATED); | 508 | eeh_pe_state_clear(pe, EEH_PE_ISOLATED); |
475 | 509 | ||
476 | return rc ? -EIO : 0; | 510 | return rc ? -EIO : 0; |
477 | } | 511 | } |
478 | 512 | ||
513 | int eeh_pe_reset_and_recover(struct eeh_pe *pe) | ||
514 | { | ||
515 | int result, ret; | ||
516 | |||
517 | /* Bail if the PE is being recovered */ | ||
518 | if (pe->state & EEH_PE_RECOVERING) | ||
519 | return 0; | ||
520 | |||
521 | /* Put the PE into recovery mode */ | ||
522 | eeh_pe_state_mark(pe, EEH_PE_RECOVERING); | ||
523 | |||
524 | /* Save states */ | ||
525 | eeh_pe_dev_traverse(pe, eeh_dev_save_state, NULL); | ||
526 | |||
527 | /* Report error */ | ||
528 | eeh_pe_dev_traverse(pe, eeh_report_error, &result); | ||
529 | |||
530 | /* Issue reset */ | ||
531 | eeh_pe_state_mark(pe, EEH_PE_RESET); | ||
532 | ret = eeh_reset_pe(pe); | ||
533 | if (ret) { | ||
534 | eeh_pe_state_clear(pe, EEH_PE_RECOVERING | EEH_PE_RESET); | ||
535 | return ret; | ||
536 | } | ||
537 | eeh_pe_state_clear(pe, EEH_PE_RESET); | ||
538 | |||
539 | /* Unfreeze the PE */ | ||
540 | ret = eeh_clear_pe_frozen_state(pe, true); | ||
541 | if (ret) { | ||
542 | eeh_pe_state_clear(pe, EEH_PE_RECOVERING); | ||
543 | return ret; | ||
544 | } | ||
545 | |||
546 | /* Notify completion of reset */ | ||
547 | eeh_pe_dev_traverse(pe, eeh_report_reset, &result); | ||
548 | |||
549 | /* Restore device state */ | ||
550 | eeh_pe_dev_traverse(pe, eeh_dev_restore_state, NULL); | ||
551 | |||
552 | /* Resume */ | ||
553 | eeh_pe_dev_traverse(pe, eeh_report_resume, NULL); | ||
554 | |||
555 | /* Clear recovery mode */ | ||
556 | eeh_pe_state_clear(pe, EEH_PE_RECOVERING); | ||
557 | |||
558 | return 0; | ||
559 | } | ||
560 | |||
479 | /** | 561 | /** |
480 | * eeh_reset_device - Perform actual reset of a pci slot | 562 | * eeh_reset_device - Perform actual reset of a pci slot |
481 | * @pe: EEH PE | 563 | * @pe: EEH PE |
@@ -534,7 +616,7 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus) | |||
534 | eeh_pe_state_clear(pe, EEH_PE_RESET); | 616 | eeh_pe_state_clear(pe, EEH_PE_RESET); |
535 | 617 | ||
536 | /* Clear frozen state */ | 618 | /* Clear frozen state */ |
537 | rc = eeh_clear_pe_frozen_state(pe); | 619 | rc = eeh_clear_pe_frozen_state(pe, false); |
538 | if (rc) | 620 | if (rc) |
539 | return rc; | 621 | return rc; |
540 | 622 | ||