linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [PATCH RFC 00/15] powerpc/eeh: Synchronize access to struct eeh_pe
@ 2019-10-02  6:02 Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 01/15] powerpc/eeh: Introduce refcounting for " Sam Bobroff
                   ` (14 more replies)
  0 siblings, 15 replies; 17+ messages in thread
From: Sam Bobroff @ 2019-10-02  6:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: oohall

Hi Everyone,

It's currently possible to cause a kernel crash by being (un)lucky while adding
or removing PCI devices during EEH recovery.  The cause of the problem is that
there are trees of objects (struct eeh_pe, implemented with pointers and kernel
linked lists) that are manipulated concurrently by the PCI core and the EEH
recovery system, without synchronization.  Unsurprisingly this allows the
kernel to hit list poison values or free'd memory, and crash.

Here's a first draft of a way to address this, and I would welcome feedback at
this stage: Does it make sense? Is it simple enough? Is it maintainable? Are
there other options I should explore?  etc...

The design:

We have to consider that:
- eeh_dev_check_failure() can be called in any context, so the only methods
  that are safe to use there are a spinlock or atomics.
- Some recovery operations are blocking (and can take a long time), so they
  can't be protected by a spinlock. These operations are performed while
  traversing lists of PEs.

The simplest solution might initially seem to be to hold the pci_rescan_remove
lock during EEH recovery, but it seems to have some significant problems:
- It's a mutex, and can't be used in eeh_dev_check_failure().
- If there is a crash during recovery, either in the core kernel or a driver's
  call-back function, the lock would be held forever and that would block a lot
  of the PCI core.
- The EEH code is quite recursive and it's very difficult to work out where to
  take the lock without causing deadlocks. If there are bugs, they would likely
  be deadlocks.

So instead, this proposal uses a spinlock (eeh_pe_lock) to protect access to
the list-membership fields of eeh_pe in combination with reference counting for
blocking operations.  Because the spinlock is only protecting the list fields
(and no state), the lock only needs to be used over small sections that can be
inspected easily.

For blocking operations I'm using the standard kernel refcounting. It only
involves one extra field on each PE but it does require tweaks to maintain the
refcount in most functions that use PEs and sometimes that can be tricky.

The way the refcount is used is fairly standard: it starts at 1 and is
incremented for each pointer pointing to the PE. The initial value of 1
represents the 'existance' of the PE and it's membership in the 'tree' under
it's PHB, regardless of where in that tree it is, or if it has children. A PE
is removed from those lists before it's inital reference is dropped.

The interaction of the spinlock and refcounting provides this property: If the
spinlock is held and a ref is held on a PE then it is safe to start from that
PE and traverse anywhere along the list fields or parent pointers to other PEs
without taking references to them.  (To keep a reference to a PE for use
outside of the spinlock, a ref must be taken before the spinlock is released.)

That property can be used to perform blocking operations while traversing the
lists by 'sliding' the reference along, only holding the spinlock while moving
to the next iteration.  It seems to work well but is not perfect: when the lock
is released, the current PE may be removed and there isn't any simple, safe way
to continue the traversal. The situation can be detected, so there won't be a
crash, but traversal can be left incomplete. Perhaps this could be fixed by
using a temporary list of struct eeh_pe that is collected while holding the
spinlock (I've experimented with this, and it works but seems a bit
complicated), or perhaps we can just work around it where it matters.  It
hasn't happened at all during my testing. I'll look at it if this proposal goes
ahead.

For clarity, I have also kept the scope very tightly on the lists of struct
eeh_pe and the parent pointer. This patchset does not protect the list of
eeh_dev in each eeh_pe or the pdev member of struct eeh_dev, both of which are
also affected by races. I've left a few 'this is unsafe' comments in the code
in those areas.  I'll look more at it if this proposal goes ahead, I don't
think that they will be difficult compared to eeh_pe.

Lastly, I've included an "orphan tracking system" that I used during
development to verify that reference couting was acting as expected, but I have
no idea if it should be included in the final version.  It keeps track of PEs
that have been removed from the PHB tree, but not yet freed and makes that list
available in debugfs.  Any PEs that remain orphans for very long are going to
be the result of bugs.  It's extra risk because it itself could contain bugs,
but it could also be useful during debugging.

Cheers,
Sam.

Sam Bobroff (15):
  powerpc/eeh: Introduce refcounting for struct eeh_pe
  powerpc/eeh: Rename eeh_pe_get() to eeh_pe_find()
  powerpc/eeh: Track orphaned struct eeh_pe
  powerpc/eeh: Sync eeh_pe_next(), eeh_pe_find() and early-out
    traversals
  powerpc/eeh: Sync eeh_pe_get_parent()
  powerpc/eeh: Sync eeh_phb_pe_get()
  powerpc/eeh: Sync eeh_add_to_parent_pe() and eeh_rmv_from_parent_pe()
  powerpc/eeh: Sync eeh_handle_normal_event()
  powerpw/eeh: Sync eeh_handle_special_event(), pnv_eeh_get_pe(),
    pnv_eeh_next_error()
  powerpc/eeh: Sync eeh_phb_check_failure()
  powerpc/eeh: Sync eeh_dev_check_failure()
  powerpc/eeh: Sync eeh_pe_get_state()
  powerpc/eeh: Sync pnv_eeh_ei_write()
  powerpc/eeh: Sync eeh_force_recover_write()
  powerpc/eeh: Sync pcibios_set_pcie_reset_state()

 arch/powerpc/include/asm/eeh.h               |  20 +-
 arch/powerpc/kernel/eeh.c                    |  65 +++++-
 arch/powerpc/kernel/eeh_driver.c             |  23 +-
 arch/powerpc/kernel/eeh_pe.c                 | 230 ++++++++++++++++---
 arch/powerpc/platforms/powernv/eeh-powernv.c |  43 +++-
 5 files changed, 329 insertions(+), 52 deletions(-)

-- 
2.22.0.216.g00a2a96fc9


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH RFC 01/15] powerpc/eeh: Introduce refcounting for struct eeh_pe
  2019-10-02  6:02 [PATCH RFC 00/15] powerpc/eeh: Synchronize access to struct eeh_pe Sam Bobroff
@ 2019-10-02  6:02 ` Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 02/15] powerpc/eeh: Rename eeh_pe_get() to eeh_pe_find() Sam Bobroff
                   ` (13 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sam Bobroff @ 2019-10-02  6:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: oohall

Introduce infrastructure supporting refcounting for struct eeh_pe.

This will provide protection of the list members and struct memory so
that crashes related to accessing free memory or poisoned list
pointers can be avoided (for example, when EEH is triggered during
device removal).

While this provides no additional synchronization of the other EEH
state, it seems to be an effective way of providing the necessary
safety with a very low risk of introducing deadlocks.

Signed-off-by: Sam Bobroff <sbobroff@linux.ibm.com>
---
 arch/powerpc/include/asm/eeh.h |  7 ++++
 arch/powerpc/kernel/eeh_pe.c   | 70 +++++++++++++++++++++++++++++++++-
 2 files changed, 75 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index 5448b68ff260..54ba958760f2 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -13,6 +13,7 @@
 #include <linux/string.h>
 #include <linux/time.h>
 #include <linux/atomic.h>
+#include <linux/kref.h>
 
 #include <uapi/asm/eeh.h>
 
@@ -72,6 +73,7 @@ struct pci_dn;
 #define EEH_PE_PRI_BUS		(1 << 11)	/* Cached primary bus   */
 
 struct eeh_pe {
+	struct kref kref;		/* Reference count		*/
 	int type;			/* PE type: PHB/Bus/Device	*/
 	int state;			/* PE EEH dependent mode	*/
 	int config_addr;		/* Traditional PCI address	*/
@@ -276,7 +278,12 @@ static inline bool eeh_state_active(int state)
 
 typedef void (*eeh_edev_traverse_func)(struct eeh_dev *edev, void *flag);
 typedef void *(*eeh_pe_traverse_func)(struct eeh_pe *pe, void *flag);
+void eeh_lock_pes(unsigned long *flags);
+void eeh_unlock_pes(unsigned long flags);
 void eeh_set_pe_aux_size(int size);
+void eeh_get_pe(struct eeh_pe *pe);
+void eeh_put_pe(struct eeh_pe *pe);
+void eeh_pe_move_to_parent(struct eeh_pe **pe);
 int eeh_phb_pe_create(struct pci_controller *phb);
 int eeh_wait_state(struct eeh_pe *pe, int max_wait);
 struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb);
diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
index 177852e39a25..d455df7b4928 100644
--- a/arch/powerpc/kernel/eeh_pe.c
+++ b/arch/powerpc/kernel/eeh_pe.c
@@ -22,6 +22,25 @@
 static int eeh_pe_aux_size = 0;
 static LIST_HEAD(eeh_phb_pe);
 
+/*
+ * Lock protecting the parent and child list fields of struct eeh_pe (this does
+ * not include the edev list).  The lock does not, in general, prevent struct
+ * eeh_pe objects from being freed, but it does provide that when held and a
+ * reference is held on a PE, that neither that PE or any other PE reachable
+ * via it's parent or child fields will be freed.
+ */
+static DEFINE_RAW_SPINLOCK(eeh_pe_lock);
+
+void eeh_lock_pes(unsigned long *flags)
+{
+	raw_spin_lock_irqsave(&eeh_pe_lock, *flags);
+}
+
+void eeh_unlock_pes(unsigned long flags)
+{
+	raw_spin_unlock_irqrestore(&eeh_pe_lock, flags);
+}
+
 /**
  * eeh_set_pe_aux_size - Set PE auxillary data size
  * @size: PE auxillary data size
@@ -59,6 +78,7 @@ static struct eeh_pe *eeh_pe_alloc(struct pci_controller *phb, int type)
 	if (!pe) return NULL;
 
 	/* Initialize PHB PE */
+	kref_init(&pe->kref); /* Acquire existence ref */
 	pe->type = type;
 	pe->phb = phb;
 	INIT_LIST_HEAD(&pe->child_list);
@@ -69,6 +89,51 @@ static struct eeh_pe *eeh_pe_alloc(struct pci_controller *phb, int type)
 	return pe;
 }
 
+static void eeh_pe_free(struct kref *kref)
+{
+	struct eeh_pe *pe = container_of(kref, struct eeh_pe, kref);
+
+	if (WARN_ONCE(pe->type & EEH_PE_PHB,
+		      "EEH: PHB#%x-PE#%x: Attempt to free PHB PE!\n",
+		      pe->phb->global_number, pe->addr))
+		return;
+
+	if (WARN_ONCE(pe->parent,
+		      "EEH: PHB#%x-PE#%x: Attempt to free in-use PE!\n",
+		      pe->phb->global_number, pe->addr))
+		return;
+
+	kfree(pe);
+}
+
+void eeh_get_pe(struct eeh_pe *pe)
+{
+	if (!pe)
+		return;
+	WARN_ONCE(!kref_get_unless_zero(&pe->kref),
+		  "EEH: PHB#%x-PE#%x: Attempt to get when refcount 0!\n",
+		  pe->phb->global_number, pe->addr);
+}
+
+void eeh_put_pe(struct eeh_pe *pe)
+{
+	if (!pe)
+		return;
+	kref_put(&pe->kref, eeh_pe_free);
+}
+
+void eeh_pe_move_to_parent(struct eeh_pe **pe)
+{
+	unsigned long flags;
+	struct eeh_pe *old_pe = *pe;
+
+	eeh_lock_pes(&flags);
+	*pe = (*pe)->parent;
+	eeh_get_pe(*pe);
+	eeh_put_pe(old_pe);
+	eeh_unlock_pes(flags);
+}
+
 /**
  * eeh_phb_pe_create - Create PHB PE
  * @phb: PCI controller
@@ -439,7 +504,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
 			pr_err("%s: No PHB PE is found (PHB Domain=%d)\n",
 				__func__, pdn->phb->global_number);
 			edev->pe = NULL;
-			kfree(pe);
+			eeh_put_pe(pe); /* Release existence ref */
 			return -EEXIST;
 		}
 	}
@@ -509,7 +574,8 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev)
 			if (list_empty(&pe->edevs) &&
 			    list_empty(&pe->child_list)) {
 				list_del(&pe->child);
-				kfree(pe);
+				pe->parent = NULL;
+				eeh_put_pe(pe); /* Release existence ref */
 			} else {
 				break;
 			}
-- 
2.22.0.216.g00a2a96fc9


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC 02/15] powerpc/eeh: Rename eeh_pe_get() to eeh_pe_find()
  2019-10-02  6:02 [PATCH RFC 00/15] powerpc/eeh: Synchronize access to struct eeh_pe Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 01/15] powerpc/eeh: Introduce refcounting for " Sam Bobroff
@ 2019-10-02  6:02 ` Sam Bobroff
  2019-11-13  2:32   ` Oliver O'Halloran
  2019-10-02  6:02 ` [PATCH RFC 03/15] powerpc/eeh: Track orphaned struct eeh_pe Sam Bobroff
                   ` (12 subsequent siblings)
  14 siblings, 1 reply; 17+ messages in thread
From: Sam Bobroff @ 2019-10-02  6:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: oohall

There are now functions eeh_get_pe() and eeh_pe_get() which seems
likely to cause confusion.

Keep eeh_get_pe() because "get" is commonly used to refer to acquiring
a reference (which it does), and rename eeh_pe_get() to eeh_pe_find()
because it performs a search.

Signed-off-by: Sam Bobroff <sbobroff@linux.ibm.com>
---
 arch/powerpc/include/asm/eeh.h               |  2 +-
 arch/powerpc/kernel/eeh.c                    |  2 +-
 arch/powerpc/kernel/eeh_pe.c                 | 12 ++++++------
 arch/powerpc/platforms/powernv/eeh-powernv.c |  4 ++--
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index 54ba958760f2..e6f461d07426 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -288,7 +288,7 @@ int eeh_phb_pe_create(struct pci_controller *phb);
 int eeh_wait_state(struct eeh_pe *pe, int max_wait);
 struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb);
 struct eeh_pe *eeh_pe_next(struct eeh_pe *pe, struct eeh_pe *root);
-struct eeh_pe *eeh_pe_get(struct pci_controller *phb,
+struct eeh_pe *eeh_pe_find(struct pci_controller *phb,
 			  int pe_no, int config_addr);
 int eeh_add_to_parent_pe(struct eeh_dev *edev);
 int eeh_rmv_from_parent_pe(struct eeh_dev *edev);
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index d613e7ea7794..7e36ad0cfa2b 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -1940,7 +1940,7 @@ static ssize_t eeh_force_recover_write(struct file *filp,
 		return -ENODEV;
 
 	/* Retrieve PE */
-	pe = eeh_pe_get(hose, pe_no, 0);
+	pe = eeh_pe_find(hose, pe_no, 0);
 	if (!pe)
 		return -ENODEV;
 
diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
index d455df7b4928..fda52d1989c3 100644
--- a/arch/powerpc/kernel/eeh_pe.c
+++ b/arch/powerpc/kernel/eeh_pe.c
@@ -315,7 +315,7 @@ void eeh_pe_dev_traverse(struct eeh_pe *root,
 }
 
 /**
- * __eeh_pe_get - Check the PE address
+ * __eeh_pe_find - Check the PE address
  * @data: EEH PE
  * @flag: EEH device
  *
@@ -329,7 +329,7 @@ struct eeh_pe_get_flag {
 	int config_addr;
 };
 
-static void *__eeh_pe_get(struct eeh_pe *pe, void *flag)
+static void *__eeh_pe_find(struct eeh_pe *pe, void *flag)
 {
 	struct eeh_pe_get_flag *tmp = (struct eeh_pe_get_flag *) flag;
 
@@ -371,14 +371,14 @@ static void *__eeh_pe_get(struct eeh_pe *pe, void *flag)
  * which is composed of PCI bus/device/function number, or unified
  * PE address.
  */
-struct eeh_pe *eeh_pe_get(struct pci_controller *phb,
-		int pe_no, int config_addr)
+struct eeh_pe *eeh_pe_find(struct pci_controller *phb,
+			   int pe_no, int config_addr)
 {
 	struct eeh_pe *root = eeh_phb_pe_get(phb);
 	struct eeh_pe_get_flag tmp = { pe_no, config_addr };
 	struct eeh_pe *pe;
 
-	pe = eeh_pe_traverse(root, __eeh_pe_get, &tmp);
+	pe = eeh_pe_traverse(root, __eeh_pe_find, &tmp);
 
 	return pe;
 }
@@ -447,7 +447,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
 	 * PE should be composed of PCI bus and its subordinate
 	 * components.
 	 */
-	pe = eeh_pe_get(pdn->phb, edev->pe_config_addr, config_addr);
+	pe = eeh_pe_find(pdn->phb, edev->pe_config_addr, config_addr);
 	if (pe) {
 		if (pe->type & EEH_PE_INVALID) {
 			list_add_tail(&edev->entry, &pe->edevs);
diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c
index 25d1712b519d..e477e0b70968 100644
--- a/arch/powerpc/platforms/powernv/eeh-powernv.c
+++ b/arch/powerpc/platforms/powernv/eeh-powernv.c
@@ -142,7 +142,7 @@ static ssize_t pnv_eeh_ei_write(struct file *filp,
 		return -EINVAL;
 
 	/* Retrieve PE */
-	pe = eeh_pe_get(hose, pe_no, 0);
+	pe = eeh_pe_find(hose, pe_no, 0);
 	if (!pe)
 		return -ENODEV;
 
@@ -1425,7 +1425,7 @@ static int pnv_eeh_get_pe(struct pci_controller *hose,
 	}
 
 	/* Find the PE according to PE# */
-	dev_pe = eeh_pe_get(hose, pe_no, 0);
+	dev_pe = eeh_pe_find(hose, pe_no, 0);
 	if (!dev_pe)
 		return -EEXIST;
 
-- 
2.22.0.216.g00a2a96fc9


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC 03/15] powerpc/eeh: Track orphaned struct eeh_pe
  2019-10-02  6:02 [PATCH RFC 00/15] powerpc/eeh: Synchronize access to struct eeh_pe Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 01/15] powerpc/eeh: Introduce refcounting for " Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 02/15] powerpc/eeh: Rename eeh_pe_get() to eeh_pe_find() Sam Bobroff
@ 2019-10-02  6:02 ` Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 04/15] powerpc/eeh: Sync eeh_pe_next(), eeh_pe_find() and early-out traversals Sam Bobroff
                   ` (11 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sam Bobroff @ 2019-10-02  6:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: oohall

To facilitate debugging of (the inevitable) refcounting problems with
struct eeh_pes, detect when a struct eeh_pe has been removed from it's
global PHB list but not yet freed ("orphaned"), and collect these
PEs in a list.

They should only remain in the list briefly during processing, so any
PEs that stay longer will be the result of bugs.

The list can be examined by reading from the "eeh_pe_debug" file in
debugfs.

Signed-off-by: Sam Bobroff <sbobroff@linux.ibm.com>
---
 arch/powerpc/include/asm/eeh.h |  4 +++
 arch/powerpc/kernel/eeh.c      | 21 ++++++++++++++
 arch/powerpc/kernel/eeh_pe.c   | 53 +++++++++++++++++++++++++++++++++-
 3 files changed, 77 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index e6f461d07426..df843d04268d 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -14,6 +14,7 @@
 #include <linux/time.h>
 #include <linux/atomic.h>
 #include <linux/kref.h>
+#include <linux/seq_file.h>
 
 #include <uapi/asm/eeh.h>
 
@@ -66,6 +67,7 @@ struct pci_dn;
 #define EEH_PE_RECOVERING	(1 << 1)	/* Recovering PE	*/
 #define EEH_PE_CFG_BLOCKED	(1 << 2)	/* Block config access	*/
 #define EEH_PE_RESET		(1 << 3)	/* PE reset in progress */
+#define EEH_PE_ORPHAN		(1 << 4)	/* Removal pending      */
 
 #define EEH_PE_KEEP		(1 << 8)	/* Keep PE on hotplug	*/
 #define EEH_PE_CFG_RESTRICTED	(1 << 9)	/* Block config on error */
@@ -103,6 +105,7 @@ struct eeh_pe {
 	unsigned long stack_trace[64];
 	int trace_entries;
 #endif /* CONFIG_STACKTRACE */
+	struct list_head orphan_entry;	/* Memb. eeh_orphan_pes         */
 };
 
 #define eeh_pe_for_each_dev(pe, edev, tmp) \
@@ -280,6 +283,7 @@ typedef void (*eeh_edev_traverse_func)(struct eeh_dev *edev, void *flag);
 typedef void *(*eeh_pe_traverse_func)(struct eeh_pe *pe, void *flag);
 void eeh_lock_pes(unsigned long *flags);
 void eeh_unlock_pes(unsigned long flags);
+void eeh_pe_seq_show_orphans(struct seq_file *s);
 void eeh_set_pe_aux_size(int size);
 void eeh_get_pe(struct eeh_pe *pe);
 void eeh_put_pe(struct eeh_pe *pe);
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index 7e36ad0cfa2b..7eb6ca1ab72b 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -2153,6 +2153,24 @@ static const struct file_operations eeh_dev_break_fops = {
 	.read   = eeh_debugfs_dev_usage,
 };
 
+static int eeh_pe_debug_seq_file(struct seq_file *s, void *unused)
+{
+	eeh_pe_seq_show_orphans(s);
+	return 0;
+}
+
+static int eeh_pe_debug_dbgfs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, eeh_pe_debug_seq_file, inode->i_private);
+}
+
+static const struct file_operations eeh_pe_debug_fops = {
+	.owner		= THIS_MODULE,
+	.open		= eeh_pe_debug_dbgfs_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
 #endif
 
 static int __init eeh_init_proc(void)
@@ -2177,6 +2195,9 @@ static int __init eeh_init_proc(void)
 		debugfs_create_file_unsafe("eeh_force_recover", 0600,
 				powerpc_debugfs_root, NULL,
 				&eeh_force_recover_fops);
+		debugfs_create_file_unsafe("eeh_pe_debug", 0400,
+				powerpc_debugfs_root, NULL,
+				&eeh_pe_debug_fops);
 		eeh_cache_debugfs_init();
 #endif
 	}
diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
index fda52d1989c3..aa279474a928 100644
--- a/arch/powerpc/kernel/eeh_pe.c
+++ b/arch/powerpc/kernel/eeh_pe.c
@@ -21,6 +21,7 @@
 
 static int eeh_pe_aux_size = 0;
 static LIST_HEAD(eeh_phb_pe);
+static LIST_HEAD(eeh_orphan_pes);
 
 /*
  * Lock protecting the parent and child list fields of struct eeh_pe (this does
@@ -30,6 +31,7 @@ static LIST_HEAD(eeh_phb_pe);
  * via it's parent or child fields will be freed.
  */
 static DEFINE_RAW_SPINLOCK(eeh_pe_lock);
+static DEFINE_RAW_SPINLOCK(eeh_orphan_lock);
 
 void eeh_lock_pes(unsigned long *flags)
 {
@@ -89,9 +91,24 @@ static struct eeh_pe *eeh_pe_alloc(struct pci_controller *phb, int type)
 	return pe;
 }
 
+void eeh_pe_seq_show_orphans(struct seq_file *s)
+{
+	unsigned long flags;
+	struct eeh_pe *pe;
+
+	seq_puts(s, "Orphaned PEs awaiting free:\n");
+	raw_spin_lock_irqsave(&eeh_orphan_lock, flags);
+	list_for_each_entry(pe, &eeh_orphan_pes, orphan_entry)
+		seq_printf(s, "PHB#%x PE#%x Refcount:%d\n",
+			   pe->phb->global_number, pe->addr,
+			   kref_read(&pe->kref));
+	raw_spin_unlock_irqrestore(&eeh_orphan_lock, flags);
+}
+
 static void eeh_pe_free(struct kref *kref)
 {
 	struct eeh_pe *pe = container_of(kref, struct eeh_pe, kref);
+	unsigned long flags;
 
 	if (WARN_ONCE(pe->type & EEH_PE_PHB,
 		      "EEH: PHB#%x-PE#%x: Attempt to free PHB PE!\n",
@@ -103,6 +120,13 @@ static void eeh_pe_free(struct kref *kref)
 		      pe->phb->global_number, pe->addr))
 		return;
 
+	raw_spin_lock_irqsave(&eeh_orphan_lock, flags);
+	if (pe->state & EEH_PE_ORPHAN) {
+		pr_warn("EEH: PHB#%x-PE#%x: Orphan freed\n",
+			pe->phb->global_number, pe->addr);
+		list_del(&pe->orphan_entry);
+	}
+	raw_spin_unlock_irqrestore(&eeh_orphan_lock, flags);
 	kfree(pe);
 }
 
@@ -117,9 +141,36 @@ void eeh_get_pe(struct eeh_pe *pe)
 
 void eeh_put_pe(struct eeh_pe *pe)
 {
+	int rv, global_number, addr;
+	unsigned long flags;
+
 	if (!pe)
 		return;
-	kref_put(&pe->kref, eeh_pe_free);
+
+	global_number = pe->phb->global_number;
+	addr = pe->addr;
+	rv = kref_put(&pe->kref, eeh_pe_free);
+	raw_spin_lock_irqsave(&eeh_orphan_lock, flags);
+	if (!rv && !pe->parent && !(pe->type & EEH_PE_PHB) &&
+	    !(pe->state & EEH_PE_ORPHAN)) {
+		/* A PE's parent pointer is only set to NULL when it's removed
+		 * from it's PHB's list (in eeh_rmv_from_parent_pe()), and that
+		 * is followed by a call to eeh_put_pe(). If eeh_put_pe()
+		 * doesn't cause the PE to be free'd, then a ref is being held
+		 * elsewhere that should be released quickly. Track these PEs
+		 * so that if they are not released, they can be found
+		 * to assist debugging.
+		 */
+		pr_info("EEH: PHB#%x-PE#%x: Orphaned (unlinked from global list but still referenced)\n",
+			global_number, addr);
+
+		pe->state |= EEH_PE_ORPHAN;
+		list_add_tail(&pe->orphan_entry, &eeh_orphan_pes);
+	}
+	raw_spin_unlock_irqrestore(&eeh_orphan_lock, flags);
+	if (rv)
+		pr_debug("EEH: PHB#%x-PE#%x: Final reference released.\n",
+			 global_number, addr);
 }
 
 void eeh_pe_move_to_parent(struct eeh_pe **pe)
-- 
2.22.0.216.g00a2a96fc9


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC 04/15] powerpc/eeh: Sync eeh_pe_next(), eeh_pe_find() and early-out traversals
  2019-10-02  6:02 [PATCH RFC 00/15] powerpc/eeh: Synchronize access to struct eeh_pe Sam Bobroff
                   ` (2 preceding siblings ...)
  2019-10-02  6:02 ` [PATCH RFC 03/15] powerpc/eeh: Track orphaned struct eeh_pe Sam Bobroff
@ 2019-10-02  6:02 ` Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 05/15] powerpc/eeh: Sync eeh_pe_get_parent() Sam Bobroff
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sam Bobroff @ 2019-10-02  6:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: oohall

