2019-05-27 14:55:05 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2012-09-08 06:44:07 +08:00
|
|
|
/*
|
|
|
|
* The file intends to implement PE based on the information from
|
|
|
|
* platforms. Basically, there have 3 types of PEs: PHB/Bus/Device.
|
|
|
|
* All the PEs should be organized as hierarchy tree. The first level
|
|
|
|
* of the tree will be associated to existing PHBs since the particular
|
|
|
|
* PE is only meaningful in one PHB domain.
|
|
|
|
*
|
|
|
|
* Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2012.
|
|
|
|
*/
|
|
|
|
|
2013-06-27 13:46:43 +08:00
|
|
|
#include <linux/delay.h>
|
2012-09-08 06:44:07 +08:00
|
|
|
#include <linux/export.h>
|
|
|
|
#include <linux/gfp.h>
|
|
|
|
#include <linux/kernel.h>
|
2022-03-09 03:20:25 +08:00
|
|
|
#include <linux/of.h>
|
2012-09-08 06:44:07 +08:00
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
|
|
|
|
#include <asm/pci-bridge.h>
|
|
|
|
#include <asm/ppc-pci.h>
|
|
|
|
|
2014-07-17 12:41:43 +08:00
|
|
|
static int eeh_pe_aux_size = 0;
|
2012-09-08 06:44:07 +08:00
|
|
|
static LIST_HEAD(eeh_phb_pe);
|
|
|
|
|
2014-07-17 12:41:43 +08:00
|
|
|
/**
|
2023-12-28 18:15:18 +08:00
|
|
|
* eeh_set_pe_aux_size - Set PE auxiliary data size
|
|
|
|
* @size: PE auxiliary data size in bytes
|
2014-07-17 12:41:43 +08:00
|
|
|
*
|
2023-12-28 18:15:18 +08:00
|
|
|
* Set PE auxiliary data size.
|
2014-07-17 12:41:43 +08:00
|
|
|
*/
|
|
|
|
void eeh_set_pe_aux_size(int size)
|
|
|
|
{
|
|
|
|
if (size < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
eeh_pe_aux_size = size;
|
|
|
|
}
|
|
|
|
|
2012-09-08 06:44:07 +08:00
|
|
|
/**
|
|
|
|
* eeh_pe_alloc - Allocate PE
|
|
|
|
* @phb: PCI controller
|
|
|
|
* @type: PE type
|
|
|
|
*
|
|
|
|
* Allocate PE instance dynamically.
|
|
|
|
*/
|
|
|
|
static struct eeh_pe *eeh_pe_alloc(struct pci_controller *phb, int type)
|
|
|
|
{
|
|
|
|
struct eeh_pe *pe;
|
2014-07-17 12:41:43 +08:00
|
|
|
size_t alloc_size;
|
|
|
|
|
|
|
|
alloc_size = sizeof(struct eeh_pe);
|
|
|
|
if (eeh_pe_aux_size) {
|
|
|
|
alloc_size = ALIGN(alloc_size, cache_line_size());
|
|
|
|
alloc_size += eeh_pe_aux_size;
|
|
|
|
}
|
2012-09-08 06:44:07 +08:00
|
|
|
|
|
|
|
/* Allocate PHB PE */
|
2014-07-17 12:41:43 +08:00
|
|
|
pe = kzalloc(alloc_size, GFP_KERNEL);
|
2012-09-08 06:44:07 +08:00
|
|
|
if (!pe) return NULL;
|
|
|
|
|
|
|
|
/* Initialize PHB PE */
|
|
|
|
pe->type = type;
|
|
|
|
pe->phb = phb;
|
|
|
|
INIT_LIST_HEAD(&pe->child_list);
|
|
|
|
INIT_LIST_HEAD(&pe->edevs);
|
|
|
|
|
2014-07-17 12:41:43 +08:00
|
|
|
pe->data = (void *)pe + ALIGN(sizeof(struct eeh_pe),
|
|
|
|
cache_line_size());
|
2012-09-08 06:44:07 +08:00
|
|
|
return pe;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* eeh_phb_pe_create - Create PHB PE
|
|
|
|
* @phb: PCI controller
|
|
|
|
*
|
|
|
|
* The function should be called while the PHB is detected during
|
|
|
|
* system boot or PCI hotplug in order to create PHB PE.
|
|
|
|
*/
|
2012-12-22 06:04:10 +08:00
|
|
|
int eeh_phb_pe_create(struct pci_controller *phb)
|
2012-09-08 06:44:07 +08:00
|
|
|
{
|
|
|
|
struct eeh_pe *pe;
|
|
|
|
|
|
|
|
/* Allocate PHB PE */
|
|
|
|
pe = eeh_pe_alloc(phb, EEH_PE_PHB);
|
|
|
|
if (!pe) {
|
|
|
|
pr_err("%s: out of memory!\n", __func__);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Put it into the list */
|
|
|
|
list_add_tail(&pe->child, &eeh_phb_pe);
|
|
|
|
|
2016-11-16 11:02:15 +08:00
|
|
|
pr_debug("EEH: Add PE for PHB#%x\n", phb->global_number);
|
2012-09-08 06:44:07 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-09-12 09:23:32 +08:00
|
|
|
/**
|
|
|
|
* eeh_wait_state - Wait for PE state
|
|
|
|
* @pe: EEH PE
|
|
|
|
* @max_wait: maximal period in millisecond
|
|
|
|
*
|
|
|
|
* Wait for the state of associated PE. It might take some time
|
|
|
|
* to retrieve the PE's state.
|
|
|
|
*/
|
|
|
|
int eeh_wait_state(struct eeh_pe *pe, int max_wait)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int mwait;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* According to PAPR, the state of PE might be temporarily
|
|
|
|
* unavailable. Under the circumstance, we have to wait
|
|
|
|
* for indicated time determined by firmware. The maximal
|
|
|
|
* wait time is 5 minutes, which is acquired from the original
|
|
|
|
* EEH implementation. Also, the original implementation
|
|
|
|
* also defined the minimal wait time as 1 second.
|
|
|
|
*/
|
|
|
|
#define EEH_STATE_MIN_WAIT_TIME (1000)
|
|
|
|
#define EEH_STATE_MAX_WAIT_TIME (300 * 1000)
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
ret = eeh_ops->get_state(pe, &mwait);
|
|
|
|
|
|
|
|
if (ret != EEH_STATE_UNAVAILABLE)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (max_wait <= 0) {
|
|
|
|
pr_warn("%s: Timeout when getting PE's state (%d)\n",
|
|
|
|
__func__, max_wait);
|
|
|
|
return EEH_STATE_NOT_SUPPORT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mwait < EEH_STATE_MIN_WAIT_TIME) {
|
|
|
|
pr_warn("%s: Firmware returned bad wait value %d\n",
|
|
|
|
__func__, mwait);
|
|
|
|
mwait = EEH_STATE_MIN_WAIT_TIME;
|
|
|
|
} else if (mwait > EEH_STATE_MAX_WAIT_TIME) {
|
|
|
|
pr_warn("%s: Firmware returned too long wait value %d\n",
|
|
|
|
__func__, mwait);
|
|
|
|
mwait = EEH_STATE_MAX_WAIT_TIME;
|
|
|
|
}
|
|
|
|
|
|
|
|
msleep(min(mwait, max_wait));
|
|
|
|
max_wait -= mwait;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-08 06:44:07 +08:00
|
|
|
/**
|
|
|
|
* eeh_phb_pe_get - Retrieve PHB PE based on the given PHB
|
|
|
|
* @phb: PCI controller
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
2013-06-20 13:20:53 +08:00
|
|
|
struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb)
|
2012-09-08 06:44:07 +08:00
|
|
|
{
|
|
|
|
struct eeh_pe *pe;
|
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
2012-09-21 07:29:46 +08:00
|
|
|
if ((pe->type & EEH_PE_PHB) && pe->phb == phb)
|
2012-09-08 06:44:07 +08:00
|
|
|
return pe;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-09-08 06:44:08 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* eeh_pe_next - Retrieve the next PE in the tree
|
|
|
|
* @pe: current PE
|
|
|
|
* @root: root PE
|
|
|
|
*
|
|
|
|
* The function is used to retrieve the next PE in the
|
|
|
|
* hierarchy PE tree.
|
|
|
|
*/
|
2018-05-25 11:11:35 +08:00
|
|
|
struct eeh_pe *eeh_pe_next(struct eeh_pe *pe, struct eeh_pe *root)
|
2012-09-08 06:44:08 +08:00
|
|
|
{
|
|
|
|
struct list_head *next = pe->child_list.next;
|
|
|
|
|
|
|
|
if (next == &pe->child_list) {
|
|
|
|
while (1) {
|
|
|
|
if (pe == root)
|
|
|
|
return NULL;
|
|
|
|
next = pe->child.next;
|
|
|
|
if (next != &pe->parent->child_list)
|
|
|
|
break;
|
|
|
|
pe = pe->parent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return list_entry(next, struct eeh_pe, child);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* eeh_pe_traverse - Traverse PEs in the specified PHB
|
|
|
|
* @root: root PE
|
|
|
|
* @fn: callback
|
|
|
|
* @flag: extra parameter to callback
|
|
|
|
*
|
|
|
|
* The function is used to traverse the specified PE and its
|
|
|
|
* child PEs. The traversing is to be terminated once the
|
|
|
|
* callback returns something other than NULL, or no more PEs
|
|
|
|
* to be traversed.
|
|
|
|
*/
|
powerpc/eeh: Use partial hotplug for EEH unaware drivers
When EEH error happens to one specific PE, some devices with drivers
supporting EEH won't except hotplug on the device. However, there
might have other deivces without driver, or with driver without EEH
support. For the case, we need do partial hotplug in order to make
sure that the PE becomes absolutely quite during reset. Otherise,
the PE reset might fail and leads to failure of error recovery.
The current code doesn't handle that 'mixed' case properly, it either
uses the error callbacks to the drivers, or tries hotplug, but doesn't
handle a PE (EEH domain) composed of a combination of the two.
The patch intends to support so-called "partial" hotplug for EEH:
Before we do reset, we stop and remove those PCI devices without
EEH sensitive driver. The corresponding EEH devices are not detached
from its PE, but with special flag. After the reset is done, those
EEH devices with the special flag will be scanned one by one.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2013-07-24 10:24:58 +08:00
|
|
|
void *eeh_pe_traverse(struct eeh_pe *root,
|
2018-05-25 11:11:32 +08:00
|
|
|
eeh_pe_traverse_func fn, void *flag)
|
2012-09-08 06:44:08 +08:00
|
|
|
{
|
|
|
|
struct eeh_pe *pe;
|
|
|
|
void *ret;
|
|
|
|
|
2018-05-25 11:11:35 +08:00
|
|
|
eeh_for_each_pe(root, pe) {
|
2012-09-08 06:44:08 +08:00
|
|
|
ret = fn(pe, flag);
|
|
|
|
if (ret) return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-09-08 06:44:15 +08:00
|
|
|
/**
|
|
|
|
* eeh_pe_dev_traverse - Traverse the devices from the PE
|
|
|
|
* @root: EEH PE
|
|
|
|
* @fn: function callback
|
|
|
|
* @flag: extra parameter to callback
|
|
|
|
*
|
|
|
|
* The function is used to traverse the devices of the specified
|
|
|
|
* PE and its child PEs.
|
|
|
|
*/
|
2019-08-16 12:48:15 +08:00
|
|
|
void eeh_pe_dev_traverse(struct eeh_pe *root,
|
2018-05-25 11:11:32 +08:00
|
|
|
eeh_edev_traverse_func fn, void *flag)
|
2012-09-08 06:44:15 +08:00
|
|
|
{
|
|
|
|
struct eeh_pe *pe;
|
2013-07-24 10:24:56 +08:00
|
|
|
struct eeh_dev *edev, *tmp;
|
2012-09-08 06:44:15 +08:00
|
|
|
|
|
|
|
if (!root) {
|
2014-07-17 12:41:41 +08:00
|
|
|
pr_warn("%s: Invalid PE %p\n",
|
|
|
|
__func__, root);
|
2019-08-16 12:48:15 +08:00
|
|
|
return;
|
2012-09-08 06:44:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Traverse root PE */
|
2019-08-16 12:48:15 +08:00
|
|
|
eeh_for_each_pe(root, pe)
|
|
|
|
eeh_pe_for_each_dev(pe, edev, tmp)
|
|
|
|
fn(edev, flag);
|
2012-09-08 06:44:15 +08:00
|
|
|
}
|
|
|
|
|
2012-09-08 06:44:08 +08:00
|
|
|
/**
|
|
|
|
* __eeh_pe_get - Check the PE address
|
|
|
|
*
|
|
|
|
* For one particular PE, it can be identified by PE address
|
|
|
|
* or tranditional BDF address. BDF address is composed of
|
|
|
|
* Bus/Device/Function number. The extra data referred by flag
|
|
|
|
* indicates which type of address should be used.
|
|
|
|
*/
|
2018-05-25 11:11:32 +08:00
|
|
|
static void *__eeh_pe_get(struct eeh_pe *pe, void *flag)
|
2012-09-08 06:44:08 +08:00
|
|
|
{
|
powerpc/eeh: Clean up PE addressing
When support for EEH on PowerNV was added a lot of pseries specific code
was made "generic" and some of the quirks of pseries EEH came along for the
ride. One of the stranger quirks is eeh_pe containing two types of PE
address: pe->addr and pe->config_addr. There reason for this appears to be
historical baggage rather than any real requirements.
On pseries EEH PEs are manipulated using RTAS calls. Each EEH RTAS call
takes a "PE configuration address" as an input which is used to identify
which EEH PE is being manipulated by the call. When initialising the EEH
state for a device the first thing we need to do is determine the
configuration address for the PE which contains the device so we can enable
EEH on that PE. This process is outlined in PAPR which is the modern
(i.e post-2003) FW specification for pseries. However, EEH support was
first described in the pSeries RISC Platform Architecture (RPA) and
although they are mostly compatible EEH is one of the areas where they are
not.
The major difference is that RPA doesn't actually have the concept of a PE.
On RPA systems the EEH RTAS calls are done on a per-device basis using the
same config_addr that would be passed to the RTAS functions to access PCI
config space (e.g. ibm,read-pci-config). The config_addr is not identical
since the function and config register offsets of the config_addr must be
set to zero. EEH operations being done on a per-device basis doesn't make a
whole lot of sense when you consider how EEH was implemented on legacy PCI
systems.
For legacy PCI(-X) systems EEH was implemented using special PCI-PCI
bridges which contained logic to detect errors and freeze the secondary
bus when one occurred. This means that the EEH enabled state is shared
among all devices behind that EEH bridge. As a result there's no way to
implement the per-device control required for the semantics specified by
RPA. It can be made to work if we assume that a separate EEH bridge exists
for each EEH capable PCI slot and there are no bridges behind those slots.
However, RPA also specifies the ibm,configure-bridge RTAS call for
re-initalising bridges behind EEH capable slots after they are reset due
to an EEH event so that is probably not a valid assumption. This
incoherence was fixed in later PAPR, which succeeded RPA. Unfortunately,
since Linux EEH support seems to have been implemented based on the RPA
spec some of the legacy assumptions were carried over (probably for POWER4
compatibility).
The fix made in PAPR was the introduction of the "PE" concept and
redefining the EEH RTAS calls (set-eeh-option, reset-slot, etc) to operate
on a per-PE basis so all devices behind an EEH bride would share the same
EEH state. The "config_addr" argument to the EEH RTAS calls became the
"PE_config_addr" and the OS was required to use the
ibm,get-config-addr-info RTAS call to find the correct PE address for the
device. When support for the new interfaces was added to Linux it was
implemented using something like:
At probe time:
pdn->eeh_config_addr = rtas_config_addr(pdn);
pdn->eeh_pe_config_addr = rtas_get_config_addr_info(pdn);
When performing an RTAS call:
config_addr = pdn->eeh_config_addr;
if (pdn->eeh_pe_config_addr)
config_addr = pdn->eeh_pe_config_addr;
rtas_call(..., config_addr, ...);
In other words, if the ibm,get-config-addr-info RTAS call is implemented
and returned a valid result we'd use that as the argument to the EEH
RTAS calls. If not, Linux would fall back to using the device's
config_addr. Over time these addresses have moved around going from pci_dn
to eeh_dev and finally into eeh_pe. Today the users look like this:
config_addr = pe->config_addr;
if (pe->addr)
config_addr = pe->addr;
rtas_call(..., config_addr, ...);
However, considering the EEH core always operates on a per-PE basis and
even on pseries the only per-device operation is the initial call to
ibm,set-eeh-option I'm not sure if any of this actually works on an RPA
system today. It doesn't make much sense to have the fallback address in
a generic structure either since the bulk of the code which reference it
is in pseries anyway.
The EEH core makes a token effort to support looking up a PE using the
config_addr by having two arguments to eeh_pe_get(). However, a survey of
all the callers to eeh_pe_get() shows that all bar one have the config_addr
argument hard-coded to zero.The only caller that doesn't is in
eeh_pe_tree_insert() which has:
if (!eeh_has_flag(EEH_VALID_PE_ZERO) && !edev->pe_config_addr)
return -EINVAL;
pe = eeh_pe_get(hose, edev->pe_config_addr, edev->bdfn);
The third argument (config_addr) is only used if the second (pe->addr)
argument is invalid. The preceding check ensures that the call to
eeh_pe_get() will never happen if edev->pe_config_addr is invalid so there
is no situation where eeh_pe_get() will search for a PE based on the 3rd
argument. The check also means that we'll never insert a PE into the tree
where pe_config_addr is zero since EEH_VALID_PE_ZERO is never set on
pseries. All the users of the fallback address on pseries never actually
use the fallback and all the only caller that supplies something for the
config_addr argument to eeh_pe_get() never use it either. It's all dead
code.
This patch removes the fallback address from eeh_pe since nothing uses it.
Specificly, we do this by:
1) Removing pe->config_addr
2) Removing the EEH_VALID_PE_ZERO flag
3) Removing the fallback address argument to eeh_pe_get().
4) Removing all the checks for pe->addr being zero in the pseries EEH code.
This leaves us with PE's only being identified by what's in their pe->addr
field and the EEH core relying on the platform to ensure that eeh_dev's are
only inserted into the EEH tree if they're actually inside a PE.
No functional changes, I hope.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20200918093050.37344-9-oohall@gmail.com
2020-09-18 17:30:50 +08:00
|
|
|
int *target_pe = flag;
|
2012-09-08 06:44:08 +08:00
|
|
|
|
powerpc/eeh: Clean up PE addressing
When support for EEH on PowerNV was added a lot of pseries specific code
was made "generic" and some of the quirks of pseries EEH came along for the
ride. One of the stranger quirks is eeh_pe containing two types of PE
address: pe->addr and pe->config_addr. There reason for this appears to be
historical baggage rather than any real requirements.
On pseries EEH PEs are manipulated using RTAS calls. Each EEH RTAS call
takes a "PE configuration address" as an input which is used to identify
which EEH PE is being manipulated by the call. When initialising the EEH
state for a device the first thing we need to do is determine the
configuration address for the PE which contains the device so we can enable
EEH on that PE. This process is outlined in PAPR which is the modern
(i.e post-2003) FW specification for pseries. However, EEH support was
first described in the pSeries RISC Platform Architecture (RPA) and
although they are mostly compatible EEH is one of the areas where they are
not.
The major difference is that RPA doesn't actually have the concept of a PE.
On RPA systems the EEH RTAS calls are done on a per-device basis using the
same config_addr that would be passed to the RTAS functions to access PCI
config space (e.g. ibm,read-pci-config). The config_addr is not identical
since the function and config register offsets of the config_addr must be
set to zero. EEH operations being done on a per-device basis doesn't make a
whole lot of sense when you consider how EEH was implemented on legacy PCI
systems.
For legacy PCI(-X) systems EEH was implemented using special PCI-PCI
bridges which contained logic to detect errors and freeze the secondary
bus when one occurred. This means that the EEH enabled state is shared
among all devices behind that EEH bridge. As a result there's no way to
implement the per-device control required for the semantics specified by
RPA. It can be made to work if we assume that a separate EEH bridge exists
for each EEH capable PCI slot and there are no bridges behind those slots.
However, RPA also specifies the ibm,configure-bridge RTAS call for
re-initalising bridges behind EEH capable slots after they are reset due
to an EEH event so that is probably not a valid assumption. This
incoherence was fixed in later PAPR, which succeeded RPA. Unfortunately,
since Linux EEH support seems to have been implemented based on the RPA
spec some of the legacy assumptions were carried over (probably for POWER4
compatibility).
The fix made in PAPR was the introduction of the "PE" concept and
redefining the EEH RTAS calls (set-eeh-option, reset-slot, etc) to operate
on a per-PE basis so all devices behind an EEH bride would share the same
EEH state. The "config_addr" argument to the EEH RTAS calls became the
"PE_config_addr" and the OS was required to use the
ibm,get-config-addr-info RTAS call to find the correct PE address for the
device. When support for the new interfaces was added to Linux it was
implemented using something like:
At probe time:
pdn->eeh_config_addr = rtas_config_addr(pdn);
pdn->eeh_pe_config_addr = rtas_get_config_addr_info(pdn);
When performing an RTAS call:
config_addr = pdn->eeh_config_addr;
if (pdn->eeh_pe_config_addr)
config_addr = pdn->eeh_pe_config_addr;
rtas_call(..., config_addr, ...);
In other words, if the ibm,get-config-addr-info RTAS call is implemented
and returned a valid result we'd use that as the argument to the EEH
RTAS calls. If not, Linux would fall back to using the device's
config_addr. Over time these addresses have moved around going from pci_dn
to eeh_dev and finally into eeh_pe. Today the users look like this:
config_addr = pe->config_addr;
if (pe->addr)
config_addr = pe->addr;
rtas_call(..., config_addr, ...);
However, considering the EEH core always operates on a per-PE basis and
even on pseries the only per-device operation is the initial call to
ibm,set-eeh-option I'm not sure if any of this actually works on an RPA
system today. It doesn't make much sense to have the fallback address in
a generic structure either since the bulk of the code which reference it
is in pseries anyway.
The EEH core makes a token effort to support looking up a PE using the
config_addr by having two arguments to eeh_pe_get(). However, a survey of
all the callers to eeh_pe_get() shows that all bar one have the config_addr
argument hard-coded to zero.The only caller that doesn't is in
eeh_pe_tree_insert() which has:
if (!eeh_has_flag(EEH_VALID_PE_ZERO) && !edev->pe_config_addr)
return -EINVAL;
pe = eeh_pe_get(hose, edev->pe_config_addr, edev->bdfn);
The third argument (config_addr) is only used if the second (pe->addr)
argument is invalid. The preceding check ensures that the call to
eeh_pe_get() will never happen if edev->pe_config_addr is invalid so there
is no situation where eeh_pe_get() will search for a PE based on the 3rd
argument. The check also means that we'll never insert a PE into the tree
where pe_config_addr is zero since EEH_VALID_PE_ZERO is never set on
pseries. All the users of the fallback address on pseries never actually
use the fallback and all the only caller that supplies something for the
config_addr argument to eeh_pe_get() never use it either. It's all dead
code.
This patch removes the fallback address from eeh_pe since nothing uses it.
Specificly, we do this by:
1) Removing pe->config_addr
2) Removing the EEH_VALID_PE_ZERO flag
3) Removing the fallback address argument to eeh_pe_get().
4) Removing all the checks for pe->addr being zero in the pseries EEH code.
This leaves us with PE's only being identified by what's in their pe->addr
field and the EEH core relying on the platform to ensure that eeh_dev's are
only inserted into the EEH tree if they're actually inside a PE.
No functional changes, I hope.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20200918093050.37344-9-oohall@gmail.com
2020-09-18 17:30:50 +08:00
|
|
|
/* PHB PEs are special and should be ignored */
|
2012-09-12 03:16:16 +08:00
|
|
|
if (pe->type & EEH_PE_PHB)
|
2012-09-08 06:44:08 +08:00
|
|
|
return NULL;
|
|
|
|
|
powerpc/eeh: Clean up PE addressing
When support for EEH on PowerNV was added a lot of pseries specific code
was made "generic" and some of the quirks of pseries EEH came along for the
ride. One of the stranger quirks is eeh_pe containing two types of PE
address: pe->addr and pe->config_addr. There reason for this appears to be
historical baggage rather than any real requirements.
On pseries EEH PEs are manipulated using RTAS calls. Each EEH RTAS call
takes a "PE configuration address" as an input which is used to identify
which EEH PE is being manipulated by the call. When initialising the EEH
state for a device the first thing we need to do is determine the
configuration address for the PE which contains the device so we can enable
EEH on that PE. This process is outlined in PAPR which is the modern
(i.e post-2003) FW specification for pseries. However, EEH support was
first described in the pSeries RISC Platform Architecture (RPA) and
although they are mostly compatible EEH is one of the areas where they are
not.
The major difference is that RPA doesn't actually have the concept of a PE.
On RPA systems the EEH RTAS calls are done on a per-device basis using the
same config_addr that would be passed to the RTAS functions to access PCI
config space (e.g. ibm,read-pci-config). The config_addr is not identical
since the function and config register offsets of the config_addr must be
set to zero. EEH operations being done on a per-device basis doesn't make a
whole lot of sense when you consider how EEH was implemented on legacy PCI
systems.
For legacy PCI(-X) systems EEH was implemented using special PCI-PCI
bridges which contained logic to detect errors and freeze the secondary
bus when one occurred. This means that the EEH enabled state is shared
among all devices behind that EEH bridge. As a result there's no way to
implement the per-device control required for the semantics specified by
RPA. It can be made to work if we assume that a separate EEH bridge exists
for each EEH capable PCI slot and there are no bridges behind those slots.
However, RPA also specifies the ibm,configure-bridge RTAS call for
re-initalising bridges behind EEH capable slots after they are reset due
to an EEH event so that is probably not a valid assumption. This
incoherence was fixed in later PAPR, which succeeded RPA. Unfortunately,
since Linux EEH support seems to have been implemented based on the RPA
spec some of the legacy assumptions were carried over (probably for POWER4
compatibility).
The fix made in PAPR was the introduction of the "PE" concept and
redefining the EEH RTAS calls (set-eeh-option, reset-slot, etc) to operate
on a per-PE basis so all devices behind an EEH bride would share the same
EEH state. The "config_addr" argument to the EEH RTAS calls became the
"PE_config_addr" and the OS was required to use the
ibm,get-config-addr-info RTAS call to find the correct PE address for the
device. When support for the new interfaces was added to Linux it was
implemented using something like:
At probe time:
pdn->eeh_config_addr = rtas_config_addr(pdn);
pdn->eeh_pe_config_addr = rtas_get_config_addr_info(pdn);
When performing an RTAS call:
config_addr = pdn->eeh_config_addr;
if (pdn->eeh_pe_config_addr)
config_addr = pdn->eeh_pe_config_addr;
rtas_call(..., config_addr, ...);
In other words, if the ibm,get-config-addr-info RTAS call is implemented
and returned a valid result we'd use that as the argument to the EEH
RTAS calls. If not, Linux would fall back to using the device's
config_addr. Over time these addresses have moved around going from pci_dn
to eeh_dev and finally into eeh_pe. Today the users look like this:
config_addr = pe->config_addr;
if (pe->addr)
config_addr = pe->addr;
rtas_call(..., config_addr, ...);
However, considering the EEH core always operates on a per-PE basis and
even on pseries the only per-device operation is the initial call to
ibm,set-eeh-option I'm not sure if any of this actually works on an RPA
system today. It doesn't make much sense to have the fallback address in
a generic structure either since the bulk of the code which reference it
is in pseries anyway.
The EEH core makes a token effort to support looking up a PE using the
config_addr by having two arguments to eeh_pe_get(). However, a survey of
all the callers to eeh_pe_get() shows that all bar one have the config_addr
argument hard-coded to zero.The only caller that doesn't is in
eeh_pe_tree_insert() which has:
if (!eeh_has_flag(EEH_VALID_PE_ZERO) && !edev->pe_config_addr)
return -EINVAL;
pe = eeh_pe_get(hose, edev->pe_config_addr, edev->bdfn);
The third argument (config_addr) is only used if the second (pe->addr)
argument is invalid. The preceding check ensures that the call to
eeh_pe_get() will never happen if edev->pe_config_addr is invalid so there
is no situation where eeh_pe_get() will search for a PE based on the 3rd
argument. The check also means that we'll never insert a PE into the tree
where pe_config_addr is zero since EEH_VALID_PE_ZERO is never set on
pseries. All the users of the fallback address on pseries never actually
use the fallback and all the only caller that supplies something for the
config_addr argument to eeh_pe_get() never use it either. It's all dead
code.
This patch removes the fallback address from eeh_pe since nothing uses it.
Specificly, we do this by:
1) Removing pe->config_addr
2) Removing the EEH_VALID_PE_ZERO flag
3) Removing the fallback address argument to eeh_pe_get().
4) Removing all the checks for pe->addr being zero in the pseries EEH code.
This leaves us with PE's only being identified by what's in their pe->addr
field and the EEH core relying on the platform to ensure that eeh_dev's are
only inserted into the EEH tree if they're actually inside a PE.
No functional changes, I hope.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20200918093050.37344-9-oohall@gmail.com
2020-09-18 17:30:50 +08:00
|
|
|
if (*target_pe == pe->addr)
|
2012-09-08 06:44:08 +08:00
|
|
|
return pe;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* eeh_pe_get - Search PE based on the given address
|
2017-08-29 15:34:00 +08:00
|
|
|
* @phb: PCI controller
|
|
|
|
* @pe_no: PE number
|
2012-09-08 06:44:08 +08:00
|
|
|
*
|
|
|
|
* Search the corresponding PE based on the specified address which
|
|
|
|
* is included in the eeh device. The function is used to check if
|
|
|
|
* the associated PE has been created against the PE address. It's
|
|
|
|
* notable that the PE address has 2 format: traditional PE address
|
|
|
|
* which is composed of PCI bus/device/function number, or unified
|
|
|
|
* PE address.
|
|
|
|
*/
|
powerpc/eeh: Clean up PE addressing
When support for EEH on PowerNV was added a lot of pseries specific code
was made "generic" and some of the quirks of pseries EEH came along for the
ride. One of the stranger quirks is eeh_pe containing two types of PE
address: pe->addr and pe->config_addr. There reason for this appears to be
historical baggage rather than any real requirements.
On pseries EEH PEs are manipulated using RTAS calls. Each EEH RTAS call
takes a "PE configuration address" as an input which is used to identify
which EEH PE is being manipulated by the call. When initialising the EEH
state for a device the first thing we need to do is determine the
configuration address for the PE which contains the device so we can enable
EEH on that PE. This process is outlined in PAPR which is the modern
(i.e post-2003) FW specification for pseries. However, EEH support was
first described in the pSeries RISC Platform Architecture (RPA) and
although they are mostly compatible EEH is one of the areas where they are
not.
The major difference is that RPA doesn't actually have the concept of a PE.
On RPA systems the EEH RTAS calls are done on a per-device basis using the
same config_addr that would be passed to the RTAS functions to access PCI
config space (e.g. ibm,read-pci-config). The config_addr is not identical
since the function and config register offsets of the config_addr must be
set to zero. EEH operations being done on a per-device basis doesn't make a
whole lot of sense when you consider how EEH was implemented on legacy PCI
systems.
For legacy PCI(-X) systems EEH was implemented using special PCI-PCI
bridges which contained logic to detect errors and freeze the secondary
bus when one occurred. This means that the EEH enabled state is shared
among all devices behind that EEH bridge. As a result there's no way to
implement the per-device control required for the semantics specified by
RPA. It can be made to work if we assume that a separate EEH bridge exists
for each EEH capable PCI slot and there are no bridges behind those slots.
However, RPA also specifies the ibm,configure-bridge RTAS call for
re-initalising bridges behind EEH capable slots after they are reset due
to an EEH event so that is probably not a valid assumption. This
incoherence was fixed in later PAPR, which succeeded RPA. Unfortunately,
since Linux EEH support seems to have been implemented based on the RPA
spec some of the legacy assumptions were carried over (probably for POWER4
compatibility).
The fix made in PAPR was the introduction of the "PE" concept and
redefining the EEH RTAS calls (set-eeh-option, reset-slot, etc) to operate
on a per-PE basis so all devices behind an EEH bride would share the same
EEH state. The "config_addr" argument to the EEH RTAS calls became the
"PE_config_addr" and the OS was required to use the
ibm,get-config-addr-info RTAS call to find the correct PE address for the
device. When support for the new interfaces was added to Linux it was
implemented using something like:
At probe time:
pdn->eeh_config_addr = rtas_config_addr(pdn);
pdn->eeh_pe_config_addr = rtas_get_config_addr_info(pdn);
When performing an RTAS call:
config_addr = pdn->eeh_config_addr;
if (pdn->eeh_pe_config_addr)
config_addr = pdn->eeh_pe_config_addr;
rtas_call(..., config_addr, ...);
In other words, if the ibm,get-config-addr-info RTAS call is implemented
and returned a valid result we'd use that as the argument to the EEH
RTAS calls. If not, Linux would fall back to using the device's
config_addr. Over time these addresses have moved around going from pci_dn
to eeh_dev and finally into eeh_pe. Today the users look like this:
config_addr = pe->config_addr;
if (pe->addr)
config_addr = pe->addr;
rtas_call(..., config_addr, ...);
However, considering the EEH core always operates on a per-PE basis and
even on pseries the only per-device operation is the initial call to
ibm,set-eeh-option I'm not sure if any of this actually works on an RPA
system today. It doesn't make much sense to have the fallback address in
a generic structure either since the bulk of the code which reference it
is in pseries anyway.
The EEH core makes a token effort to support looking up a PE using the
config_addr by having two arguments to eeh_pe_get(). However, a survey of
all the callers to eeh_pe_get() shows that all bar one have the config_addr
argument hard-coded to zero.The only caller that doesn't is in
eeh_pe_tree_insert() which has:
if (!eeh_has_flag(EEH_VALID_PE_ZERO) && !edev->pe_config_addr)
return -EINVAL;
pe = eeh_pe_get(hose, edev->pe_config_addr, edev->bdfn);
The third argument (config_addr) is only used if the second (pe->addr)
argument is invalid. The preceding check ensures that the call to
eeh_pe_get() will never happen if edev->pe_config_addr is invalid so there
is no situation where eeh_pe_get() will search for a PE based on the 3rd
argument. The check also means that we'll never insert a PE into the tree
where pe_config_addr is zero since EEH_VALID_PE_ZERO is never set on
pseries. All the users of the fallback address on pseries never actually
use the fallback and all the only caller that supplies something for the
config_addr argument to eeh_pe_get() never use it either. It's all dead
code.
This patch removes the fallback address from eeh_pe since nothing uses it.
Specificly, we do this by:
1) Removing pe->config_addr
2) Removing the EEH_VALID_PE_ZERO flag
3) Removing the fallback address argument to eeh_pe_get().
4) Removing all the checks for pe->addr being zero in the pseries EEH code.
This leaves us with PE's only being identified by what's in their pe->addr
field and the EEH core relying on the platform to ensure that eeh_dev's are
only inserted into the EEH tree if they're actually inside a PE.
No functional changes, I hope.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20200918093050.37344-9-oohall@gmail.com
2020-09-18 17:30:50 +08:00
|
|
|
struct eeh_pe *eeh_pe_get(struct pci_controller *phb, int pe_no)
|
2012-09-08 06:44:08 +08:00
|
|
|
{
|
2017-08-29 15:34:00 +08:00
|
|
|
struct eeh_pe *root = eeh_phb_pe_get(phb);
|
2012-09-08 06:44:08 +08:00
|
|
|
|
powerpc/eeh: Clean up PE addressing
When support for EEH on PowerNV was added a lot of pseries specific code
was made "generic" and some of the quirks of pseries EEH came along for the
ride. One of the stranger quirks is eeh_pe containing two types of PE
address: pe->addr and pe->config_addr. There reason for this appears to be
historical baggage rather than any real requirements.
On pseries EEH PEs are manipulated using RTAS calls. Each EEH RTAS call
takes a "PE configuration address" as an input which is used to identify
which EEH PE is being manipulated by the call. When initialising the EEH
state for a device the first thing we need to do is determine the
configuration address for the PE which contains the device so we can enable
EEH on that PE. This process is outlined in PAPR which is the modern
(i.e post-2003) FW specification for pseries. However, EEH support was
first described in the pSeries RISC Platform Architecture (RPA) and
although they are mostly compatible EEH is one of the areas where they are
not.
The major difference is that RPA doesn't actually have the concept of a PE.
On RPA systems the EEH RTAS calls are done on a per-device basis using the
same config_addr that would be passed to the RTAS functions to access PCI
config space (e.g. ibm,read-pci-config). The config_addr is not identical
since the function and config register offsets of the config_addr must be
set to zero. EEH operations being done on a per-device basis doesn't make a
whole lot of sense when you consider how EEH was implemented on legacy PCI
systems.
For legacy PCI(-X) systems EEH was implemented using special PCI-PCI
bridges which contained logic to detect errors and freeze the secondary
bus when one occurred. This means that the EEH enabled state is shared
among all devices behind that EEH bridge. As a result there's no way to
implement the per-device control required for the semantics specified by
RPA. It can be made to work if we assume that a separate EEH bridge exists
for each EEH capable PCI slot and there are no bridges behind those slots.
However, RPA also specifies the ibm,configure-bridge RTAS call for
re-initalising bridges behind EEH capable slots after they are reset due
to an EEH event so that is probably not a valid assumption. This
incoherence was fixed in later PAPR, which succeeded RPA. Unfortunately,
since Linux EEH support seems to have been implemented based on the RPA
spec some of the legacy assumptions were carried over (probably for POWER4
compatibility).
The fix made in PAPR was the introduction of the "PE" concept and
redefining the EEH RTAS calls (set-eeh-option, reset-slot, etc) to operate
on a per-PE basis so all devices behind an EEH bride would share the same
EEH state. The "config_addr" argument to the EEH RTAS calls became the
"PE_config_addr" and the OS was required to use the
ibm,get-config-addr-info RTAS call to find the correct PE address for the
device. When support for the new interfaces was added to Linux it was
implemented using something like:
At probe time:
pdn->eeh_config_addr = rtas_config_addr(pdn);
pdn->eeh_pe_config_addr = rtas_get_config_addr_info(pdn);
When performing an RTAS call:
config_addr = pdn->eeh_config_addr;
if (pdn->eeh_pe_config_addr)
config_addr = pdn->eeh_pe_config_addr;
rtas_call(..., config_addr, ...);
In other words, if the ibm,get-config-addr-info RTAS call is implemented
and returned a valid result we'd use that as the argument to the EEH
RTAS calls. If not, Linux would fall back to using the device's
config_addr. Over time these addresses have moved around going from pci_dn
to eeh_dev and finally into eeh_pe. Today the users look like this:
config_addr = pe->config_addr;
if (pe->addr)
config_addr = pe->addr;
rtas_call(..., config_addr, ...);
However, considering the EEH core always operates on a per-PE basis and
even on pseries the only per-device operation is the initial call to
ibm,set-eeh-option I'm not sure if any of this actually works on an RPA
system today. It doesn't make much sense to have the fallback address in
a generic structure either since the bulk of the code which reference it
is in pseries anyway.
The EEH core makes a token effort to support looking up a PE using the
config_addr by having two arguments to eeh_pe_get(). However, a survey of
all the callers to eeh_pe_get() shows that all bar one have the config_addr
argument hard-coded to zero.The only caller that doesn't is in
eeh_pe_tree_insert() which has:
if (!eeh_has_flag(EEH_VALID_PE_ZERO) && !edev->pe_config_addr)
return -EINVAL;
pe = eeh_pe_get(hose, edev->pe_config_addr, edev->bdfn);
The third argument (config_addr) is only used if the second (pe->addr)
argument is invalid. The preceding check ensures that the call to
eeh_pe_get() will never happen if edev->pe_config_addr is invalid so there
is no situation where eeh_pe_get() will search for a PE based on the 3rd
argument. The check also means that we'll never insert a PE into the tree
where pe_config_addr is zero since EEH_VALID_PE_ZERO is never set on
pseries. All the users of the fallback address on pseries never actually
use the fallback and all the only caller that supplies something for the
config_addr argument to eeh_pe_get() never use it either. It's all dead
code.
This patch removes the fallback address from eeh_pe since nothing uses it.
Specificly, we do this by:
1) Removing pe->config_addr
2) Removing the EEH_VALID_PE_ZERO flag
3) Removing the fallback address argument to eeh_pe_get().
4) Removing all the checks for pe->addr being zero in the pseries EEH code.
This leaves us with PE's only being identified by what's in their pe->addr
field and the EEH core relying on the platform to ensure that eeh_dev's are
only inserted into the EEH tree if they're actually inside a PE.
No functional changes, I hope.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20200918093050.37344-9-oohall@gmail.com
2020-09-18 17:30:50 +08:00
|
|
|
return eeh_pe_traverse(root, __eeh_pe_get, &pe_no);
|
2012-09-08 06:44:08 +08:00
|
|
|
}
|
|
|
|
|
2012-09-08 06:44:09 +08:00
|
|
|
/**
|
2020-07-25 16:12:29 +08:00
|
|
|
* eeh_pe_tree_insert - Add EEH device to parent PE
|
2012-09-08 06:44:09 +08:00
|
|
|
* @edev: EEH device
|
2020-07-25 16:12:31 +08:00
|
|
|
* @new_pe_parent: PE to create additional PEs under
|
2012-09-08 06:44:09 +08:00
|
|
|
*
|
2020-07-25 16:12:31 +08:00
|
|
|
* Add EEH device to the PE in edev->pe_config_addr. If a PE already
|
|
|
|
* exists with that address then @edev is added to that PE. Otherwise
|
|
|
|
* a new PE is created and inserted into the PE tree as a child of
|
|
|
|
* @new_pe_parent.
|
|
|
|
*
|
|
|
|
* If @new_pe_parent is NULL then the new PE will be inserted under
|
2022-05-18 22:26:29 +08:00
|
|
|
* directly under the PHB.
|
2012-09-08 06:44:09 +08:00
|
|
|
*/
|
2020-07-25 16:12:31 +08:00
|
|
|
int eeh_pe_tree_insert(struct eeh_dev *edev, struct eeh_pe *new_pe_parent)
|
2012-09-08 06:44:09 +08:00
|
|
|
{
|
2020-07-25 16:12:30 +08:00
|
|
|
struct pci_controller *hose = edev->controller;
|
2012-09-08 06:44:09 +08:00
|
|
|
struct eeh_pe *pe, *parent;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search the PE has been existing or not according
|
|
|
|
* to the PE address. If that has been existing, the
|
|
|
|
* PE should be composed of PCI bus and its subordinate
|
|
|
|
* components.
|
|
|
|
*/
|
powerpc/eeh: Clean up PE addressing
When support for EEH on PowerNV was added a lot of pseries specific code
was made "generic" and some of the quirks of pseries EEH came along for the
ride. One of the stranger quirks is eeh_pe containing two types of PE
address: pe->addr and pe->config_addr. There reason for this appears to be
historical baggage rather than any real requirements.
On pseries EEH PEs are manipulated using RTAS calls. Each EEH RTAS call
takes a "PE configuration address" as an input which is used to identify
which EEH PE is being manipulated by the call. When initialising the EEH
state for a device the first thing we need to do is determine the
configuration address for the PE which contains the device so we can enable
EEH on that PE. This process is outlined in PAPR which is the modern
(i.e post-2003) FW specification for pseries. However, EEH support was
first described in the pSeries RISC Platform Architecture (RPA) and
although they are mostly compatible EEH is one of the areas where they are
not.
The major difference is that RPA doesn't actually have the concept of a PE.
On RPA systems the EEH RTAS calls are done on a per-device basis using the
same config_addr that would be passed to the RTAS functions to access PCI
config space (e.g. ibm,read-pci-config). The config_addr is not identical
since the function and config register offsets of the config_addr must be
set to zero. EEH operations being done on a per-device basis doesn't make a
whole lot of sense when you consider how EEH was implemented on legacy PCI
systems.
For legacy PCI(-X) systems EEH was implemented using special PCI-PCI
bridges which contained logic to detect errors and freeze the secondary
bus when one occurred. This means that the EEH enabled state is shared
among all devices behind that EEH bridge. As a result there's no way to
implement the per-device control required for the semantics specified by
RPA. It can be made to work if we assume that a separate EEH bridge exists
for each EEH capable PCI slot and there are no bridges behind those slots.
However, RPA also specifies the ibm,configure-bridge RTAS call for
re-initalising bridges behind EEH capable slots after they are reset due
to an EEH event so that is probably not a valid assumption. This
incoherence was fixed in later PAPR, which succeeded RPA. Unfortunately,
since Linux EEH support seems to have been implemented based on the RPA
spec some of the legacy assumptions were carried over (probably for POWER4
compatibility).
The fix made in PAPR was the introduction of the "PE" concept and
redefining the EEH RTAS calls (set-eeh-option, reset-slot, etc) to operate
on a per-PE basis so all devices behind an EEH bride would share the same
EEH state. The "config_addr" argument to the EEH RTAS calls became the
"PE_config_addr" and the OS was required to use the
ibm,get-config-addr-info RTAS call to find the correct PE address for the
device. When support for the new interfaces was added to Linux it was
implemented using something like:
At probe time:
pdn->eeh_config_addr = rtas_config_addr(pdn);
pdn->eeh_pe_config_addr = rtas_get_config_addr_info(pdn);
When performing an RTAS call:
config_addr = pdn->eeh_config_addr;
if (pdn->eeh_pe_config_addr)
config_addr = pdn->eeh_pe_config_addr;
rtas_call(..., config_addr, ...);
In other words, if the ibm,get-config-addr-info RTAS call is implemented
and returned a valid result we'd use that as the argument to the EEH
RTAS calls. If not, Linux would fall back to using the device's
config_addr. Over time these addresses have moved around going from pci_dn
to eeh_dev and finally into eeh_pe. Today the users look like this:
config_addr = pe->config_addr;
if (pe->addr)
config_addr = pe->addr;
rtas_call(..., config_addr, ...);
However, considering the EEH core always operates on a per-PE basis and
even on pseries the only per-device operation is the initial call to
ibm,set-eeh-option I'm not sure if any of this actually works on an RPA
system today. It doesn't make much sense to have the fallback address in
a generic structure either since the bulk of the code which reference it
is in pseries anyway.
The EEH core makes a token effort to support looking up a PE using the
config_addr by having two arguments to eeh_pe_get(). However, a survey of
all the callers to eeh_pe_get() shows that all bar one have the config_addr
argument hard-coded to zero.The only caller that doesn't is in
eeh_pe_tree_insert() which has:
if (!eeh_has_flag(EEH_VALID_PE_ZERO) && !edev->pe_config_addr)
return -EINVAL;
pe = eeh_pe_get(hose, edev->pe_config_addr, edev->bdfn);
The third argument (config_addr) is only used if the second (pe->addr)
argument is invalid. The preceding check ensures that the call to
eeh_pe_get() will never happen if edev->pe_config_addr is invalid so there
is no situation where eeh_pe_get() will search for a PE based on the 3rd
argument. The check also means that we'll never insert a PE into the tree
where pe_config_addr is zero since EEH_VALID_PE_ZERO is never set on
pseries. All the users of the fallback address on pseries never actually
use the fallback and all the only caller that supplies something for the
config_addr argument to eeh_pe_get() never use it either. It's all dead
code.
This patch removes the fallback address from eeh_pe since nothing uses it.
Specificly, we do this by:
1) Removing pe->config_addr
2) Removing the EEH_VALID_PE_ZERO flag
3) Removing the fallback address argument to eeh_pe_get().
4) Removing all the checks for pe->addr being zero in the pseries EEH code.
This leaves us with PE's only being identified by what's in their pe->addr
field and the EEH core relying on the platform to ensure that eeh_dev's are
only inserted into the EEH tree if they're actually inside a PE.
No functional changes, I hope.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20200918093050.37344-9-oohall@gmail.com
2020-09-18 17:30:50 +08:00
|
|
|
pe = eeh_pe_get(hose, edev->pe_config_addr);
|
2019-08-16 12:48:16 +08:00
|
|
|
if (pe) {
|
|
|
|
if (pe->type & EEH_PE_INVALID) {
|
|
|
|
list_add_tail(&edev->entry, &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;
|
|
|
|
}
|
|
|
|
|
2020-07-25 16:12:31 +08:00
|
|
|
eeh_edev_dbg(edev, "Added to existing PE (parent: PE#%x)\n",
|
2019-08-16 12:48:16 +08:00
|
|
|
pe->parent->addr);
|
|
|
|
} else {
|
|
|
|
/* Mark the PE as type of PCI bus */
|
|
|
|
pe->type = EEH_PE_BUS;
|
|
|
|
edev->pe = pe;
|
|
|
|
|
|
|
|
/* Put the edev to PE */
|
|
|
|
list_add_tail(&edev->entry, &pe->edevs);
|
|
|
|
eeh_edev_dbg(edev, "Added to bus PE\n");
|
2012-09-12 03:16:16 +08:00
|
|
|
}
|
2012-09-08 06:44:09 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a new EEH PE */
|
2016-03-04 07:53:08 +08:00
|
|
|
if (edev->physfn)
|
2020-07-25 16:12:30 +08:00
|
|
|
pe = eeh_pe_alloc(hose, EEH_PE_VF);
|
2016-03-04 07:53:08 +08:00
|
|
|
else
|
2020-07-25 16:12:30 +08:00
|
|
|
pe = eeh_pe_alloc(hose, EEH_PE_DEVICE);
|
2012-09-08 06:44:09 +08:00
|
|
|
if (!pe) {
|
|
|
|
pr_err("%s: out of memory!\n", __func__);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2020-10-07 12:09:02 +08:00
|
|
|
|
|
|
|
pe->addr = edev->pe_config_addr;
|
2012-09-08 06:44:09 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Put the new EEH PE into hierarchy tree. If the parent
|
|
|
|
* can't be found, the newly created PE will be attached
|
|
|
|
* to PHB directly. Otherwise, we have to associate the
|
|
|
|
* PE with its parent.
|
|
|
|
*/
|
2020-07-25 16:12:31 +08:00
|
|
|
if (!new_pe_parent) {
|
|
|
|
new_pe_parent = eeh_phb_pe_get(hose);
|
|
|
|
if (!new_pe_parent) {
|
2012-09-08 06:44:09 +08:00
|
|
|
pr_err("%s: No PHB PE is found (PHB Domain=%d)\n",
|
2020-07-25 16:12:30 +08:00
|
|
|
__func__, hose->global_number);
|
2012-09-08 06:44:09 +08:00
|
|
|
edev->pe = NULL;
|
|
|
|
kfree(pe);
|
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
}
|
2020-07-25 16:12:31 +08:00
|
|
|
|
|
|
|
/* link new PE into the tree */
|
|
|
|
pe->parent = new_pe_parent;
|
|
|
|
list_add_tail(&pe->child, &new_pe_parent->child_list);
|
2012-09-08 06:44:09 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Put the newly created PE into the child list and
|
|
|
|
* link the EEH device accordingly.
|
|
|
|
*/
|
2018-09-12 09:23:26 +08:00
|
|
|
list_add_tail(&edev->entry, &pe->edevs);
|
2012-09-08 06:44:09 +08:00
|
|
|
edev->pe = pe;
|
2020-07-25 16:12:31 +08:00
|
|
|
eeh_edev_dbg(edev, "Added to new (parent: PE#%x)\n",
|
|
|
|
new_pe_parent->addr);
|
2012-09-08 06:44:09 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2012-09-08 06:44:10 +08:00
|
|
|
|
|
|
|
/**
|
2020-07-25 16:12:29 +08:00
|
|
|
* eeh_pe_tree_remove - Remove one EEH device from the associated PE
|
2012-09-08 06:44:10 +08:00
|
|
|
* @edev: EEH device
|
|
|
|
*
|
|
|
|
* The PE hierarchy tree might be changed when doing PCI hotplug.
|
|
|
|
* Also, the PCI devices or buses could be removed from the system
|
|
|
|
* during EEH recovery. So we have to call the function remove the
|
|
|
|
* corresponding PE accordingly if necessary.
|
|
|
|
*/
|
2020-07-25 16:12:29 +08:00
|
|
|
int eeh_pe_tree_remove(struct eeh_dev *edev)
|
2012-09-08 06:44:10 +08:00
|
|
|
{
|
2012-09-12 03:16:16 +08:00
|
|
|
struct eeh_pe *pe, *parent, *child;
|
2019-09-03 18:15:52 +08:00
|
|
|
bool keep, recover;
|
2012-09-12 03:16:16 +08:00
|
|
|
int cnt;
|
2012-09-08 06:44:10 +08:00
|
|
|
|
2018-09-12 09:23:28 +08:00
|
|
|
pe = eeh_dev_to_pe(edev);
|
|
|
|
if (!pe) {
|
2019-08-16 12:48:13 +08:00
|
|
|
eeh_edev_dbg(edev, "No PE found for device.\n");
|
2012-09-08 06:44:10 +08:00
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove the EEH device */
|
|
|
|
edev->pe = NULL;
|
2018-09-12 09:23:26 +08:00
|
|
|
list_del(&edev->entry);
|
2012-09-08 06:44:10 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if the parent PE includes any EEH devices.
|
|
|
|
* If not, we should delete that. Also, we should
|
|
|
|
* delete the parent PE if it doesn't have associated
|
|
|
|
* child PEs and EEH devices.
|
|
|
|
*/
|
|
|
|
while (1) {
|
|
|
|
parent = pe->parent;
|
2019-09-03 18:15:52 +08:00
|
|
|
|
|
|
|
/* PHB PEs should never be removed */
|
2012-09-12 03:16:16 +08:00
|
|
|
if (pe->type & EEH_PE_PHB)
|
2012-09-08 06:44:10 +08:00
|
|
|
break;
|
|
|
|
|
2019-09-03 18:15:52 +08:00
|
|
|
/*
|
|
|
|
* XXX: KEEP is set while resetting a PE. I don't think it's
|
|
|
|
* ever set without RECOVERING also being set. I could
|
|
|
|
* be wrong though so catch that with a WARN.
|
|
|
|
*/
|
|
|
|
keep = !!(pe->state & EEH_PE_KEEP);
|
|
|
|
recover = !!(pe->state & EEH_PE_RECOVERING);
|
|
|
|
WARN_ON(keep && !recover);
|
|
|
|
|
|
|
|
if (!keep && !recover) {
|
2012-09-12 03:16:17 +08:00
|
|
|
if (list_empty(&pe->edevs) &&
|
|
|
|
list_empty(&pe->child_list)) {
|
|
|
|
list_del(&pe->child);
|
|
|
|
kfree(pe);
|
|
|
|
} else {
|
|
|
|
break;
|
2012-09-12 03:16:16 +08:00
|
|
|
}
|
2012-09-12 03:16:17 +08:00
|
|
|
} else {
|
2019-09-03 18:15:52 +08:00
|
|
|
/*
|
|
|
|
* Mark the PE as invalid. At the end of the recovery
|
|
|
|
* process any invalid PEs will be garbage collected.
|
|
|
|
*
|
|
|
|
* We need to delay the free()ing of them since we can
|
|
|
|
* remove edev's while traversing the PE tree which
|
|
|
|
* might trigger the removal of a PE and we can't
|
|
|
|
* deal with that (yet).
|
|
|
|
*/
|
2012-09-12 03:16:17 +08:00
|
|
|
if (list_empty(&pe->edevs)) {
|
|
|
|
cnt = 0;
|
|
|
|
list_for_each_entry(child, &pe->child_list, child) {
|
2012-11-23 05:58:26 +08:00
|
|
|
if (!(child->type & EEH_PE_INVALID)) {
|
2012-09-12 03:16:17 +08:00
|
|
|
cnt++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-09-12 03:16:16 +08:00
|
|
|
|
2012-09-12 03:16:17 +08:00
|
|
|
if (!cnt)
|
|
|
|
pe->type |= EEH_PE_INVALID;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
2012-09-08 06:44:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pe = parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2012-09-08 06:44:12 +08:00
|
|
|
|
2013-06-20 13:21:01 +08:00
|
|
|
/**
|
|
|
|
* eeh_pe_update_time_stamp - Update PE's frozen time stamp
|
|
|
|
* @pe: EEH PE
|
|
|
|
*
|
|
|
|
* We have time stamp for each PE to trace its time of getting
|
|
|
|
* frozen in last hour. The function should be called to update
|
|
|
|
* the time stamp on first error of the specific PE. On the other
|
|
|
|
* handle, we needn't account for errors happened in last hour.
|
|
|
|
*/
|
|
|
|
void eeh_pe_update_time_stamp(struct eeh_pe *pe)
|
|
|
|
{
|
2017-11-05 05:26:52 +08:00
|
|
|
time64_t tstamp;
|
2013-06-20 13:21:01 +08:00
|
|
|
|
|
|
|
if (!pe) return;
|
|
|
|
|
|
|
|
if (pe->freeze_count <= 0) {
|
|
|
|
pe->freeze_count = 0;
|
2017-11-05 05:26:52 +08:00
|
|
|
pe->tstamp = ktime_get_seconds();
|
2013-06-20 13:21:01 +08:00
|
|
|
} else {
|
2017-11-05 05:26:52 +08:00
|
|
|
tstamp = ktime_get_seconds();
|
|
|
|
if (tstamp - pe->tstamp > 3600) {
|
2013-06-20 13:21:01 +08:00
|
|
|
pe->tstamp = tstamp;
|
|
|
|
pe->freeze_count = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-08 06:44:12 +08:00
|
|
|
/**
|
2018-09-12 09:23:31 +08:00
|
|
|
* eeh_pe_state_mark - Mark specified state for PE and its associated device
|
|
|
|
* @pe: EEH PE
|
2012-09-08 06:44:12 +08:00
|
|
|
*
|
2018-09-12 09:23:31 +08:00
|
|
|
* EEH error affects the current PE and its child PEs. The function
|
|
|
|
* is used to mark appropriate state for the affected PEs and the
|
|
|
|
* associated devices.
|
2012-09-08 06:44:12 +08:00
|
|
|
*/
|
2018-09-12 09:23:31 +08:00
|
|
|
void eeh_pe_state_mark(struct eeh_pe *root, int state)
|
2012-09-08 06:44:12 +08:00
|
|
|
{
|
2018-09-12 09:23:31 +08:00
|
|
|
struct eeh_pe *pe;
|
2014-10-01 15:07:53 +08:00
|
|
|
|
2018-09-12 09:23:31 +08:00
|
|
|
eeh_for_each_pe(root, pe)
|
|
|
|
if (!(pe->state & EEH_PE_REMOVED))
|
|
|
|
pe->state |= state;
|
2012-09-08 06:44:12 +08:00
|
|
|
}
|
2018-09-12 09:23:31 +08:00
|
|
|
EXPORT_SYMBOL_GPL(eeh_pe_state_mark);
|
2012-09-08 06:44:12 +08:00
|
|
|
|
|
|
|
/**
|
2018-09-12 09:23:31 +08:00
|
|
|
* eeh_pe_mark_isolated
|
2012-09-08 06:44:12 +08:00
|
|
|
* @pe: EEH PE
|
|
|
|
*
|
2024-01-04 07:16:04 +08:00
|
|
|
* Record that a PE has been isolated by marking the PE and its children as
|
2018-09-12 09:23:31 +08:00
|
|
|
* EEH_PE_ISOLATED (and EEH_PE_CFG_BLOCKED, if required) and their PCI devices
|
|
|
|
* as pci_channel_io_frozen.
|
2012-09-08 06:44:12 +08:00
|
|
|
*/
|
2018-09-12 09:23:31 +08:00
|
|
|
void eeh_pe_mark_isolated(struct eeh_pe *root)
|
2012-09-08 06:44:12 +08:00
|
|
|
{
|
2018-09-12 09:23:31 +08:00
|
|
|
struct eeh_pe *pe;
|
|
|
|
struct eeh_dev *edev;
|
|
|
|
struct pci_dev *pdev;
|
|
|
|
|
|
|
|
eeh_pe_state_mark(root, EEH_PE_ISOLATED);
|
|
|
|
eeh_for_each_pe(root, pe) {
|
|
|
|
list_for_each_entry(edev, &pe->edevs, entry) {
|
|
|
|
pdev = eeh_dev_to_pci_dev(edev);
|
|
|
|
if (pdev)
|
|
|
|
pdev->error_state = pci_channel_io_frozen;
|
|
|
|
}
|
|
|
|
/* Block PCI config access if required */
|
|
|
|
if (pe->state & EEH_PE_CFG_RESTRICTED)
|
|
|
|
pe->state |= EEH_PE_CFG_BLOCKED;
|
|
|
|
}
|
2012-09-08 06:44:12 +08:00
|
|
|
}
|
2018-09-12 09:23:31 +08:00
|
|
|
EXPORT_SYMBOL_GPL(eeh_pe_mark_isolated);
|
2012-09-08 06:44:12 +08:00
|
|
|
|
2019-08-16 12:48:15 +08:00
|
|
|
static void __eeh_pe_dev_mode_mark(struct eeh_dev *edev, void *flag)
|
powerpc/eeh: No hotplug on permanently removed dev
The issue was detected in a bit complicated test case where
we have multiple hierarchical PEs shown as following figure:
+-----------------+
| PE#3 p2p#0 |
| p2p#1 |
+-----------------+
|
+-----------------+
| PE#4 pdev#0 |
| pdev#1 |
+-----------------+
PE#4 (have 2 PCI devices) is the child of PE#3, which has 2 p2p
bridges. We accidentally had less-known scenario: PE#4 was removed
permanently from the system because of permanent failure (e.g.
exceeding the max allowd failure times in last hour), then we detects
EEH errors on PE#3 and tried to recover it. However, eeh_dev instances
for pdev#0/1 were not detached from PE#4, which was still connected to
PE#3. All of that was because of the fact that we rely on count-based
pcibios_release_device(), which isn't reliable enough. When doing
recovery for PE#3, we still apply hotplug on PE#4 and pdev#0/1, which
are not valid any more. Eventually, we run into kernel crash.
The patch fixes above issue from two aspects. For unplug, we simply
skip those permanently removed PE, whose state is (EEH_PE_STATE_ISOLATED
&& !EEH_PE_STATE_RECOVERING) and its frozen count should be greater
than EEH_MAX_ALLOWED_FREEZES. For plug, we marked all permanently
removed EEH devices with EEH_DEV_REMOVED and return 0xFF's on read
its PCI config so that PCI core will omit them.
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2014-04-24 16:00:19 +08:00
|
|
|
{
|
|
|
|
int mode = *((int *)flag);
|
|
|
|
|
|
|
|
edev->mode |= mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* eeh_pe_dev_state_mark - Mark state for all device under the PE
|
|
|
|
* @pe: EEH PE
|
|
|
|
*
|
|
|
|
* Mark specific state for all child devices of the PE.
|
|
|
|
*/
|
|
|
|
void eeh_pe_dev_mode_mark(struct eeh_pe *pe, int mode)
|
|
|
|
{
|
|
|
|
eeh_pe_dev_traverse(pe, __eeh_pe_dev_mode_mark, &mode);
|
|
|
|
}
|
|
|
|
|
2012-09-08 06:44:12 +08:00
|
|
|
/**
|
2018-11-29 11:16:39 +08:00
|
|
|
* eeh_pe_state_clear - Clear state for the PE
|
2012-09-08 06:44:12 +08:00
|
|
|
* @data: EEH PE
|
2018-11-29 11:16:39 +08:00
|
|
|
* @state: state
|
|
|
|
* @include_passed: include passed-through devices?
|
2012-09-08 06:44:12 +08:00
|
|
|
*
|
|
|
|
* The function is used to clear the indicated state from the
|
|
|
|
* given PE. Besides, we also clear the check count of the PE
|
|
|
|
* as well.
|
|
|
|
*/
|
2018-11-29 11:16:39 +08:00
|
|
|
void eeh_pe_state_clear(struct eeh_pe *root, int state, bool include_passed)
|
2012-09-08 06:44:12 +08:00
|
|
|
{
|
2018-11-29 11:16:39 +08:00
|
|
|
struct eeh_pe *pe;
|
2014-09-30 10:38:59 +08:00
|
|
|
struct eeh_dev *edev, *tmp;
|
|
|
|
struct pci_dev *pdev;
|
2012-09-08 06:44:12 +08:00
|
|
|
|
2018-11-29 11:16:39 +08:00
|
|
|
eeh_for_each_pe(root, pe) {
|
|
|
|
/* Keep the state of permanently removed PE intact */
|
|
|
|
if (pe->state & EEH_PE_REMOVED)
|
|
|
|
continue;
|
powerpc/eeh: No hotplug on permanently removed dev
The issue was detected in a bit complicated test case where
we have multiple hierarchical PEs shown as following figure:
+-----------------+
| PE#3 p2p#0 |
| p2p#1 |
+-----------------+
|
+-----------------+
| PE#4 pdev#0 |
| pdev#1 |
+-----------------+
PE#4 (have 2 PCI devices) is the child of PE#3, which has 2 p2p
bridges. We accidentally had less-known scenario: PE#4 was removed
permanently from the system because of permanent failure (e.g.
exceeding the max allowd failure times in last hour), then we detects
EEH errors on PE#3 and tried to recover it. However, eeh_dev instances
for pdev#0/1 were not detached from PE#4, which was still connected to
PE#3. All of that was because of the fact that we rely on count-based
pcibios_release_device(), which isn't reliable enough. When doing
recovery for PE#3, we still apply hotplug on PE#4 and pdev#0/1, which
are not valid any more. Eventually, we run into kernel crash.
The patch fixes above issue from two aspects. For unplug, we simply
skip those permanently removed PE, whose state is (EEH_PE_STATE_ISOLATED
&& !EEH_PE_STATE_RECOVERING) and its frozen count should be greater
than EEH_MAX_ALLOWED_FREEZES. For plug, we marked all permanently
removed EEH devices with EEH_DEV_REMOVED and return 0xFF's on read
its PCI config so that PCI core will omit them.
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2014-04-24 16:00:19 +08:00
|
|
|
|
2018-11-29 11:16:39 +08:00
|
|
|
if (!include_passed && eeh_pe_passed(pe))
|
|
|
|
continue;
|
powerpc/eeh: No hotplug on permanently removed dev
The issue was detected in a bit complicated test case where
we have multiple hierarchical PEs shown as following figure:
+-----------------+
| PE#3 p2p#0 |
| p2p#1 |
+-----------------+
|
+-----------------+
| PE#4 pdev#0 |
| pdev#1 |
+-----------------+
PE#4 (have 2 PCI devices) is the child of PE#3, which has 2 p2p
bridges. We accidentally had less-known scenario: PE#4 was removed
permanently from the system because of permanent failure (e.g.
exceeding the max allowd failure times in last hour), then we detects
EEH errors on PE#3 and tried to recover it. However, eeh_dev instances
for pdev#0/1 were not detached from PE#4, which was still connected to
PE#3. All of that was because of the fact that we rely on count-based
pcibios_release_device(), which isn't reliable enough. When doing
recovery for PE#3, we still apply hotplug on PE#4 and pdev#0/1, which
are not valid any more. Eventually, we run into kernel crash.
The patch fixes above issue from two aspects. For unplug, we simply
skip those permanently removed PE, whose state is (EEH_PE_STATE_ISOLATED
&& !EEH_PE_STATE_RECOVERING) and its frozen count should be greater
than EEH_MAX_ALLOWED_FREEZES. For plug, we marked all permanently
removed EEH devices with EEH_DEV_REMOVED and return 0xFF's on read
its PCI config so that PCI core will omit them.
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2014-04-24 16:00:19 +08:00
|
|
|
|
2018-11-29 11:16:39 +08:00
|
|
|
pe->state &= ~state;
|
2014-09-30 10:38:59 +08:00
|
|
|
|
2018-11-29 11:16:39 +08:00
|
|
|
/*
|
|
|
|
* Special treatment on clearing isolated state. Clear
|
|
|
|
* check count since last isolation and put all affected
|
|
|
|
* devices to normal state.
|
|
|
|
*/
|
|
|
|
if (!(state & EEH_PE_ISOLATED))
|
2014-09-30 10:38:59 +08:00
|
|
|
continue;
|
|
|
|
|
2018-11-29 11:16:39 +08:00
|
|
|
pe->check_count = 0;
|
|
|
|
eeh_pe_for_each_dev(pe, edev, tmp) {
|
|
|
|
pdev = eeh_dev_to_pci_dev(edev);
|
|
|
|
if (!pdev)
|
|
|
|
continue;
|
2014-10-01 15:07:53 +08:00
|
|
|
|
2018-11-29 11:16:39 +08:00
|
|
|
pdev->error_state = pci_channel_io_normal;
|
|
|
|
}
|
2012-09-08 06:44:12 +08:00
|
|
|
|
2018-11-29 11:16:39 +08:00
|
|
|
/* Unblock PCI config access if required */
|
|
|
|
if (pe->state & EEH_PE_CFG_RESTRICTED)
|
|
|
|
pe->state &= ~EEH_PE_CFG_BLOCKED;
|
|
|
|
}
|
2015-07-30 07:26:51 +08:00
|
|
|
}
|
|
|
|
|
2013-06-27 13:46:43 +08:00
|
|
|
/*
|
|
|
|
* Some PCI bridges (e.g. PLX bridges) have primary/secondary
|
|
|
|
* buses assigned explicitly by firmware, and we probably have
|
|
|
|
* lost that after reset. So we have to delay the check until
|
|
|
|
* the PCI-CFG registers have been restored for the parent
|
|
|
|
* bridge.
|
2012-09-08 06:44:15 +08:00
|
|
|
*
|
2013-06-27 13:46:43 +08:00
|
|
|
* Don't use normal PCI-CFG accessors, which probably has been
|
|
|
|
* blocked on normal path during the stage. So we need utilize
|
|
|
|
* eeh operations, which is always permitted.
|
2012-09-08 06:44:15 +08:00
|
|
|
*/
|
2015-03-17 13:15:07 +08:00
|
|
|
static void eeh_bridge_check_link(struct eeh_dev *edev)
|
2013-06-27 13:46:43 +08:00
|
|
|
{
|
|
|
|
int cap;
|
|
|
|
uint32_t val;
|
|
|
|
int timeout = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We only check root port and downstream ports of
|
|
|
|
* PCIe switches
|
|
|
|
*/
|
2013-07-24 10:24:59 +08:00
|
|
|
if (!(edev->mode & (EEH_DEV_ROOT_PORT | EEH_DEV_DS_PORT)))
|
2013-06-27 13:46:43 +08:00
|
|
|
return;
|
|
|
|
|
2019-08-16 12:48:13 +08:00
|
|
|
eeh_edev_dbg(edev, "Checking PCIe link...\n");
|
2013-06-27 13:46:43 +08:00
|
|
|
|
|
|
|
/* Check slot status */
|
2013-07-24 10:24:59 +08:00
|
|
|
cap = edev->pcie_cap;
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->read_config(edev, cap + PCI_EXP_SLTSTA, 2, &val);
|
2013-06-27 13:46:43 +08:00
|
|
|
if (!(val & PCI_EXP_SLTSTA_PDS)) {
|
2019-08-16 12:48:13 +08:00
|
|
|
eeh_edev_dbg(edev, "No card in the slot (0x%04x) !\n", val);
|
2013-06-27 13:46:43 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check power status if we have the capability */
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->read_config(edev, cap + PCI_EXP_SLTCAP, 2, &val);
|
2013-06-27 13:46:43 +08:00
|
|
|
if (val & PCI_EXP_SLTCAP_PCP) {
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->read_config(edev, cap + PCI_EXP_SLTCTL, 2, &val);
|
2013-06-27 13:46:43 +08:00
|
|
|
if (val & PCI_EXP_SLTCTL_PCC) {
|
2019-08-16 12:48:13 +08:00
|
|
|
eeh_edev_dbg(edev, "In power-off state, power it on ...\n");
|
2013-06-27 13:46:43 +08:00
|
|
|
val &= ~(PCI_EXP_SLTCTL_PCC | PCI_EXP_SLTCTL_PIC);
|
|
|
|
val |= (0x0100 & PCI_EXP_SLTCTL_PIC);
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->write_config(edev, cap + PCI_EXP_SLTCTL, 2, val);
|
2013-06-27 13:46:43 +08:00
|
|
|
msleep(2 * 1000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable link */
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->read_config(edev, cap + PCI_EXP_LNKCTL, 2, &val);
|
2013-06-27 13:46:43 +08:00
|
|
|
val &= ~PCI_EXP_LNKCTL_LD;
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->write_config(edev, cap + PCI_EXP_LNKCTL, 2, val);
|
2013-06-27 13:46:43 +08:00
|
|
|
|
|
|
|
/* Check link */
|
2023-06-12 01:19:32 +08:00
|
|
|
if (!edev->pdev->link_active_reporting) {
|
|
|
|
eeh_edev_dbg(edev, "No link reporting capability\n");
|
2013-06-27 13:46:43 +08:00
|
|
|
msleep(1000);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait the link is up until timeout (5s) */
|
|
|
|
timeout = 0;
|
|
|
|
while (timeout < 5000) {
|
|
|
|
msleep(20);
|
|
|
|
timeout += 20;
|
|
|
|
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->read_config(edev, cap + PCI_EXP_LNKSTA, 2, &val);
|
2013-06-27 13:46:43 +08:00
|
|
|
if (val & PCI_EXP_LNKSTA_DLLLA)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (val & PCI_EXP_LNKSTA_DLLLA)
|
2019-08-16 12:48:13 +08:00
|
|
|
eeh_edev_dbg(edev, "Link up (%s)\n",
|
2013-06-27 13:46:43 +08:00
|
|
|
(val & PCI_EXP_LNKSTA_CLS_2_5GB) ? "2.5GB" : "5GB");
|
|
|
|
else
|
2019-08-16 12:48:13 +08:00
|
|
|
eeh_edev_dbg(edev, "Link not ready (0x%04x)\n", val);
|
2013-06-27 13:46:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
|
|
|
|
#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)])
|
|
|
|
|
2015-03-17 13:15:07 +08:00
|
|
|
static void eeh_restore_bridge_bars(struct eeh_dev *edev)
|
2013-06-27 13:46:43 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Device BARs: 0x10 - 0x18
|
|
|
|
* Bus numbers and windows: 0x18 - 0x30
|
|
|
|
*/
|
|
|
|
for (i = 4; i < 13; i++)
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->write_config(edev, i*4, 4, edev->config_space[i]);
|
2013-06-27 13:46:43 +08:00
|
|
|
/* Rom: 0x38 */
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->write_config(edev, 14*4, 4, edev->config_space[14]);
|
2013-06-27 13:46:43 +08:00
|
|
|
|
|
|
|
/* Cache line & Latency timer: 0xC 0xD */
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->write_config(edev, PCI_CACHE_LINE_SIZE, 1,
|
2013-06-27 13:46:43 +08:00
|
|
|
SAVED_BYTE(PCI_CACHE_LINE_SIZE));
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->write_config(edev, PCI_LATENCY_TIMER, 1,
|
|
|
|
SAVED_BYTE(PCI_LATENCY_TIMER));
|
2013-06-27 13:46:43 +08:00
|
|
|
/* Max latency, min grant, interrupt ping and line: 0x3C */
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->write_config(edev, 15*4, 4, edev->config_space[15]);
|
2013-06-27 13:46:43 +08:00
|
|
|
|
|
|
|
/* PCI Command: 0x4 */
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->write_config(edev, PCI_COMMAND, 4, edev->config_space[1] |
|
2018-04-11 11:37:58 +08:00
|
|
|
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
2013-06-27 13:46:43 +08:00
|
|
|
|
|
|
|
/* Check the PCIe link is ready */
|
2015-03-17 13:15:07 +08:00
|
|
|
eeh_bridge_check_link(edev);
|
2013-06-27 13:46:43 +08:00
|
|
|
}
|
|
|
|
|
2015-03-17 13:15:07 +08:00
|
|
|
static void eeh_restore_device_bars(struct eeh_dev *edev)
|
2012-09-08 06:44:15 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
u32 cmd;
|
|
|
|
|
|
|
|
for (i = 4; i < 10; i++)
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->write_config(edev, i*4, 4, edev->config_space[i]);
|
2012-09-08 06:44:15 +08:00
|
|
|
/* 12 == Expansion ROM Address */
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->write_config(edev, 12*4, 4, edev->config_space[12]);
|
2012-09-08 06:44:15 +08:00
|
|
|
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->write_config(edev, PCI_CACHE_LINE_SIZE, 1,
|
2012-09-08 06:44:15 +08:00
|
|
|
SAVED_BYTE(PCI_CACHE_LINE_SIZE));
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->write_config(edev, PCI_LATENCY_TIMER, 1,
|
2012-09-08 06:44:15 +08:00
|
|
|
SAVED_BYTE(PCI_LATENCY_TIMER));
|
|
|
|
|
|
|
|
/* max latency, min grant, interrupt pin and line */
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->write_config(edev, 15*4, 4, edev->config_space[15]);
|
2012-09-08 06:44:15 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Restore PERR & SERR bits, some devices require it,
|
|
|
|
* don't touch the other command bits
|
|
|
|
*/
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->read_config(edev, PCI_COMMAND, 4, &cmd);
|
2012-09-08 06:44:15 +08:00
|
|
|
if (edev->config_space[1] & PCI_COMMAND_PARITY)
|
|
|
|
cmd |= PCI_COMMAND_PARITY;
|
|
|
|
else
|
|
|
|
cmd &= ~PCI_COMMAND_PARITY;
|
|
|
|
if (edev->config_space[1] & PCI_COMMAND_SERR)
|
|
|
|
cmd |= PCI_COMMAND_SERR;
|
|
|
|
else
|
|
|
|
cmd &= ~PCI_COMMAND_SERR;
|
2020-07-25 16:12:26 +08:00
|
|
|
eeh_ops->write_config(edev, PCI_COMMAND, 4, cmd);
|
2013-06-27 13:46:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* eeh_restore_one_device_bars - Restore the Base Address Registers for one device
|
|
|
|
* @data: EEH device
|
|
|
|
* @flag: Unused
|
|
|
|
*
|
|
|
|
* Loads the PCI configuration space base address registers,
|
|
|
|
* the expansion ROM base address, the latency timer, and etc.
|
|
|
|
* from the saved values in the device node.
|
|
|
|
*/
|
2019-08-16 12:48:15 +08:00
|
|
|
static void eeh_restore_one_device_bars(struct eeh_dev *edev, void *flag)
|
2013-06-27 13:46:43 +08:00
|
|
|
{
|
powerpc/eeh: Use partial hotplug for EEH unaware drivers
When EEH error happens to one specific PE, some devices with drivers
supporting EEH won't except hotplug on the device. However, there
might have other deivces without driver, or with driver without EEH
support. For the case, we need do partial hotplug in order to make
sure that the PE becomes absolutely quite during reset. Otherise,
the PE reset might fail and leads to failure of error recovery.
The current code doesn't handle that 'mixed' case properly, it either
uses the error callbacks to the drivers, or tries hotplug, but doesn't
handle a PE (EEH domain) composed of a combination of the two.
The patch intends to support so-called "partial" hotplug for EEH:
Before we do reset, we stop and remove those PCI devices without
EEH sensitive driver. The corresponding EEH devices are not detached
from its PE, but with special flag. After the reset is done, those
EEH devices with the special flag will be scanned one by one.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2013-07-24 10:24:58 +08:00
|
|
|
/* Do special restore for bridges */
|
2013-07-24 10:24:59 +08:00
|
|
|
if (edev->mode & EEH_DEV_BRIDGE)
|
2015-03-17 13:15:07 +08:00
|
|
|
eeh_restore_bridge_bars(edev);
|
2013-06-27 13:46:43 +08:00
|
|
|
else
|
2015-03-17 13:15:07 +08:00
|
|
|
eeh_restore_device_bars(edev);
|
2012-09-08 06:44:15 +08:00
|
|
|
|
2020-07-25 16:12:24 +08:00
|
|
|
if (eeh_ops->restore_config)
|
|
|
|
eeh_ops->restore_config(edev);
|
2012-09-08 06:44:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* eeh_pe_restore_bars - Restore the PCI config space info
|
|
|
|
* @pe: EEH PE
|
|
|
|
*
|
|
|
|
* This routine performs a recursive walk to the children
|
|
|
|
* of this device as well.
|
|
|
|
*/
|
|
|
|
void eeh_pe_restore_bars(struct eeh_pe *pe)
|
|
|
|
{
|
2012-09-12 03:16:18 +08:00
|
|
|
/*
|
|
|
|
* We needn't take the EEH lock since eeh_pe_dev_traverse()
|
|
|
|
* will take that.
|
|
|
|
*/
|
2012-09-08 06:44:15 +08:00
|
|
|
eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL);
|
|
|
|
}
|
2012-09-08 06:44:19 +08:00
|
|
|
|
2014-06-11 16:26:44 +08:00
|
|
|
/**
|
|
|
|
* eeh_pe_loc_get - Retrieve location code binding to the given PE
|
|
|
|
* @pe: EEH PE
|
|
|
|
*
|
|
|
|
* Retrieve the location code of the given PE. If the primary PE bus
|
|
|
|
* is root bus, we will grab location code from PHB device tree node
|
|
|
|
* or root port. Otherwise, the upstream bridge's device tree node
|
|
|
|
* of the primary PE bus will be checked for the location code.
|
|
|
|
*/
|
|
|
|
const char *eeh_pe_loc_get(struct eeh_pe *pe)
|
|
|
|
{
|
|
|
|
struct pci_bus *bus = eeh_pe_bus_get(pe);
|
2015-12-02 13:25:32 +08:00
|
|
|
struct device_node *dn;
|
2014-07-15 13:42:22 +08:00
|
|
|
const char *loc = NULL;
|
2014-06-11 16:26:44 +08:00
|
|
|
|
2015-12-02 13:25:32 +08:00
|
|
|
while (bus) {
|
|
|
|
dn = pci_bus_to_OF_node(bus);
|
|
|
|
if (!dn) {
|
|
|
|
bus = bus->parent;
|
|
|
|
continue;
|
|
|
|
}
|
2014-06-11 16:26:44 +08:00
|
|
|
|
2015-12-02 13:25:32 +08:00
|
|
|
if (pci_is_root_bus(bus))
|
2014-07-15 13:42:22 +08:00
|
|
|
loc = of_get_property(dn, "ibm,io-base-loc-code", NULL);
|
2015-12-02 13:25:32 +08:00
|
|
|
else
|
|
|
|
loc = of_get_property(dn, "ibm,slot-location-code",
|
|
|
|
NULL);
|
|
|
|
|
2014-06-11 16:26:44 +08:00
|
|
|
if (loc)
|
2015-12-02 13:25:32 +08:00
|
|
|
return loc;
|
2014-06-11 16:26:44 +08:00
|
|
|
|
2015-12-02 13:25:32 +08:00
|
|
|
bus = bus->parent;
|
2014-06-11 16:26:44 +08:00
|
|
|
}
|
|
|
|
|
2015-12-02 13:25:32 +08:00
|
|
|
return "N/A";
|
2014-06-11 16:26:44 +08:00
|
|
|
}
|
|
|
|
|
2012-09-08 06:44:19 +08:00
|
|
|
/**
|
|
|
|
* eeh_pe_bus_get - Retrieve PCI bus according to the given PE
|
|
|
|
* @pe: EEH PE
|
|
|
|
*
|
|
|
|
* Retrieve the PCI bus according to the given PE. Basically,
|
|
|
|
* there're 3 types of PEs: PHB/Bus/Device. For PHB PE, the
|
|
|
|
* primary PCI bus will be retrieved. The parent bus will be
|
|
|
|
* returned for BUS PE. However, we don't have associated PCI
|
|
|
|
* bus for DEVICE PE.
|
|
|
|
*/
|
|
|
|
struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe)
|
|
|
|
{
|
|
|
|
struct eeh_dev *edev;
|
|
|
|
struct pci_dev *pdev;
|
2024-06-17 22:02:40 +08:00
|
|
|
struct pci_bus *bus = NULL;
|
2012-09-08 06:44:19 +08:00
|
|
|
|
2016-02-09 12:50:23 +08:00
|
|
|
if (pe->type & EEH_PE_PHB)
|
|
|
|
return pe->phb->bus;
|
2013-06-20 13:20:55 +08:00
|
|
|
|
2016-02-09 12:50:23 +08:00
|
|
|
/* The primary bus might be cached during probe time */
|
|
|
|
if (pe->state & EEH_PE_PRI_BUS)
|
|
|
|
return pe->bus;
|
|
|
|
|
|
|
|
/* Retrieve the parent PCI bus of first (top) PCI device */
|
2018-09-12 09:23:26 +08:00
|
|
|
edev = list_first_entry_or_null(&pe->edevs, struct eeh_dev, entry);
|
2024-06-17 22:02:40 +08:00
|
|
|
pci_lock_rescan_remove();
|
2016-02-09 12:50:23 +08:00
|
|
|
pdev = eeh_dev_to_pci_dev(edev);
|
|
|
|
if (pdev)
|
2024-06-17 22:02:40 +08:00
|
|
|
bus = pdev->bus;
|
|
|
|
pci_unlock_rescan_remove();
|
2012-09-08 06:44:19 +08:00
|
|
|
|
2024-06-17 22:02:40 +08:00
|
|
|
return bus;
|
2012-09-08 06:44:19 +08:00
|
|
|
}
|