[PATCH RFC 07/15] powerpc/eeh: Sync eeh_add_to_parent_pe() and eeh_rmv_from_parent_pe()
From: Sam Bobroff <hidden>
Date: 2019-10-02 06:16:56
Subsystem:
linux for powerpc (32-bit and 64-bit), pci enhanced error handling (eeh) for powerpc, the rest · Maintainers:
Madhavan Srinivasan, Mahesh J Salgaonkar, Linus Torvalds
Note that even though there is currently only one place where a PE can be removed from the parent/child tree (eeh_rmv_from_parent_pe()), it is still protected against concurrent removal in case that changes in the future. Signed-off-by: Sam Bobroff <redacted> --- arch/powerpc/kernel/eeh_pe.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
index e89a30de2e7e..c9780b7109ec 100644
--- a/arch/powerpc/kernel/eeh_pe.c
+++ b/arch/powerpc/kernel/eeh_pe.c@@ -528,6 +528,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) struct eeh_pe *pe, *parent; struct pci_dn *pdn = eeh_dev_to_pdn(edev); int config_addr = (pdn->busno << 8) | (pdn->devfn); + unsigned long flags; /* Check if the PE number is valid */ if (!eeh_has_flag(EEH_VALID_PE_ZERO) && !edev->pe_config_addr) {
@@ -541,6 +542,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) * PE should be composed of PCI bus and its subordinate * components. */ + /* Acquire ref */ pe = eeh_pe_find(pdn->phb, edev->pe_config_addr, config_addr); if (pe) { if (pe->type & EEH_PE_INVALID) {
@@ -557,7 +559,6 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) parent->type &= ~EEH_PE_INVALID; parent = parent->parent; } - eeh_edev_dbg(edev, "Added to device PE (parent: PE#%x)\n", pe->parent->addr);
@@ -570,10 +571,12 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) list_add_tail(&edev->entry, &pe->edevs); eeh_edev_dbg(edev, "Added to bus PE\n"); } + eeh_put_pe(pe); /* Release ref */ return 0; } /* Create a new EEH PE */ + /* Acquire existence ref */ if (edev->physfn) pe = eeh_pe_alloc(pdn->phb, EEH_PE_VF); else
@@ -591,9 +594,9 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) * to PHB directly. Otherwise, we have to associate the * PE with its parent. */ - parent = eeh_pe_get_parent(edev); + parent = eeh_pe_get_parent(edev); /* Acquire ref */ if (!parent) { - parent = eeh_phb_pe_get(pdn->phb); + parent = eeh_phb_pe_get(pdn->phb); /* Acquire ref */ if (!parent) { pr_err("%s: No PHB PE is found (PHB Domain=%d)\n", __func__, pdn->phb->global_number);
@@ -602,6 +605,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) return -EEXIST; } } + eeh_lock_pes(&flags); pe->parent = parent; /*
@@ -609,10 +613,13 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) * link the EEH device accordingly. */ list_add_tail(&pe->child, &parent->child_list); + eeh_unlock_pes(flags); list_add_tail(&edev->entry, &pe->edevs); edev->pe = pe; eeh_edev_dbg(edev, "Added to device PE (parent: PE#%x)\n", - pe->parent->addr); + parent->addr); + eeh_put_pe(parent); /* Release ref */ + /* Retain existence ref */ return 0; }
@@ -631,12 +638,15 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev) struct eeh_pe *pe, *parent, *child; bool keep, recover; int cnt; + unsigned long flags; + /* TODO: Unsafe until eeh_dev can be synchronized * with eeh_pe. */ pe = eeh_dev_to_pe(edev); if (!pe) { eeh_edev_dbg(edev, "No PE found for device.\n"); return -EEXIST; } + eeh_get_pe(pe); /* Acquire ref */ /* Remove the EEH device */ edev->pe = NULL;
@@ -648,6 +658,7 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev) * delete the parent PE if it doesn't have associated * child PEs and EEH devices. */ + eeh_lock_pes(&flags); while (1) { parent = pe->parent;
@@ -699,8 +710,15 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev) } } + if (!parent) + break; /* PE has been removed */ + + eeh_get_pe(parent); /* Acquire ref */ + eeh_put_pe(pe); /* Release ref */ pe = parent; } + eeh_unlock_pes(flags); + eeh_put_pe(pe); /* Release ref */ return 0; }
--
2.22.0.216.g00a2a96fc9