Reference counting for the in-line loop macro "eeh_for_each_pe()" can
be done by having eeh_pe_next() both get and put references; "rolling"
a reference along the list. This allows most loops to work without
change, although ones that use an "early-out" must manually release
the final reference.

While reference counting will keep the current iteration's PE from
being freed while it is in use, it's also necessary to check that it
hasn't been removed from the list that's being traversed.  This is
done by checking the parent pointer, which is set to NULL on removal
(see eeh_rmv_from_parent_pe()) (PHB type PEs never have their parent
set, but aren't a problem: they can't be removed). If this does occur,
the traversal is terminated. This may leave the traversal incomplete,
but that is preferable to crashing.

Signed-off-by: Sam Bobroff <sbobroff@linux.ibm.com>
---
 arch/powerpc/include/asm/eeh.h   |  7 ++++-
 arch/powerpc/kernel/eeh_driver.c |  4 ++-
 arch/powerpc/kernel/eeh_pe.c     | 50 +++++++++++++++++++++++++-------
 3 files changed, 48 insertions(+), 13 deletions(-)

diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index df843d04268d..3ab03d407eb1 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -111,8 +111,13 @@ struct eeh_pe {
 #define eeh_pe_for_each_dev(pe, edev, tmp) \
 		list_for_each_entry_safe(edev, tmp, &pe->edevs, entry)
 
+/*
+ * Note that eeh_pe_next() maintins a reference on 'pe' for each
+ * iteration and that it must be manually released if the loop is
+ * exited early (i.e. before eeh_pe_next() returns NULL).
+ */
 #define eeh_for_each_pe(root, pe) \
-	for (pe = root; pe; pe = eeh_pe_next(pe, root))
+	for (pe = root, eeh_get_pe(pe); pe; pe = eeh_pe_next(pe, root))
 
 static inline bool eeh_pe_passed(struct eeh_pe *pe)
 {
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
index 28e54fe3ac6c..b3245d0cfb22 100644
--- a/arch/powerpc/kernel/eeh_driver.c
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -590,8 +590,10 @@ static int eeh_clear_pe_frozen_state(struct eeh_pe *root, bool include_passed)
 			for (i = 0; i < 3; i++)
 				if (!eeh_unfreeze_pe(pe))
 					break;
-			if (i >= 3)
+			if (i >= 3) {
+				eeh_put_pe(pe); /* Release loop ref */
 				return -EIO;
+			}
 		}
 	}
 	eeh_pe_state_clear(root, EEH_PE_ISOLATED, include_passed);
diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
index aa279474a928..b89ed46f14e6 100644
--- a/arch/powerpc/kernel/eeh_pe.c
+++ b/arch/powerpc/kernel/eeh_pe.c
@@ -294,23 +294,44 @@ struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb)
  *
  * The function is used to retrieve the next PE in the
  * hierarchy PE tree.
+ *
+ * Consumes the ref on 'pe', returns a referenced PE (if not null).
  */
-struct eeh_pe *eeh_pe_next(struct eeh_pe *pe, struct eeh_pe *root)
+struct eeh_pe *eeh_pe_next(struct eeh_pe *prev_pe, struct eeh_pe *root)
 {
-	struct list_head *next = pe->child_list.next;
+	struct list_head *next;
+	struct eeh_pe *next_pe = NULL, *pe = prev_pe;
+	unsigned long flags;
 
+	eeh_lock_pes(&flags);
+	if (!(pe->type & EEH_PE_PHB) && !pe->parent) {
+		/* Current PE has been removed since the last iteration.
+		 * There's no way to recover so bail out. The traversal
+		 * may be incomplete.
+		 */
+		eeh_unlock_pes(flags);
+		pr_warn("EEH: Warning: PHB#%x-PE%x: Traversal possibly incomplete.\n",
+			pe->phb->global_number, pe->addr);
+		eeh_put_pe(pe); /* Release ref from last iter */
+		return NULL;
+	}
+	next = pe->child_list.next;
 	if (next == &pe->child_list) {
 		while (1) {
 			if (pe == root)
-				return NULL;
+				goto out;
 			next = pe->child.next;
 			if (next != &pe->parent->child_list)
 				break;
 			pe = pe->parent;
 		}
 	}
-
-	return list_entry(next, struct eeh_pe, child);
+	next_pe = list_entry(next, struct eeh_pe, child);
+	eeh_get_pe(next_pe); /* Acquire ref for next iter */
+out:
+	eeh_unlock_pes(flags);
+	eeh_put_pe(prev_pe); /* Release ref from last iter */
+	return next_pe;
 }
 
 /**
@@ -332,7 +353,10 @@ void *eeh_pe_traverse(struct eeh_pe *root,
 
 	eeh_for_each_pe(root, pe) {
 		ret = fn(pe, flag);
-		if (ret) return ret;
+		if (ret) {
+			eeh_put_pe(pe); /* Early-out: release last ref */
+			return ret;
+		}
 	}
 
 	return NULL;
