aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/powerpc/include/asm/eeh.h7
-rw-r--r--arch/powerpc/platforms/pseries/eeh_pe.c50
2 files changed, 43 insertions, 14 deletions
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index 58c5ee61e70..afeb40086fb 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -45,9 +45,10 @@ struct device_node;
* in the corresponding PHB. Therefore, the root PEs should be created
* against existing PHBs in on-to-one fashion.
*/
-#define EEH_PE_PHB 1 /* PHB PE */
-#define EEH_PE_DEVICE 2 /* Device PE */
-#define EEH_PE_BUS 3 /* Bus PE */
+#define EEH_PE_INVALID (1 << 0) /* Invalid */
+#define EEH_PE_PHB (1 << 1) /* PHB PE */
+#define EEH_PE_DEVICE (1 << 2) /* Device PE */
+#define EEH_PE_BUS (1 << 3) /* Bus PE */
#define EEH_PE_ISOLATED (1 << 0) /* Isolated PE */
#define EEH_PE_RECOVERING (1 << 1) /* Recovering PE */
diff --git a/arch/powerpc/platforms/pseries/eeh_pe.c b/arch/powerpc/platforms/pseries/eeh_pe.c
index 904123c7657..51fc56abb7a 100644
--- a/arch/powerpc/platforms/pseries/eeh_pe.c
+++ b/arch/powerpc/platforms/pseries/eeh_pe.c
@@ -107,7 +107,7 @@ static struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb)
* the PE for PHB has been determined when that
* was created.
*/
- if (pe->type == EEH_PE_PHB &&
+ if ((pe->type & EEH_PE_PHB) &&
pe->phb == phb) {
eeh_unlock();
return pe;
@@ -219,7 +219,7 @@ static void *__eeh_pe_get(void *data, void *flag)
struct eeh_dev *edev = (struct eeh_dev *)flag;
/* Unexpected PHB PE */
- if (pe->type == EEH_PE_PHB)
+ if (pe->type & EEH_PE_PHB)
return NULL;
/* We prefer PE address */
@@ -314,7 +314,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
* components.
*/
pe = eeh_pe_get(edev);
- if (pe) {
+ if (pe && !(pe->type & EEH_PE_INVALID)) {
if (!edev->pe_config_addr) {
pr_err("%s: PE with addr 0x%x already exists\n",
__func__, edev->config_addr);
@@ -331,6 +331,24 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
edev->dn->full_name, pe->addr);
return 0;
+ } else if (pe && (pe->type & EEH_PE_INVALID)) {
+ list_add_tail(&edev->list, &pe->edevs);
+ edev->pe = pe;
+ /*
+ * We're running to here because of PCI hotplug caused by
+ * EEH recovery. We need clear EEH_PE_INVALID until the top.
+ */
+ parent = pe;
+ while (parent) {
+ if (!(parent->type & EEH_PE_INVALID))
+ break;
+ parent->type &= ~EEH_PE_INVALID;
+ parent = parent->parent;
+ }
+ pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
+ edev->dn->full_name, pe->addr, pe->parent->addr);
+
+ return 0;
}
/* Create a new EEH PE */
@@ -385,7 +403,8 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
*/
int eeh_rmv_from_parent_pe(struct eeh_dev *edev)
{
- struct eeh_pe *pe, *parent;
+ struct eeh_pe *pe, *parent, *child;
+ int cnt;
if (!edev->pe) {
pr_warning("%s: No PE found for EEH device %s\n",
@@ -406,13 +425,22 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev)
*/
while (1) {
parent = pe->parent;
- if (pe->type == EEH_PE_PHB)
+ if (pe->type & EEH_PE_PHB)
break;
- if (list_empty(&pe->edevs) &&
- list_empty(&pe->child_list)) {
- list_del(&pe->child);
- kfree(pe);
+ if (list_empty(&pe->edevs)) {
+ cnt = 0;
+ list_for_each_entry(child, &pe->child_list, child) {
+ if (!(pe->type & EEH_PE_INVALID)) {
+ cnt++;
+ break;
+ }
+ }
+
+ if (!cnt)
+ pe->type |= EEH_PE_INVALID;
+ else
+ break;
}
pe = parent;
@@ -578,9 +606,9 @@ struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe)
struct eeh_dev *edev;
struct pci_dev *pdev;
- if (pe->type == EEH_PE_PHB) {
+ if (pe->type & EEH_PE_PHB) {
bus = pe->phb->bus;
- } else if (pe->type == EEH_PE_BUS) {
+ } else if (pe->type & EEH_PE_BUS) {
edev = list_first_entry(&pe->edevs, struct eeh_dev, list);
pdev = eeh_dev_to_pci_dev(edev);
if (pdev)