mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-01 22:54:01 +00:00
PCI: aerdrv: introduce default_downstream_reset_link
I noticed that when I inject a fatal error to an endpoint via aer-inject, aer_root_reset() is called as reset_link for a downstream port at upstream of the endpoint: pcieport 0000:00:06.0: AER: Uncorrected (Fatal) error received: id=5401 : pcieport 0000:52:02.0: Root Port link has been reset It externally appears to be working, but internally issues some accesses to PCI_ERR_ROOT_COMMAND/STATUS registers that is for root port so not available on downstream port. This patch introduces default_downstream_reset_link that is a version of aer_root_reset() with no accesses to root port's register. It is used for downstream ports that has no reset_link function its specific. This patch also updates related description in pcieaer-howto.txt. Some minor fixes are included. Signed-off-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com> Reviewed-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
This commit is contained in:
parent
517cae3829
commit
89713422a7
4 changed files with 71 additions and 46 deletions
|
@ -13,7 +13,7 @@ Reporting (AER) driver and provides information on how to use it, as
|
||||||
well as how to enable the drivers of endpoint devices to conform with
|
well as how to enable the drivers of endpoint devices to conform with
|
||||||
PCI Express AER driver.
|
PCI Express AER driver.
|
||||||
|
|
||||||
1.2 Copyright © Intel Corporation 2006.
|
1.2 Copyright (C) Intel Corporation 2006.
|
||||||
|
|
||||||
1.3 What is the PCI Express AER Driver?
|
1.3 What is the PCI Express AER Driver?
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ but the PCI Express link itself is fully functional. Fatal errors, on
|
||||||
the other hand, cause the link to be unreliable.
|
the other hand, cause the link to be unreliable.
|
||||||
|
|
||||||
When AER is enabled, a PCI Express device will automatically send an
|
When AER is enabled, a PCI Express device will automatically send an
|
||||||
error message to the PCIE root port above it when the device captures
|
error message to the PCIe root port above it when the device captures
|
||||||
an error. The Root Port, upon receiving an error reporting message,
|
an error. The Root Port, upon receiving an error reporting message,
|
||||||
internally processes and logs the error message in its PCI Express
|
internally processes and logs the error message in its PCI Express
|
||||||
capability structure. Error information being logged includes storing
|
capability structure. Error information being logged includes storing
|
||||||
|
@ -194,8 +194,9 @@ to reset link, AER port service driver is required to provide the
|
||||||
function to reset link. Firstly, kernel looks for if the upstream
|
function to reset link. Firstly, kernel looks for if the upstream
|
||||||
component has an aer driver. If it has, kernel uses the reset_link
|
component has an aer driver. If it has, kernel uses the reset_link
|
||||||
callback of the aer driver. If the upstream component has no aer driver
|
callback of the aer driver. If the upstream component has no aer driver
|
||||||
and the port is downstream port, we will use the aer driver of the
|
and the port is downstream port, we will perform a hot reset as the
|
||||||
root port who reports the AER error. As for upstream ports,
|
default by setting the Secondary Bus Reset bit of the Bridge Control
|
||||||
|
register associated with the downstream port. As for upstream ports,
|
||||||
they should provide their own aer service drivers with reset_link
|
they should provide their own aer service drivers with reset_link
|
||||||
function. If error_detected returns PCI_ERS_RESULT_CAN_RECOVER and
|
function. If error_detected returns PCI_ERS_RESULT_CAN_RECOVER and
|
||||||
reset_link returns PCI_ERS_RESULT_RECOVERED, the error handling goes
|
reset_link returns PCI_ERS_RESULT_RECOVERED, the error handling goes
|
||||||
|
@ -249,11 +250,11 @@ cleanup uncorrectable status register. Pls. refer to section 3.3.
|
||||||
|
|
||||||
4. Software error injection
|
4. Software error injection
|
||||||
|
|
||||||
Debugging PCIE AER error recovery code is quite difficult because it
|
Debugging PCIe AER error recovery code is quite difficult because it
|
||||||
is hard to trigger real hardware errors. Software based error
|
is hard to trigger real hardware errors. Software based error
|
||||||
injection can be used to fake various kinds of PCIE errors.
|
injection can be used to fake various kinds of PCIe errors.
|
||||||
|
|
||||||
First you should enable PCIE AER software error injection in kernel
|
First you should enable PCIe AER software error injection in kernel
|
||||||
configuration, that is, following item should be in your .config.
|
configuration, that is, following item should be in your .config.
|
||||||
|
|
||||||
CONFIG_PCIEAER_INJECT=y or CONFIG_PCIEAER_INJECT=m
|
CONFIG_PCIEAER_INJECT=y or CONFIG_PCIEAER_INJECT=m
|
||||||
|
|
|
@ -341,7 +341,6 @@ static int __devinit aer_probe(struct pcie_device *dev)
|
||||||
**/
|
**/
|
||||||
static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
|
static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
u16 p2p_ctrl;
|
|
||||||
u32 reg32;
|
u32 reg32;
|
||||||
int pos;
|
int pos;
|
||||||
|
|
||||||
|
@ -352,27 +351,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
|
||||||
reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
|
reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
|
||||||
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
|
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
|
||||||
|
|
||||||
/* Assert Secondary Bus Reset */
|
aer_do_secondary_bus_reset(dev);
|
||||||
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl);
|
|
||||||
p2p_ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
|
|
||||||
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* we should send hot reset message for 2ms to allow it time to
|
|
||||||
* propogate to all downstream ports
|
|
||||||
*/
|
|
||||||
msleep(2);
|
|
||||||
|
|
||||||
/* De-assert Secondary Bus Reset */
|
|
||||||
p2p_ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
|
|
||||||
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* System software must wait for at least 100ms from the end
|
|
||||||
* of a reset of one or more device before it is permitted
|
|
||||||
* to issue Configuration Requests to those devices.
|
|
||||||
*/
|
|
||||||
msleep(200);
|
|
||||||
dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n");
|
dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n");
|
||||||
|
|
||||||
/* Clear Root Error Status */
|
/* Clear Root Error Status */
|
||||||
|
|
|
@ -114,6 +114,7 @@ static inline pci_ers_result_t merge_result(enum pci_ers_result orig,
|
||||||
}
|
}
|
||||||
|
|
||||||
extern struct bus_type pcie_port_bus_type;
|
extern struct bus_type pcie_port_bus_type;
|
||||||
|
extern void aer_do_secondary_bus_reset(struct pci_dev *dev);
|
||||||
extern int aer_init(struct pcie_device *dev);
|
extern int aer_init(struct pcie_device *dev);
|
||||||
extern void aer_isr(struct work_struct *work);
|
extern void aer_isr(struct work_struct *work);
|
||||||
extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
|
extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
|
||||||
|
|
|
@ -373,6 +373,53 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
|
||||||
return result_data.result;
|
return result_data.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* aer_do_secondary_bus_reset - perform secondary bus reset
|
||||||
|
* @dev: pointer to bridge's pci_dev data structure
|
||||||
|
*
|
||||||
|
* Invoked when performing link reset at Root Port or Downstream Port.
|
||||||
|
*/
|
||||||
|
void aer_do_secondary_bus_reset(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
u16 p2p_ctrl;
|
||||||
|
|
||||||
|
/* Assert Secondary Bus Reset */
|
||||||
|
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl);
|
||||||
|
p2p_ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
|
||||||
|
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* we should send hot reset message for 2ms to allow it time to
|
||||||
|
* propagate to all downstream ports
|
||||||
|
*/
|
||||||
|
msleep(2);
|
||||||
|
|
||||||
|
/* De-assert Secondary Bus Reset */
|
||||||
|
p2p_ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
|
||||||
|
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* System software must wait for at least 100ms from the end
|
||||||
|
* of a reset of one or more device before it is permitted
|
||||||
|
* to issue Configuration Requests to those devices.
|
||||||
|
*/
|
||||||
|
msleep(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* default_downstream_reset_link - default reset function for Downstream Port
|
||||||
|
* @dev: pointer to downstream port's pci_dev data structure
|
||||||
|
*
|
||||||
|
* Invoked when performing link reset at Downstream Port w/ no aer driver.
|
||||||
|
*/
|
||||||
|
static pci_ers_result_t default_downstream_reset_link(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
aer_do_secondary_bus_reset(dev);
|
||||||
|
dev_printk(KERN_DEBUG, &dev->dev,
|
||||||
|
"Downstream Port link has been reset\n");
|
||||||
|
return PCI_ERS_RESULT_RECOVERED;
|
||||||
|
}
|
||||||
|
|
||||||
static int find_aer_service_iter(struct device *device, void *data)
|
static int find_aer_service_iter(struct device *device, void *data)
|
||||||
{
|
{
|
||||||
struct pcie_port_service_driver *service_driver, **drv;
|
struct pcie_port_service_driver *service_driver, **drv;
|
||||||
|
@ -406,31 +453,28 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev,
|
||||||
pci_ers_result_t status;
|
pci_ers_result_t status;
|
||||||
struct pcie_port_service_driver *driver;
|
struct pcie_port_service_driver *driver;
|
||||||
|
|
||||||
if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)
|
if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) {
|
||||||
|
/* Reset this port for all subordinates */
|
||||||
udev = dev;
|
udev = dev;
|
||||||
else
|
} else {
|
||||||
|
/* Reset the upstream component (likely downstream port) */
|
||||||
udev = dev->bus->self;
|
udev = dev->bus->self;
|
||||||
|
}
|
||||||
|
|
||||||
/* Use the aer driver of the component firstly */
|
/* Use the aer driver of the component firstly */
|
||||||
driver = find_aer_service(udev);
|
driver = find_aer_service(udev);
|
||||||
|
|
||||||
/*
|
if (driver && driver->reset_link) {
|
||||||
* If it hasn't the driver and is downstream port, use the root port's
|
status = driver->reset_link(udev);
|
||||||
*/
|
} else if (udev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) {
|
||||||
if (!driver || !driver->reset_link) {
|
status = default_downstream_reset_link(udev);
|
||||||
if (udev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM &&
|
} else {
|
||||||
aerdev->device.driver &&
|
dev_printk(KERN_DEBUG, &dev->dev,
|
||||||
to_service_driver(aerdev->device.driver)->reset_link) {
|
"no link-reset support at upstream device %s\n",
|
||||||
driver = to_service_driver(aerdev->device.driver);
|
pci_name(udev));
|
||||||
} else {
|
return PCI_ERS_RESULT_DISCONNECT;
|
||||||
dev_printk(KERN_DEBUG, &dev->dev,
|
|
||||||
"no link-reset support at upstream device %s\n",
|
|
||||||
pci_name(udev));
|
|
||||||
return PCI_ERS_RESULT_DISCONNECT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status = driver->reset_link(udev);
|
|
||||||
if (status != PCI_ERS_RESULT_RECOVERED) {
|
if (status != PCI_ERS_RESULT_RECOVERED) {
|
||||||
dev_printk(KERN_DEBUG, &dev->dev,
|
dev_printk(KERN_DEBUG, &dev->dev,
|
||||||
"link reset at upstream device %s failed\n",
|
"link reset at upstream device %s failed\n",
|
||||||
|
|
Loading…
Reference in a new issue