@@ -388,24 +412,26 @@ static void *__eeh_pe_find(struct eeh_pe *pe, void *flag)
 	if (pe->type & EEH_PE_PHB)
 		return NULL;
 
+	eeh_get_pe(pe); /* Acquire ref */
 	/*
 	 * We prefer PE address. For most cases, we should
 	 * have non-zero PE address
 	 */
 	if (eeh_has_flag(EEH_VALID_PE_ZERO)) {
 		if (tmp->pe_no == pe->addr)
-			return pe;
+			return pe; /* Give ref */
 	} else {
 		if (tmp->pe_no &&
 		    (tmp->pe_no == pe->addr))
-			return pe;
+			return pe; /* Give ref */
 	}
 
 	/* Try BDF address */
 	if (tmp->config_addr &&
 	   (tmp->config_addr == pe->config_addr))
-		return pe;
+		return pe; /* Give ref */
 
+	eeh_put_pe(pe); /* Release ref */
 	return NULL;
 }
 
@@ -421,15 +447,17 @@ static void *__eeh_pe_find(struct eeh_pe *pe, void *flag)
  * notable that the PE address has 2 format: traditional PE address
  * which is composed of PCI bus/device/function number, or unified
  * PE address.
+ * Returns a ref'd PE or NULL.
  */
 struct eeh_pe *eeh_pe_find(struct pci_controller *phb,
-			   int pe_no, int config_addr)
+		int pe_no, int config_addr)
 {
-	struct eeh_pe *root = eeh_phb_pe_get(phb);
+	struct eeh_pe *root = eeh_phb_pe_get(phb); /* Acquire ref */
 	struct eeh_pe_get_flag tmp = { pe_no, config_addr };
 	struct eeh_pe *pe;
 
 	pe = eeh_pe_traverse(root, __eeh_pe_find, &tmp);
+	eeh_put_pe(root); /* Release ref */
 
 	return pe;
 }
-- 
2.22.0.216.g00a2a96fc9


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC 05/15] powerpc/eeh: Sync eeh_pe_get_parent()
  2019-10-02  6:02 [PATCH RFC 00/15] powerpc/eeh: Synchronize access to struct eeh_pe Sam Bobroff
                   ` (3 preceding siblings ...)
  2019-10-02  6:02 ` [PATCH RFC 04/15] powerpc/eeh: Sync eeh_pe_next(), eeh_pe_find() and early-out traversals Sam Bobroff
