powerpc/eeh: Disable automatically blocked PCI config
authorGavin Shan <gwshan@linux.vnet.ibm.com>
Wed, 29 Jul 2015 23:26:51 +0000 (09:26 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 18 Aug 2015 09:34:42 +0000 (19:34 +1000)
pcibios_set_pcie_reset_state() could be called to complete
reset request when passing through PCI device, flag
EEH_PE_ISOLATED is set before saving the PCI config sapce.
On some Broadcom adapters, EEH_PE_CFG_BLOCKED is automatically
set when the flag EEH_PE_ISOLATED is marked. It caused bogus
data saved from the PCI config space, which will be restored
to the PCI adapter after the reset. Eventually, the hardware
can't work with corrupted data in PCI config space.

The patch fixes the issue with eeh_pe_state_mark_no_cfg(), which
doesn't set EEH_PE_CFG_BLOCKED when seeing EEH_PE_ISOLATED on the
PE, in order to avoid the bogus data saved and restored to the PCI
config space.

Reported-by: Rajanikanth H. Adaveeshaiah <rajanikanth.ha@in.ibm.com>
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/include/asm/ppc-pci.h
arch/powerpc/kernel/eeh.c
arch/powerpc/kernel/eeh_pe.c

index 4122a86d6858b128d0d1b319875a92485c9c105f..ca0c5bff78490eef1b3efc87b5b8f0fc68f1e727 100644 (file)
@@ -61,6 +61,7 @@ int rtas_write_config(struct pci_dn *, int where, int size, u32 val);
 int rtas_read_config(struct pci_dn *, int where, int size, u32 *val);
 void eeh_pe_state_mark(struct eeh_pe *pe, int state);
 void eeh_pe_state_clear(struct eeh_pe *pe, int state);
+void eeh_pe_state_mark_with_cfg(struct eeh_pe *pe, int state);
 void eeh_pe_dev_mode_mark(struct eeh_pe *pe, int mode);
 
 void eeh_sysfs_add_device(struct pci_dev *pdev);
index 8e61d717915e2d7106f50f995049978f9e85e397..58c598400028c7f18d9e35d85fd7a6fd34014a83 100644 (file)
@@ -750,14 +750,14 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state stat
                eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
                break;
        case pcie_hot_reset:
-               eeh_pe_state_mark(pe, EEH_PE_ISOLATED);
+               eeh_pe_state_mark_with_cfg(pe, EEH_PE_ISOLATED);
                eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE);
                eeh_pe_dev_traverse(pe, eeh_disable_and_save_dev_state, dev);
                eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED);
                eeh_ops->reset(pe, EEH_RESET_HOT);
                break;
        case pcie_warm_reset:
-               eeh_pe_state_mark(pe, EEH_PE_ISOLATED);
+               eeh_pe_state_mark_with_cfg(pe, EEH_PE_ISOLATED);
                eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE);
                eeh_pe_dev_traverse(pe, eeh_disable_and_save_dev_state, dev);
                eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED);
index 35f0b62259bbd8f8d41952bae15d35b8d4423a1c..8654cb166c19b04fea827756513a1d23e4d92272 100644 (file)
@@ -657,6 +657,28 @@ void eeh_pe_state_clear(struct eeh_pe *pe, int state)
        eeh_pe_traverse(pe, __eeh_pe_state_clear, &state);
 }
 
+/**
+ * eeh_pe_state_mark_with_cfg - Mark PE state with unblocked config space
+ * @pe: PE
+ * @state: PE state to be set
+ *
+ * Set specified flag to PE and its child PEs. The PCI config space
+ * of some PEs is blocked automatically when EEH_PE_ISOLATED is set,
+ * which isn't needed in some situations. The function allows to set
+ * the specified flag to indicated PEs without blocking their PCI
+ * config space.
+ */
+void eeh_pe_state_mark_with_cfg(struct eeh_pe *pe, int state)
+{
+       eeh_pe_traverse(pe, __eeh_pe_state_mark, &state);
+       if (!(state & EEH_PE_ISOLATED))
+               return;
+
+       /* Clear EEH_PE_CFG_BLOCKED, which might be set just now */
+       state = EEH_PE_CFG_BLOCKED;
+       eeh_pe_traverse(pe, __eeh_pe_state_clear, &state);
+}
+
 /*
  * Some PCI bridges (e.g. PLX bridges) have primary/secondary
  * buses assigned explicitly by firmware, and we probably have