PCI: aerdrv: introduce default_downstream_reset_link
authorHidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
Thu, 15 Apr 2010 04:21:27 +0000 (13:21 +0900)
committerJesse Barnes <jbarnes@virtuousgeek.org>
Tue, 11 May 2010 19:01:38 +0000 (12:01 -0700)
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>
Documentation/PCI/pcieaer-howto.txt
drivers/pci/pcie/aer/aerdrv.c
drivers/pci/pcie/aer/aerdrv.h
drivers/pci/pcie/aer/aerdrv_core.c

index 8c406a4967990185e6973caa7fca7182d1134890..26d3d945c3c21aa965cd9b48c49cc42abf09df3f 100644 (file)
@@ -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
 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?
 
@@ -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.
 
 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,
 internally processes and logs the error message in its PCI Express
 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
 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
-and the port is downstream port, we will use the aer driver of the
-root port who reports the AER error. As for upstream ports,
+and the port is downstream port, we will perform a hot reset as the
+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
 function. If error_detected returns PCI_ERS_RESULT_CAN_RECOVER and
 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
 
-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
-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.
 
 CONFIG_PCIEAER_INJECT=y or CONFIG_PCIEAER_INJECT=m
index cbc7cc77b2c3624197c21ddfd8b69ac7856b683c..a225d58c1ac8f8b7b3a5f2fb436bb4eb07de259b 100644 (file)
@@ -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)
 {
-       u16 p2p_ctrl;
        u32 reg32;
        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;
        pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
 
-       /* 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
-        * 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);
+       aer_do_secondary_bus_reset(dev);
        dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n");
 
        /* Clear Root Error Status */
index d0f8291c5ca0db182db6ca0004ae2ffa39a62fcc..7aaae2d2bd67af5abca6575d6c0230b858fa15af 100644 (file)
@@ -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 void aer_do_secondary_bus_reset(struct pci_dev *dev);
 extern int aer_init(struct pcie_device *dev);
 extern void aer_isr(struct work_struct *work);
 extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
index 8fb14aeb74dd6584c40b53678dd0c558253b6d8c..ce42cac99dd37b6a97ff5cb84c276464e4d2689c 100644 (file)
@@ -373,6 +373,53 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
        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)
 {
        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;
        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;
-       else
+       } else {
+               /* Reset the upstream component (likely downstream port) */
                udev = dev->bus->self;
+       }
 
        /* Use the aer driver of the component firstly */
        driver = find_aer_service(udev);
 
-       /*
-        * If it hasn't the driver and is downstream port, use the root port's
-        */
-       if (!driver || !driver->reset_link) {
-               if (udev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM &&
-                       aerdev->device.driver &&
-                       to_service_driver(aerdev->device.driver)->reset_link) {
-                       driver = to_service_driver(aerdev->device.driver);
-               } else {
-                       dev_printk(KERN_DEBUG, &dev->dev,
-                               "no link-reset support at upstream device %s\n",
-                               pci_name(udev));
-                       return PCI_ERS_RESULT_DISCONNECT;
-               }
+       if (driver && driver->reset_link) {
+               status = driver->reset_link(udev);
+       } else if (udev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) {
+               status = default_downstream_reset_link(udev);
+       } else {
+               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) {
                dev_printk(KERN_DEBUG, &dev->dev,
                        "link reset at upstream device %s failed\n",