@ 2019-10-02  6:02 ` Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 06/15] powerpc/eeh: Sync eeh_phb_pe_get() Sam Bobroff
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sam Bobroff @ 2019-10-02  6:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: oohall

Synchronize access to eeh_pe.

Signed-off-by: Sam Bobroff <sbobroff@linux.ibm.com>
---
 arch/powerpc/kernel/eeh_pe.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
index b89ed46f14e6..0486d3c6ff20 100644
--- a/arch/powerpc/kernel/eeh_pe.c
+++ b/arch/powerpc/kernel/eeh_pe.c
@@ -469,10 +469,12 @@ struct eeh_pe *eeh_pe_find(struct pci_controller *phb,
  * The whole PEs existing in the system are organized as hierarchy
  * tree. The function is used to retrieve the parent PE according
  * to the parent EEH device.
+ * Returns a referenced PE.
  */
 static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev)
 {
 	struct eeh_dev *parent;
+	struct eeh_pe *pe;
 	struct pci_dn *pdn = eeh_dev_to_pdn(edev);
 
 	/*
@@ -490,8 +492,14 @@ static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev)
 		if (!parent)
 			return NULL;
 
-		if (parent->pe)
-			return parent->pe;
+		if (parent->pe) {
+			/* TODO: Unsafe until eeh_dev can be synchronized
+			 * with eeh_pe.
+			 */
+			pe = parent->pe;
+			eeh_get_pe(pe);
+			return pe;
+		}
 
 		pdn = pdn->parent;
 	}
-- 
2.22.0.216.g00a2a96fc9


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC 06/15] powerpc/eeh: Sync eeh_phb_pe_get()
  2019-10-02  6:02 [PATCH RFC 00/15] powerpc/eeh: Synchronize access to struct eeh_pe Sam Bobroff
                   ` (4 preceding siblings ...)
  2019-10-02  6:02 ` [PATCH RFC 05/15] powerpc/eeh: Sync eeh_pe_get_parent() Sam Bobroff
@ 2019-10-02  6:02 ` Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 07/15] powerpc/eeh: Sync eeh_add_to_parent_pe() and eeh_rmv_from_parent_pe() Sam Bobroff
                   ` (8 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sam Bobroff @ 2019-10-02  6:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: oohall

Synchronize access to eeh_pe.

Signed-off-by: Sam Bobroff <sbobroff@linux.ibm.com>
---
 arch/powerpc/kernel/eeh_pe.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
index 0486d3c6ff20..e89a30de2e7e 100644
--- a/arch/powerpc/kernel/eeh_pe.c
+++ b/arch/powerpc/kernel/eeh_pe.c
@@ -269,20 +269,27 @@ int eeh_wait_state(struct eeh_pe *pe, int max_wait)
  * The overall PEs form hierarchy tree. The first layer of the
  * hierarchy tree is composed of PHB PEs. The function is used
  * to retrieve the corresponding PHB PE according to the given PHB.
+ * Returns a referenced PE.
  */
 struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb)
 {
 	struct eeh_pe *pe;
+	unsigned long flags;
 
+	eeh_lock_pes(&flags);
 	list_for_each_entry(pe, &eeh_phb_pe, child) {
 		/*
 		 * Actually, we needn't check the type since
 		 * the PE for PHB has been determined when that
 		 * was created.
 		 */
-		if ((pe->type & EEH_PE_PHB) && pe->phb == phb)
-			return pe;
+		if ((pe->type & EEH_PE_PHB) && pe->phb == phb) {
+			eeh_get_pe(pe); /* Acquire ref */
+			eeh_unlock_pes(flags);
+			return pe; /* Give ref */
+		}
 	}
+	eeh_unlock_pes(flags);
 
 	return NULL;
 }
-- 
2.22.0.216.g00a2a96fc9


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC 07/15] powerpc/eeh: Sync eeh_add_to_parent_pe() and eeh_rmv_from_parent_pe()
  2019-10-02  6:02 [PATCH RFC 00/15] powerpc/eeh: Synchronize access to struct eeh_pe Sam Bobroff
                   ` (5 preceding siblings ...)
  2019-10-02  6:02 ` [PATCH RFC 06/15] powerpc/eeh: Sync eeh_phb_pe_get() Sam Bobroff
@ 2019-10-02  6:02 ` Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 08/15] powerpc/eeh: Sync eeh_handle_normal_event() Sam Bobroff
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sam Bobroff @ 2019-10-02  6:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: oohall

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 <sbobroff@linux.ibm.com>
---
 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


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC 08/15] powerpc/eeh: Sync eeh_handle_normal_event()
  2019-10-02  6:02 [PATCH RFC 00/15] powerpc/eeh: Synchronize access to struct eeh_pe Sam Bobroff
                   ` (6 preceding siblings ...)
  2019-10-02  6:02 ` [PATCH RFC 07/15] powerpc/eeh: Sync eeh_add_to_parent_pe() and eeh_rmv_from_parent_pe() Sam Bobroff
@ 2019-10-02  6:02 ` Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 09/15] powerpw/eeh: Sync eeh_handle_special_event(), pnv_eeh_get_pe(), pnv_eeh_next_error() Sam Bobroff
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sam Bobroff @ 2019-10-02  6:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: oohall

Synchronize access to eeh_pe.

Signed-off-by: Sam Bobroff <sbobroff@linux.ibm.com>
---
 arch/powerpc/kernel/eeh_driver.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
index b3245d0cfb22..c9d73070793e 100644
--- a/arch/powerpc/kernel/eeh_driver.c
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -879,6 +879,7 @@ static void eeh_clear_slot_attention(struct pci_dev *pdev)
  * & devices under this slot, and then finally restarting the device
  * drivers (which cause a second set of hotplug events to go out to
  * userspace).
+ * Consumes the reference on 'pe'.
  */
 void eeh_handle_normal_event(struct eeh_pe *pe)
 {
@@ -898,6 +899,7 @@ void eeh_handle_normal_event(struct eeh_pe *pe)
 	if (!bus) {
 		pr_err("%s: Cannot find PCI bus for PHB#%x-PE#%x\n",
 			__func__, pe->phb->global_number, pe->addr);
+		eeh_put_pe(pe); /* Release ref */
 		return;
 	}
 
@@ -1141,6 +1143,7 @@ void eeh_handle_normal_event(struct eeh_pe *pe)
 			pci_hp_remove_devices(bus);
 			pci_unlock_rescan_remove();
 			/* The passed PE should no longer be used */
+			eeh_put_pe(pe); /* Release ref */
 			return;
 		}
 	}
@@ -1160,6 +1163,7 @@ void eeh_handle_normal_event(struct eeh_pe *pe)
 	eeh_pe_state_clear(pe, EEH_PE_RECOVERING, true);
 	pr_info("PE state after recovery:\n");
 	eeh_tree_state_dump_kprintf(pe);
+	eeh_put_pe(pe); /* Release ref */
 }
 
 /**
-- 
2.22.0.216.g00a2a96fc9


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC 09/15] powerpw/eeh: Sync eeh_handle_special_event(), pnv_eeh_get_pe(), pnv_eeh_next_error()
  2019-10-02  6:02 [PATCH RFC 00/15] powerpc/eeh: Synchronize access to struct eeh_pe Sam Bobroff
                   ` (7 preceding siblings ...)
  2019-10-02  6:02 ` [PATCH RFC 08/15] powerpc/eeh: Sync eeh_handle_normal_event() Sam Bobroff
@ 2019-10-02  6:02 ` Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 10/15] powerpc/eeh: Sync eeh_phb_check_failure() Sam Bobroff
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sam Bobroff @ 2019-10-02  6:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: oohall

Synchronize access to eeh_pe.

Signed-off-by: Sam Bobroff <sbobroff@linux.ibm.com>
---
 arch/powerpc/kernel/eeh_driver.c             | 15 +++++---
 arch/powerpc/platforms/powernv/eeh-powernv.c | 38 ++++++++++++++++----
 2 files changed, 43 insertions(+), 10 deletions(-)

diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
index c9d73070793e..bc5d58bf3904 100644
--- a/arch/powerpc/kernel/eeh_driver.c
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -1184,6 +1184,7 @@ void eeh_handle_special_event(void)
 
 
 	do {
+		/* Acquire ref if rc == _FROZEN_PE, _FENCED_PHB or _DEAD_PHB */
 		rc = eeh_ops->next_error(&pe);
 
 		switch (rc) {
@@ -1195,10 +1196,11 @@ void eeh_handle_special_event(void)
 			eeh_remove_event(NULL, true);
 
 			list_for_each_entry(hose, &hose_list, list_node) {
-				phb_pe = eeh_phb_pe_get(hose);
+				phb_pe = eeh_phb_pe_get(hose); /* Acquire ref */
 				if (!phb_pe) continue;
 
 				eeh_pe_mark_isolated(phb_pe);
+				eeh_put_pe(phb_pe); /* Release ref */
 			}
 
 			eeh_serialize_unlock(flags);
@@ -1236,15 +1238,17 @@ void eeh_handle_special_event(void)
 		if (rc == EEH_NEXT_ERR_FROZEN_PE ||
 		    rc == EEH_NEXT_ERR_FENCED_PHB) {
 			eeh_pe_state_mark(pe, EEH_PE_RECOVERING);
-			eeh_handle_normal_event(pe);
+			eeh_handle_normal_event(pe); /* Give ref */
 		} else {
 			pci_lock_rescan_remove();
 			list_for_each_entry(hose, &hose_list, list_node) {
-				phb_pe = eeh_phb_pe_get(hose);
+				phb_pe = eeh_phb_pe_get(hose); /* Acquire ref */
 				if (!phb_pe ||
 				    !(phb_pe->state & EEH_PE_ISOLATED) ||
-				    (phb_pe->state & EEH_PE_RECOVERING))
+				    (phb_pe->state & EEH_PE_RECOVERING)) {
+					eeh_put_pe(phb_pe); /* Release ref */
 					continue;
+				}
 
 				eeh_for_each_pe(pe, tmp_pe)
 					eeh_pe_for_each_dev(tmp_pe, edev, tmp_edev)
@@ -1263,11 +1267,14 @@ void eeh_handle_special_event(void)
 					       __func__,
 					       pe->phb->global_number,
 					       pe->addr);
+					eeh_put_pe(phb_pe); /* Release ref */
 					break;
 				}
 				pci_hp_remove_devices(bus);
+				eeh_put_pe(phb_pe); /* Release ref */
 			}
 			pci_unlock_rescan_remove();
