powerpc/eeh: Global mutex to protect PE tree

We have missed lots of situations where the PE hierarchy tree need
protection through the EEH global mutex. The patch fixes that for
those public APIs implemented in eeh_pe.c. The only exception is
eeh_pe_restore_bars() because it calls eeh_pe_dev_traverse(), which
has been protected by the mutex.

Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
diff --git a/arch/powerpc/platforms/pseries/eeh_pe.c b/arch/powerpc/platforms/pseries/eeh_pe.c
index 30b8d96..9d35543 100644
--- a/arch/powerpc/platforms/pseries/eeh_pe.c
+++ b/arch/powerpc/platforms/pseries/eeh_pe.c
@@ -99,8 +99,6 @@
 {
 	struct eeh_pe *pe;
 
-	eeh_lock();
-
 	list_for_each_entry(pe, &eeh_phb_pe, child) {
 		/*
 		 * Actually, we needn't check the type since
@@ -114,8 +112,6 @@
 		}
 	}
 
-	eeh_unlock();
-
 	return NULL;
 }
 
@@ -192,14 +188,21 @@
 		return NULL;
 	}
 
+	eeh_lock();
+
 	/* Traverse root PE */
 	for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
 		eeh_pe_for_each_dev(pe, edev) {
 			ret = fn(edev, flag);
-			if (ret) return ret;
+			if (ret) {
+				eeh_unlock();
+				return ret;
+			}
 		}
 	}
 
+	eeh_unlock();
+
 	return NULL;
 }
 
@@ -251,9 +254,7 @@
 	struct eeh_pe *root = eeh_phb_pe_get(edev->phb);
 	struct eeh_pe *pe;
 
-	eeh_lock();
 	pe = eeh_pe_traverse(root, __eeh_pe_get, edev);
-	eeh_unlock();
 
 	return pe;
 }
@@ -307,6 +308,8 @@
 {
 	struct eeh_pe *pe, *parent;
 
+	eeh_lock();
+
 	/*
 	 * Search the PE has been existing or not according
 	 * to the PE address. If that has been existing, the
@@ -316,6 +319,7 @@
 	pe = eeh_pe_get(edev);
 	if (pe && !(pe->type & EEH_PE_INVALID)) {
 		if (!edev->pe_config_addr) {
+			eeh_unlock();
 			pr_err("%s: PE with addr 0x%x already exists\n",
 				__func__, edev->config_addr);
 			return -EEXIST;
@@ -327,6 +331,7 @@
 
 		/* Put the edev to PE */
 		list_add_tail(&edev->list, &pe->edevs);
+		eeh_unlock();
 		pr_debug("EEH: Add %s to Bus PE#%x\n",
 			edev->dn->full_name, pe->addr);
 
@@ -345,6 +350,7 @@
 			parent->type &= ~EEH_PE_INVALID;
 			parent = parent->parent;
 		}
+		eeh_unlock();
 		pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
 			edev->dn->full_name, pe->addr, pe->parent->addr);
 
@@ -354,6 +360,7 @@
 	/* Create a new EEH PE */
 	pe = eeh_pe_alloc(edev->phb, EEH_PE_DEVICE);
 	if (!pe) {
+		eeh_unlock();
 		pr_err("%s: out of memory!\n", __func__);
 		return -ENOMEM;
 	}
@@ -370,6 +377,7 @@
 	if (!parent) {
 		parent = eeh_phb_pe_get(edev->phb);
 		if (!parent) {
+			eeh_unlock();
 			pr_err("%s: No PHB PE is found (PHB Domain=%d)\n",
 				__func__, edev->phb->global_number);
 			edev->pe = NULL;
@@ -386,6 +394,7 @@
 	list_add_tail(&pe->child, &parent->child_list);
 	list_add_tail(&edev->list, &pe->edevs);
 	edev->pe = pe;
+	eeh_unlock();
 	pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
 		edev->dn->full_name, pe->addr, pe->parent->addr);
 
@@ -413,6 +422,8 @@
 		return -EEXIST;
 	}
 
+	eeh_lock();
+
 	/* Remove the EEH device */
 	pe = edev->pe;
 	edev->pe = NULL;
@@ -457,6 +468,8 @@
 		pe = parent;
 	}
 
+	eeh_unlock();
+
 	return 0;
 }
 
@@ -502,7 +515,9 @@
  */
 void eeh_pe_state_mark(struct eeh_pe *pe, int state)
 {
+	eeh_lock();
 	eeh_pe_traverse(pe, __eeh_pe_state_mark, &state);
+	eeh_unlock();
 }
 
 /**
@@ -536,7 +551,9 @@
  */
 void eeh_pe_state_clear(struct eeh_pe *pe, int state)
 {
+	eeh_lock();
 	eeh_pe_traverse(pe, __eeh_pe_state_clear, &state);
+	eeh_unlock();
 }
 
 /**
@@ -598,6 +615,10 @@
  */
 void eeh_pe_restore_bars(struct eeh_pe *pe)
 {
+	/*
+	 * We needn't take the EEH lock since eeh_pe_dev_traverse()
+	 * will take that.
+	 */
 	eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL);
 }
 
@@ -617,6 +638,8 @@
 	struct eeh_dev *edev;
 	struct pci_dev *pdev;
 
+	eeh_lock();
+
 	if (pe->type & EEH_PE_PHB) {
 		bus = pe->phb->bus;
 	} else if (pe->type & EEH_PE_BUS) {
@@ -626,5 +649,7 @@
 			bus = pdev->bus;
 	}
 
+	eeh_unlock();
+
 	return bus;
 }