From: Sam Bobroff <sbobroff@linux.ibm.com>
To: linuxppc-dev@lists.ozlabs.org
Cc: oohall@gmail.com
Subject: [PATCH RFC 04/15] powerpc/eeh: Sync eeh_pe_next(), eeh_pe_find() and early-out traversals
Date: Wed, 2 Oct 2019 16:02:42 +1000 [thread overview]
Message-ID: <8893b691d44b065a293fd3357768a27231791bff.1569996166.git.sbobroff@linux.ibm.com> (raw)
In-Reply-To: <cover.1569996166.git.sbobroff@linux.ibm.com>
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
next prev parent reply other threads:[~2019-10-02 6:07 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
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 ` Sam Bobroff [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=8893b691d44b065a293fd3357768a27231791bff.1569996166.git.sbobroff@linux.ibm.com \
--to=sbobroff@linux.ibm.com \
--cc=linuxppc-dev@lists.ozlabs.org \
--cc=oohall@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).