+			eeh_put_pe(pe); /* Release ref */
 		}
 
 		/*
diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c
index e477e0b70968..c56a796dd894 100644
--- a/arch/powerpc/platforms/powernv/eeh-powernv.c
+++ b/arch/powerpc/platforms/powernv/eeh-powernv.c
@@ -1404,6 +1404,7 @@ static void pnv_eeh_get_and_dump_hub_diag(struct pci_controller *hose)
 	}
 }
 
+/* A return of 0 indicates that *pe is set, and referenced. */
 static int pnv_eeh_get_pe(struct pci_controller *hose,
 			  u16 pe_no, struct eeh_pe **pe)
 {
@@ -1431,6 +1432,7 @@ static int pnv_eeh_get_pe(struct pci_controller *hose,
 
 	/* Freeze the (compound) PE */
 	*pe = dev_pe;
+	eeh_get_pe(*pe); /* Acquire ref */
 	if (!(dev_pe->state & EEH_PE_ISOLATED))
 		phb->freeze_pe(phb, pe_no);
 
@@ -1439,23 +1441,26 @@ static int pnv_eeh_get_pe(struct pci_controller *hose,
 	 * have been frozen. However, we still need poke until
 	 * hitting the frozen PE on top level.
 	 */
-	dev_pe = dev_pe->parent;
+	eeh_pe_move_to_parent(&dev_pe);
 	while (dev_pe && !(dev_pe->type & EEH_PE_PHB)) {
 		int ret;
 		ret = eeh_ops->get_state(dev_pe, NULL);
 		if (ret <= 0 || eeh_state_active(ret)) {
-			dev_pe = dev_pe->parent;
+			eeh_pe_move_to_parent(&dev_pe);
 			continue;
 		}
 
 		/* Frozen parent PE */
+		eeh_put_pe(*pe); /* Release ref */
 		*pe = dev_pe;
+		eeh_get_pe(*pe); /* Acquire ref */
 		if (!(dev_pe->state & EEH_PE_ISOLATED))
 			phb->freeze_pe(phb, dev_pe->addr);
 
 		/* Next one */
-		dev_pe = dev_pe->parent;
+		eeh_pe_move_to_parent(&dev_pe);
 	}
+	eeh_put_pe(dev_pe);
 
 	return 0;
 }
@@ -1469,6 +1474,8 @@ static int pnv_eeh_get_pe(struct pci_controller *hose,
  * OPAL APIs for next error to handle. The informational error is
  * handled internally by platform. However, the dead IOC, dead PHB,
  * fenced PHB and frozen PE should be handled by EEH core eventually.
+ * On return, *pe will be ref'd iff returning _FROZEN_PE, _FENCED_PHB or
+ * _DEAD_PHB.
  */
 static int pnv_eeh_next_error(struct eeh_pe **pe)
 {
@@ -1479,6 +1486,7 @@ static int pnv_eeh_next_error(struct eeh_pe **pe)
 	__be16 err_type, severity;
 	long rc;
 	int state, ret = EEH_NEXT_ERR_NONE;
+	unsigned long flags;
 
 	/*
 	 * While running here, it's safe to purge the event queue. The
@@ -1493,9 +1501,11 @@ static int pnv_eeh_next_error(struct eeh_pe **pe)
 		 * needn't take care of it any more.
 		 */
 		phb = hose->private_data;
-		phb_pe = eeh_phb_pe_get(hose);
-		if (!phb_pe || (phb_pe->state & EEH_PE_ISOLATED))
+		phb_pe = eeh_phb_pe_get(hose); /* Acquire ref */
+		if (!phb_pe || (phb_pe->state & EEH_PE_ISOLATED)) {
+			eeh_put_pe(phb_pe); /* Release ref */
 			continue;
+		}
 
 		rc = opal_pci_next_error(phb->opal_id,
 					 &frozen_pe_no, &err_type, &severity);
@@ -1503,6 +1513,7 @@ static int pnv_eeh_next_error(struct eeh_pe **pe)
 			pr_devel("%s: Invalid return value on "
 				 "PHB#%x (0x%lx) from opal_pci_next_error",
 				 __func__, hose->global_number, rc);
+			eeh_put_pe(phb_pe); /* Release ref */
 			continue;
 		}
 
@@ -1511,6 +1522,7 @@ static int pnv_eeh_next_error(struct eeh_pe **pe)
 		    be16_to_cpu(severity) == OPAL_EEH_SEV_NO_ERROR) {
 			pr_devel("%s: No error found on PHB#%x\n",
 				 __func__, hose->global_number);
+			eeh_put_pe(phb_pe); /* Release ref */
 			continue;
 		}
 
@@ -1539,19 +1551,23 @@ static int pnv_eeh_next_error(struct eeh_pe **pe)
 		case OPAL_EEH_PHB_ERROR:
 			if (be16_to_cpu(severity) == OPAL_EEH_SEV_PHB_DEAD) {
 				*pe = phb_pe;
+				eeh_get_pe(*pe); /* Acquire ref */
 				pr_err("EEH: dead PHB#%x detected, "
 				       "location: %s\n",
 					hose->global_number,
 					eeh_pe_loc_get(phb_pe));
 				ret = EEH_NEXT_ERR_DEAD_PHB;
+				/* Retain ref on pe */
 			} else if (be16_to_cpu(severity) ==
 				   OPAL_EEH_SEV_PHB_FENCED) {
 				*pe = phb_pe;
+				eeh_get_pe(*pe); /* Acquire ref */
 				pr_err("EEH: Fenced PHB#%x detected, "
 				       "location: %s\n",
 					hose->global_number,
 					eeh_pe_loc_get(phb_pe));
 				ret = EEH_NEXT_ERR_FENCED_PHB;
+				/* Retain ref on pe */
 			} else if (be16_to_cpu(severity) == OPAL_EEH_SEV_INF) {
 				pr_info("EEH: PHB#%x informative error "
 					"detected, location: %s\n",
@@ -1568,8 +1584,10 @@ static int pnv_eeh_next_error(struct eeh_pe **pe)
 			 * If we can't find the corresponding PE, we
 			 * just try to unfreeze.
 			 */
+			/* Maybe acquire ref */
 			if (pnv_eeh_get_pe(hose,
 				be64_to_cpu(frozen_pe_no), pe)) {
+				/* 'pe' was not set by pnv_eeh_get_pe() */
 				pr_info("EEH: Clear non-existing PHB#%x-PE#%llx\n",
 					hose->global_number, be64_to_cpu(frozen_pe_no));
 				pr_info("EEH: PHB location: %s\n",
@@ -1589,6 +1607,7 @@ static int pnv_eeh_next_error(struct eeh_pe **pe)
 				ret = EEH_NEXT_ERR_NONE;
 			} else if ((*pe)->state & EEH_PE_ISOLATED ||
 				   eeh_pe_passed(*pe)) {
+				eeh_put_pe(*pe); /* Release ref */
 				ret = EEH_NEXT_ERR_NONE;
 			} else {
 				pr_err("EEH: Frozen PE#%x "
@@ -1600,6 +1619,7 @@ static int pnv_eeh_next_error(struct eeh_pe **pe)
 				       eeh_pe_loc_get(*pe),
 				       eeh_pe_loc_get(phb_pe));
 				ret = EEH_NEXT_ERR_FROZEN_PE;
+				/* Retain ref on pe */
 			}
 
 			break;
@@ -1631,7 +1651,10 @@ static int pnv_eeh_next_error(struct eeh_pe **pe)
 		 * we need have to handle frozen parent PE firstly.
 		 */
 		if (ret == EEH_NEXT_ERR_FROZEN_PE) {
+			eeh_lock_pes(&flags);
 			parent_pe = (*pe)->parent;
+			eeh_get_pe(parent_pe);
+			eeh_unlock_pes(flags);
 			while (parent_pe) {
 				/* Hit the ceiling ? */
 				if (parent_pe->type & EEH_PE_PHB)
@@ -1643,13 +1666,15 @@ static int pnv_eeh_next_error(struct eeh_pe **pe)
 					*pe = parent_pe;
 
 				/* Next parent level */
-				parent_pe = parent_pe->parent;
+				eeh_pe_move_to_parent(&parent_pe);
 			}
+			eeh_put_pe(parent_pe); /* Release ref (for early-out) */
 
 			/* We possibly migrate to another PE */
 			eeh_pe_mark_isolated(*pe);
 		}
 
+		eeh_put_pe(phb_pe); /* Release ref */
 		/*
 		 * If we have no errors on the specific PHB or only
 		 * informative error there, we continue poking it.
@@ -1664,6 +1689,7 @@ static int pnv_eeh_next_error(struct eeh_pe **pe)
 	if (ret == EEH_NEXT_ERR_NONE && eeh_enabled())
 		enable_irq(eeh_event_irq);
 
+	/* *pe may be ref'd, see above */
 	return ret;
 }
 
-- 
2.22.0.216.g00a2a96fc9


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC 10/15] powerpc/eeh: Sync eeh_phb_check_failure()
  2019-10-02  6:02 [PATCH RFC 00/15] powerpc/eeh: Synchronize access to struct eeh_pe Sam Bobroff
                   ` (8 preceding siblings ...)
  2019-10-02  6:02 ` [PATCH RFC 09/15] powerpw/eeh: Sync eeh_handle_special_event(), pnv_eeh_get_pe(), pnv_eeh_next_error() Sam Bobroff
@ 2019-10-02  6:02 ` Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 11/15] powerpc/eeh: Sync eeh_dev_check_failure() Sam Bobroff
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sam Bobroff @ 2019-10-02  6:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: oohall

Synchronize access to eeh_pe.

Signed-off-by: Sam Bobroff <sbobroff@linux.ibm.com>
---
 arch/powerpc/kernel/eeh.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index 7eb6ca1ab72b..eb37cb384ff4 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -394,7 +394,7 @@ static int eeh_phb_check_failure(struct eeh_pe *pe)
 		return -EPERM;
 
 	/* Find the PHB PE */
-	phb_pe = eeh_phb_pe_get(pe->phb);
+	phb_pe = eeh_phb_pe_get(pe->phb); /* Acquire ref */
 	if (!phb_pe) {
 		pr_warn("%s Can't find PE for PHB#%x\n",
 			__func__, pe->phb->global_number);
@@ -422,9 +422,10 @@ static int eeh_phb_check_failure(struct eeh_pe *pe)
 
 	pr_debug("EEH: PHB#%x failure detected, location: %s\n",
 		phb_pe->phb->global_number, eeh_pe_loc_get(phb_pe));
-	eeh_send_failure_event(phb_pe);
+	eeh_send_failure_event(phb_pe); /* Give ref */
 	return 1;
 out:
+	eeh_put_pe(phb_pe); /* Release ref */
 	eeh_serialize_unlock(flags);
 	return ret;
 }
-- 
2.22.0.216.g00a2a96fc9


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC 11/15] powerpc/eeh: Sync eeh_dev_check_failure()
  2019-10-02  6:02 [PATCH RFC 00/15] powerpc/eeh: Synchronize access to struct eeh_pe Sam Bobroff
                   ` (9 preceding siblings ...)
  2019-10-02  6:02 ` [PATCH RFC 10/15] powerpc/eeh: Sync eeh_phb_check_failure() Sam Bobroff
@ 2019-10-02  6:02 ` Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 12/15] powerpc/eeh: Sync eeh_pe_get_state() Sam Bobroff
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sam Bobroff @ 2019-10-02  6:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: oohall

Synchronize access to eeh_pe.

Signed-off-by: Sam Bobroff <sbobroff@linux.ibm.com>
---
 arch/powerpc/kernel/eeh.c | 26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index eb37cb384ff4..171be70b34d8 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -447,7 +447,7 @@ static int eeh_phb_check_failure(struct eeh_pe *pe)
 int eeh_dev_check_failure(struct eeh_dev *edev)
 {
 	int ret;
-	unsigned long flags;
+	unsigned long flags, pe_flags;
 	struct device_node *dn;
 	struct pci_dev *dev;
 	struct eeh_pe *pe, *parent_pe;
@@ -464,7 +464,9 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
 		return 0;
 	}
 	dev = eeh_dev_to_pci_dev(edev);
+	/* TODO: Unsafe until eeh_dev can be synchronized with eeh_pe. */
 	pe = eeh_dev_to_pe(edev);
+	eeh_get_pe(pe);
 
 	/* Access to IO BARs might get this far and still not want checking. */
 	if (!pe) {
@@ -475,6 +477,7 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
 
 	if (!pe->addr && !pe->config_addr) {
 		eeh_stats.no_cfg_addr++;
+		eeh_put_pe(pe); /* Release ref */
 		return 0;
 	}
 
@@ -482,17 +485,21 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
 	 * On PowerNV platform, we might already have fenced PHB
 	 * there and we need take care of that firstly.
 	 */
-	ret = eeh_phb_check_failure(pe);
-	if (ret > 0)
+	ret = eeh_phb_check_failure(pe); /* Acquire ref */
+	if (ret > 0) {
+		eeh_put_pe(pe); /* Release ref */
 		return ret;
+	}
 
 	/*
 	 * If the PE isn't owned by us, we shouldn't check the
 	 * state. Instead, let the owner handle it if the PE has
 	 * been frozen.
 	 */
-	if (eeh_pe_passed(pe))
+	if (eeh_pe_passed(pe)) {
+		eeh_put_pe(pe); /* Release ref */
 		return 0;
+	}
 
 	/* If we already have a pending isolation event for this
 	 * slot, we know it's bad already, we don't need to check.
@@ -548,7 +555,10 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
 	 * put into frozen state as well. We should take care
 	 * that at first.
 	 */
+	eeh_lock_pes(&pe_flags);
 	parent_pe = pe->parent;
+	eeh_get_pe(parent_pe); /* Acquire ref */
+	eeh_unlock_pes(pe_flags);
 	while (parent_pe) {
 		/* Hit the ceiling ? */
 		if (parent_pe->type & EEH_PE_PHB)
@@ -557,15 +567,18 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
 		/* Frozen parent PE ? */
 		ret = eeh_ops->get_state(parent_pe, NULL);
 		if (ret > 0 && !eeh_state_active(ret)) {
+			eeh_put_pe(pe); /* Release ref */
 			pe = parent_pe;
+			eeh_get_pe(pe); /* Acquire ref */
 			pr_err("EEH: Failure of PHB#%x-PE#%x will be handled at parent PHB#%x-PE#%x.\n",
 			       pe->phb->global_number, pe->addr,
 			       pe->phb->global_number, parent_pe->addr);
 		}
 
 		/* Next parent level */
-		parent_pe = parent_pe->parent;
+		eeh_pe_move_to_parent(&parent_pe);
 	}
+	eeh_put_pe(parent_pe); /* Release ref */
 
 	eeh_stats.slot_resets++;
 
@@ -582,11 +595,12 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
 	 */
 	pr_debug("EEH: %s: Frozen PHB#%x-PE#%x detected\n",
 		__func__, pe->phb->global_number, pe->addr);
-	eeh_send_failure_event(pe);
+	eeh_send_failure_event(pe); /* Give ref */
 
 	return 1;
 
 dn_unlock:
+	eeh_put_pe(pe); /* Release ref */
 	eeh_serialize_unlock(flags);
 	return rc;
 }
-- 
2.22.0.216.g00a2a96fc9


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC 12/15] powerpc/eeh: Sync eeh_pe_get_state()
  2019-10-02  6:02 [PATCH RFC 00/15] powerpc/eeh: Synchronize access to struct eeh_pe Sam Bobroff
                   ` (10 preceding siblings ...)
  2019-10-02  6:02 ` [PATCH RFC 11/15] powerpc/eeh: Sync eeh_dev_check_failure() Sam Bobroff
@ 2019-10-02  6:02 ` Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 13/15] powerpc/eeh: Sync pnv_eeh_ei_write() Sam Bobroff
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 17+ messages in thread
From: Sam Bobroff @ 2019-10-02  6:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: oohall

Synchronize access to eeh_pe.

Signed-off-by: Sam Bobroff <sbobroff@linux.ibm.com>
---
 arch/powerpc/kernel/eeh.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index 171be70b34d8..cba16ca0694a 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -1689,6 +1689,7 @@ int eeh_pe_get_state(struct eeh_pe *pe)
 {
 	int result, ret = 0;
 	bool rst_active, dma_en, mmio_en;
+	unsigned long flags;
 
 	/* Existing PE ? */
 	if (!pe)
@@ -1703,10 +1704,14 @@ int eeh_pe_get_state(struct eeh_pe *pe)
 	 * unavailable so that the error recovery on the guest is suspended
 	 * until the recovery completes on the host.
 	 */
+	eeh_lock_pes(&flags);
 	if (pe->parent &&
 	    !(pe->state & EEH_PE_REMOVED) &&
-	    (pe->parent->state & (EEH_PE_ISOLATED | EEH_PE_RECOVERING)))
+	    (pe->parent->state & (EEH_PE_ISOLATED | EEH_PE_RECOVERING))) {
+		eeh_unlock_pes(flags);
 		return EEH_PE_STATE_UNAVAIL;
+	}
+	eeh_unlock_pes(flags);
 
 	result = eeh_ops->get_state(pe, NULL);
 	rst_active = !!(result & EEH_STATE_RESET_ACTIVE);
-- 
2.22.0.216.g00a2a96fc9


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC 13/15] powerpc/eeh: Sync pnv_eeh_ei_write()
  2019-10-02  6:02 [PATCH RFC 00/15] powerpc/eeh: Synchronize access to struct eeh_pe Sam Bobroff
                   ` (11 preceding siblings ...)
  2019-10-02  6:02 ` [PATCH RFC 12/15] powerpc/eeh: Sync eeh_pe_get_state() Sam Bobroff
@ 2019-10-02  6:02 ` Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 14/15] powerpc/eeh: Sync eeh_force_recover_write() Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 15/15] powerpc/eeh: Sync pcibios_set_pcie_reset_state() Sam Bobroff
  14 siblings, 0 replies; 17+ messages in thread
