| From 5a0cdbfd17b90a89c64a71d8aec9773ecdb20d0d Mon Sep 17 00:00:00 2001 |
| From: Gavin Shan <gwshan@linux.vnet.ibm.com> |
| Date: Wed, 27 Apr 2016 11:14:51 +1000 |
| Subject: powerpc/eeh: Restore initial state in eeh_pe_reset_and_recover() |
| |
| From: Gavin Shan <gwshan@linux.vnet.ibm.com> |
| |
| commit 5a0cdbfd17b90a89c64a71d8aec9773ecdb20d0d upstream. |
| |
| The function eeh_pe_reset_and_recover() is used to recover EEH |
| error when the passthrou device are transferred to guest and |
| backwards. The content in the device's config space will be lost |
| on PE reset issued in the middle of the recovery. The function |
| saves/restores it before/after the reset. However, config access |
| to some adapters like Broadcom BCM5719 at this point will causes |
| fenced PHB. The config space is always blocked and we save 0xFF's |
| that are restored at late point. The memory BARs are totally |
| corrupted, causing another EEH error upon access to one of the |
| memory BARs. |
| |
| This restores the config space on those adapters like BCM5719 |
| from the content saved to the EEH device when it's populated, |
| to resolve above issue. |
| |
| Fixes: 5cfb20b9 ("powerpc/eeh: Emulate EEH recovery for VFIO devices") |
| Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com> |
| Reviewed-by: Russell Currey <ruscur@russell.cc> |
| Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/powerpc/kernel/eeh_driver.c | 23 +++++++++++++++++++++++ |
| 1 file changed, 23 insertions(+) |
| |
| --- a/arch/powerpc/kernel/eeh_driver.c |
| +++ b/arch/powerpc/kernel/eeh_driver.c |
| @@ -171,6 +171,16 @@ static void *eeh_dev_save_state(void *da |
| if (!edev) |
| return NULL; |
| |
| + /* |
| + * We cannot access the config space on some adapters. |
| + * Otherwise, it will cause fenced PHB. We don't save |
| + * the content in their config space and will restore |
| + * from the initial config space saved when the EEH |
| + * device is created. |
| + */ |
| + if (edev->pe && (edev->pe->state & EEH_PE_CFG_RESTRICTED)) |
| + return NULL; |
| + |
| pdev = eeh_dev_to_pci_dev(edev); |
| if (!pdev) |
| return NULL; |
| @@ -312,6 +322,19 @@ static void *eeh_dev_restore_state(void |
| if (!edev) |
| return NULL; |
| |
| + /* |
| + * The content in the config space isn't saved because |
| + * the blocked config space on some adapters. We have |
| + * to restore the initial saved config space when the |
| + * EEH device is created. |
| + */ |
| + if (edev->pe && (edev->pe->state & EEH_PE_CFG_RESTRICTED)) { |
| + if (list_is_last(&edev->list, &edev->pe->edevs)) |
| + eeh_pe_restore_bars(edev->pe); |
| + |
| + return NULL; |
| + } |
| + |
| pdev = eeh_dev_to_pci_dev(edev); |
| if (!pdev) |
| return NULL; |