aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc
diff options
context:
space:
mode:
authorGavin Shan <gwshan@linux.vnet.ibm.com>2014-09-30 12:38:54 +1000
committerMichael Ellerman <mpe@ellerman.id.au>2014-09-30 17:15:08 +1000
commit404079c87e2f390611b71c8f03b9f5ddb6241131 (patch)
tree2431314baf03bec6f1d413bd75d6e4af77f41f71 /arch/powerpc
parentpowerpc/eeh: Reenable PCI devices after reset (diff)
downloadlinux-dev-404079c87e2f390611b71c8f03b9f5ddb6241131.tar.xz
linux-dev-404079c87e2f390611b71c8f03b9f5ddb6241131.zip
powerpc/eeh: Clear frozen state on passing device
When passing through device, its PE might have been put into frozen state. One obvious example would be: the passed PE is forced to be offline because of hitting maximal allowed EEH errors in userland. In that case, the frozen state won't be cleared and then the PE is returned back to host, which might not have chance detecting and recovering from it. The patch adds more check when passing through device and clear the PE frozen state if necessary. Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'arch/powerpc')
-rw-r--r--arch/powerpc/kernel/eeh.c36
1 files changed, 35 insertions, 1 deletions
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index 6b4690f315d3..f5677684429e 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -1150,6 +1150,8 @@ void eeh_remove_device(struct pci_dev *dev)
int eeh_dev_open(struct pci_dev *pdev)
{
struct eeh_dev *edev;
+ int flag = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE);
+ int ret = -ENODEV;
mutex_lock(&eeh_dev_mutex);
@@ -1162,6 +1164,38 @@ int eeh_dev_open(struct pci_dev *pdev)
if (!edev || !edev->pe)
goto out;
+ /*
+ * The PE might have been put into frozen state, but we
+ * didn't detect that yet. The passed through PCI devices
+ * in frozen PE won't work properly. Clear the frozen state
+ * in advance.
+ */
+ ret = eeh_ops->get_state(edev->pe, NULL);
+ if (ret > 0 && ret != EEH_STATE_NOT_SUPPORT &&
+ (ret & flag) != flag) {
+ ret = eeh_ops->set_option(edev->pe, EEH_OPT_THAW_MMIO);
+ if (ret) {
+ pr_warn("%s: Failure %d enabling MMIO "
+ "for PHB#%x-PE#%x\n",
+ __func__, ret, edev->phb->global_number,
+ edev->pe->addr);
+ goto out;
+ }
+
+ ret = eeh_ops->set_option(edev->pe, EEH_OPT_THAW_DMA);
+ if (ret) {
+ pr_warn("%s: Failure %d enabling DMA "
+ "for PHB#%x-PE#%x\n",
+ __func__, ret, edev->phb->global_number,
+ edev->pe->addr);
+ goto out;
+ }
+ }
+
+ /* Clear software isolated state */
+ if (edev->pe->state & EEH_PE_ISOLATED)
+ eeh_pe_state_clear(edev->pe, EEH_PE_ISOLATED);
+
/* Increase PE's pass through count */
atomic_inc(&edev->pe->pass_dev_cnt);
mutex_unlock(&eeh_dev_mutex);
@@ -1169,7 +1203,7 @@ int eeh_dev_open(struct pci_dev *pdev)
return 0;
out:
mutex_unlock(&eeh_dev_mutex);
- return -ENODEV;
+ return ret;
}
EXPORT_SYMBOL_GPL(eeh_dev_open);