From: Sam Bobroff @ 2019-10-02  6:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: oohall

Synchronize access to eeh_pe.

Signed-off-by: Sam Bobroff <sbobroff@linux.ibm.com>
---
 arch/powerpc/platforms/powernv/eeh-powernv.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c
index c56a796dd894..12367ed2083b 100644
--- a/arch/powerpc/platforms/powernv/eeh-powernv.c
+++ b/arch/powerpc/platforms/powernv/eeh-powernv.c
@@ -148,6 +148,7 @@ static ssize_t pnv_eeh_ei_write(struct file *filp,
 
 	/* Do error injection */
 	ret = eeh_ops->err_inject(pe, type, func, addr, mask);
+	eeh_put_pe(pe); /* Release ref */
 	return ret < 0 ? ret : count;
 }
 
-- 
2.22.0.216.g00a2a96fc9


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC 14/15] powerpc/eeh: Sync eeh_force_recover_write()
  2019-10-02  6:02 [PATCH RFC 00/15] powerpc/eeh: Synchronize access to struct eeh_pe Sam Bobroff
                   ` (12 preceding siblings ...)
  2019-10-02  6:02 ` [PATCH RFC 13/15] powerpc/eeh: Sync pnv_eeh_ei_write() Sam Bobroff
@ 2019-10-02  6:02 ` Sam Bobroff
  2019-10-02  6:02 ` [PATCH RFC 15/15] powerpc/eeh: Sync pcibios_set_pcie_reset_state() Sam Bobroff
  14 siblings, 0 replies; 17+ messages in thread
From: Sam Bobroff @ 2019-10-02  6:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: oohall

Synchronize access to eeh_pe.

Signed-off-by: Sam Bobroff <sbobroff@linux.ibm.com>
---
 arch/powerpc/kernel/eeh.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index cba16ca0694a..26d9367c41a1 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -1971,7 +1971,7 @@ static ssize_t eeh_force_recover_write(struct file *filp,
 	 * from an odd state (e.g. PE removed, or recovery of a
 	 * non-isolated PE)
 	 */
-	__eeh_send_failure_event(pe);
+	__eeh_send_failure_event(pe); /* Give ref */
 
 	return ret < 0 ? ret : count;
 }
-- 
2.22.0.216.g00a2a96fc9


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH RFC 15/15] powerpc/eeh: Sync pcibios_set_pcie_reset_state()
  2019-10-02  6:02 [PATCH RFC 00/15] powerpc/eeh: Synchronize access to struct eeh_pe Sam Bobroff
                   ` (13 preceding siblings ...)
  2019-10-02  6:02 ` [PATCH RFC 14/15] powerpc/eeh: Sync eeh_force_recover_write() Sam Bobroff
@ 2019-10-02  6:02 ` Sam Bobroff
  14 siblings, 0 replies; 17+ messages in thread
From: Sam Bobroff @ 2019-10-02  6:02 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: oohall

Synchronize access to eeh_pe.

Signed-off-by: Sam Bobroff <sbobroff@linux.ibm.com>
---
 arch/powerpc/kernel/eeh.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index 26d9367c41a1..c61bfaf4ca26 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -924,6 +924,7 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state stat
 			__func__, pci_name(dev));
 		return -EINVAL;
 	}
+	eeh_get_pe(pe);
 
 	switch (state) {
 	case pcie_deassert_reset:
@@ -957,6 +958,7 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state stat
 		return -EINVAL;
 	};
 
+	eeh_put_pe(pe);
 	return 0;
 }
 
-- 
2.22.0.216.g00a2a96fc9


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH RFC 02/15] powerpc/eeh: Rename eeh_pe_get() to eeh_pe_find()
  2019-10-02  6:02 ` [PATCH RFC 02/15] powerpc/eeh: Rename eeh_pe_get() to eeh_pe_find() Sam Bobroff
@ 2019-11-13  2:32   ` Oliver O'Halloran
  0 siblings, 0 replies; 17+ messages in thread
From: Oliver O'Halloran @ 2019-11-13  2:32 UTC (permalink / raw)
  To: Sam Bobroff; +Cc: linuxppc-dev

On Wed, Oct 2, 2019 at 4:03 PM Sam Bobroff <sbobroff@linux.ibm.com> wrote:
>
> There are now functions eeh_get_pe() and eeh_pe_get() which seems
> likely to cause confusion.
>
> Keep eeh_get_pe() because "get" is commonly used to refer to acquiring
> a reference (which it does), and rename eeh_pe_get() to eeh_pe_find()
> because it performs a search.
>
> Signed-off-by: Sam Bobroff <sbobroff@linux.ibm.com>

Good idea.

Reviewed-by: Oliver O'Halloran <oohall@gmail.com>

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2019-11-13  3:43 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-10-02  6:02 [PATCH RFC 00/15] powerpc/eeh: Synchronize access to struct eeh_pe Sam Bobroff
2019-10-02  6:02 ` [PATCH RFC 01/15] powerpc/eeh: Introduce refcounting for " Sam Bobroff
2019-10-02  6:02 ` [PATCH RFC 02/15] powerpc/eeh: Rename eeh_pe_get() to eeh_pe_find() Sam Bobroff
2019-11-13  2:32   ` Oliver O'Halloran
2019-10-02  6:02 ` [PATCH RFC 03/15] powerpc/eeh: Track orphaned struct eeh_pe Sam Bobroff
2019-10-02  6:02 ` [PATCH RFC 04/15] powerpc/eeh: Sync eeh_pe_next(), eeh_pe_find() and early-out traversals Sam Bobroff
2019-10-02  6:02 ` [PATCH RFC 05/15] powerpc/eeh: Sync eeh_pe_get_parent() Sam Bobroff
2019-10-02  6:02 ` [PATCH RFC 06/15] powerpc/eeh: Sync eeh_phb_pe_get() Sam Bobroff
2019-10-02  6:02 ` [PATCH RFC 07/15] powerpc/eeh: Sync eeh_add_to_parent_pe() and eeh_rmv_from_parent_pe() Sam Bobroff
2019-10-02  6:02 ` [PATCH RFC 08/15] powerpc/eeh: Sync eeh_handle_normal_event() Sam Bobroff
2019-10-02  6:02 ` [PATCH RFC 09/15] powerpw/eeh: Sync eeh_handle_special_event(), pnv_eeh_get_pe(), pnv_eeh_next_error() Sam Bobroff
2019-10-02  6:02 ` [PATCH RFC 10/15] powerpc/eeh: Sync eeh_phb_check_failure() Sam Bobroff
2019-10-02  6:02 ` [PATCH RFC 11/15] powerpc/eeh: Sync eeh_dev_check_failure() Sam Bobroff
2019-10-02  6:02 ` [PATCH RFC 12/15] powerpc/eeh: Sync eeh_pe_get_state() Sam Bobroff
2019-10-02  6:02 ` [PATCH RFC 13/15] powerpc/eeh: Sync pnv_eeh_ei_write() Sam Bobroff
2019-10-02  6:02 ` [PATCH RFC 14/15] powerpc/eeh: Sync eeh_force_recover_write() Sam Bobroff
2019-10-02  6:02 ` [PATCH RFC 15/15] powerpc/eeh: Sync pcibios_set_pcie_reset_state() Sam Bobroff

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).