]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge branch 'pci/enumeration'
authorBjorn Helgaas <bhelgaas@google.com>
Wed, 6 Jun 2018 21:10:08 +0000 (16:10 -0500)
committerBjorn Helgaas <bhelgaas@google.com>
Wed, 6 Jun 2018 21:10:08 +0000 (16:10 -0500)
  - neaten pci=earlydump output (Andy Shevchenko)

  - avoid errors when extended config space inaccessible (Gilles Buloz)

  - prevent sysfs disable of device while driver attached (Christoph
    Hellwig)

  - use core interface to report PCIe link properties in bnx2x, bnxt_en,
    cxgb4, ixgbe (Bjorn Helgaas)

  - remove unused pcie_get_minimum_link() (Bjorn Helgaas)

* pci/enumeration:
  PCI: Remove unused pcie_get_minimum_link()
  ixgbe: Report PCIe link properties with pcie_print_link_status()
  cxgb4: Report PCIe link properties with pcie_print_link_status()
  bnxt_en: Report PCIe link properties with pcie_print_link_status()
  bnx2x: Report PCIe link properties with pcie_print_link_status()
  PCI: Prevent sysfs disable of device while driver is attached
  PCI: Check whether bridges allow access to extended config space
  x86/PCI: Make pci=earlydump output neat

22 files changed:
Documentation/PCI/pci-error-recovery.txt
drivers/acpi/pci_root.c
drivers/pci/hotplug/pciehp_hpc.c
drivers/pci/pci-driver.c
drivers/pci/pci.c
drivers/pci/pci.h
drivers/pci/pcie/Makefile
drivers/pci/pcie/aer/aerdrv.c
drivers/pci/pcie/aer/aerdrv.h
drivers/pci/pcie/aer/aerdrv_core.c
drivers/pci/pcie/aer/aerdrv_errprint.c
drivers/pci/pcie/aspm.c
drivers/pci/pcie/dpc.c
drivers/pci/pcie/err.c [new file with mode: 0644]
drivers/pci/pcie/portdrv.h
drivers/pci/pcie/portdrv_core.c
drivers/pci/probe.c
include/linux/acpi.h
include/linux/aer.h
include/linux/pci.h
include/ras/ras_event.h
include/uapi/linux/pci_regs.h

index 0b6bb3ef449ee7a8ee45afe9b4be61a2859ac8e3..688b69121e8294ca81d47689c25345e97d5de720 100644 (file)
@@ -110,7 +110,7 @@ The actual steps taken by a platform to recover from a PCI error
 event will be platform-dependent, but will follow the general
 sequence described below.
 
-STEP 0: Error Event
+STEP 0: Error Event: ERR_NONFATAL
 -------------------
 A PCI bus error is detected by the PCI hardware.  On powerpc, the slot
 is isolated, in that all I/O is blocked: all reads return 0xffffffff,
@@ -228,13 +228,7 @@ proceeds to either STEP3 (Link Reset) or to STEP 5 (Resume Operations).
 If any driver returned PCI_ERS_RESULT_NEED_RESET, then the platform
 proceeds to STEP 4 (Slot Reset)
 
-STEP 3: Link Reset
-------------------
-The platform resets the link.  This is a PCI-Express specific step
-and is done whenever a fatal error has been detected that can be
-"solved" by resetting the link.
-
-STEP 4: Slot Reset
+STEP 3: Slot Reset
 ------------------
 
 In response to a return value of PCI_ERS_RESULT_NEED_RESET, the
@@ -320,7 +314,7 @@ Failure).
 >>> However, it probably should.
 
 
-STEP 5: Resume Operations
+STEP 4: Resume Operations
 -------------------------
 The platform will call the resume() callback on all affected device
 drivers if all drivers on the segment have returned
@@ -332,7 +326,7 @@ a result code.
 At this point, if a new error happens, the platform will restart
 a new error recovery sequence.
 
-STEP 6: Permanent Failure
+STEP 5: Permanent Failure
 -------------------------
 A "permanent failure" has occurred, and the platform cannot recover
 the device.  The platform will call error_detected() with a
@@ -355,6 +349,27 @@ errors. See the discussion in powerpc/eeh-pci-error-recovery.txt
 for additional detail on real-life experience of the causes of
 software errors.
 
+STEP 0: Error Event: ERR_FATAL
+-------------------
+PCI bus error is detected by the PCI hardware. On powerpc, the slot is
+isolated, in that all I/O is blocked: all reads return 0xffffffff, all
+writes are ignored.
+
+STEP 1: Remove devices
+--------------------
+Platform removes the devices depending on the error agent, it could be
+this port for all subordinates or upstream component (likely downstream
+port)
+
+STEP 2: Reset link
+--------------------
+The platform resets the link.  This is a PCI-Express specific step and is
+done whenever a fatal error has been detected that can be "solved" by
+resetting the link.
+
+STEP 3: Re-enumerate the devices
+--------------------
+Initiates the re-enumeration.
 
 Conclusion; General Remarks
 ---------------------------
index 0da18bde6a165cd5d02dad6855850adfd1bde604..2ff0d6702a2e239b2f40c9c08b247013383a9d54 100644 (file)
@@ -153,6 +153,7 @@ static struct pci_osc_bit_struct pci_osc_control_bit[] = {
        { OSC_PCI_EXPRESS_PME_CONTROL, "PME" },
        { OSC_PCI_EXPRESS_AER_CONTROL, "AER" },
        { OSC_PCI_EXPRESS_CAPABILITY_CONTROL, "PCIeCapability" },
+       { OSC_PCI_EXPRESS_LTR_CONTROL, "LTR" },
 };
 
 static void decode_osc_bits(struct acpi_pci_root *root, char *msg, u32 word,
@@ -475,6 +476,9 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm)
                | OSC_PCI_EXPRESS_NATIVE_HP_CONTROL
                | OSC_PCI_EXPRESS_PME_CONTROL;
 
+       if (IS_ENABLED(CONFIG_PCIEASPM))
+               control |= OSC_PCI_EXPRESS_LTR_CONTROL;
+
        if (pci_aer_available()) {
                if (aer_acpi_firmware_first())
                        dev_info(&device->dev,
@@ -905,6 +909,8 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
                host_bridge->native_aer = 0;
        if (!(root->osc_control_set & OSC_PCI_EXPRESS_PME_CONTROL))
                host_bridge->native_pme = 0;
+       if (!(root->osc_control_set & OSC_PCI_EXPRESS_LTR_CONTROL))
+               host_bridge->native_ltr = 0;
 
        pci_scan_child_bus(bus);
        pci_set_host_bridge_release(host_bridge, acpi_pci_root_release_info,
index 18a42f8f5dc56c3cd71c1a553ebaf4c340bbbce1..e0c2b8ead6cbb5d17d5e3295d57afde71d21e2c9 100644 (file)
@@ -231,25 +231,11 @@ bool pciehp_check_link_active(struct controller *ctrl)
        return ret;
 }
 
-static void __pcie_wait_link_active(struct controller *ctrl, bool active)
-{
-       int timeout = 1000;
-
-       if (pciehp_check_link_active(ctrl) == active)
-               return;
-       while (timeout > 0) {
-               msleep(10);
-               timeout -= 10;
-               if (pciehp_check_link_active(ctrl) == active)
-                       return;
-       }
-       ctrl_dbg(ctrl, "Data Link Layer Link Active not %s in 1000 msec\n",
-                       active ? "set" : "cleared");
-}
-
 static void pcie_wait_link_active(struct controller *ctrl)
 {
-       __pcie_wait_link_active(ctrl, true);
+       struct pci_dev *pdev = ctrl_dev(ctrl);
+
+       pcie_wait_for_link(pdev, true);
 }
 
 static bool pci_bus_check_dev(struct pci_bus *bus, int devfn)
index 6ace47099fc59451422daaf7d38c52f470c9c500..ffb956457b4aa09102d7be7e9aa3c4af217c732b 100644 (file)
@@ -1535,7 +1535,7 @@ static int pci_uevent(struct device *dev, struct kobj_uevent_env *env)
        return 0;
 }
 
-#if defined(CONFIG_PCIEAER) || defined(CONFIG_EEH)
+#if defined(CONFIG_PCIEPORTBUS) || defined(CONFIG_EEH)
 /**
  * pci_uevent_ers - emit a uevent during recovery path of PCI device
  * @pdev: PCI device undergoing error recovery
index 4bafa817c40a9b2f6805bb7c92bbc566262033ae..2aaab81341ac9557b1d682076a479ea9eaaf20c5 100644 (file)
@@ -4138,6 +4138,35 @@ static int pci_pm_reset(struct pci_dev *dev, int probe)
 
        return pci_dev_wait(dev, "PM D3->D0", PCIE_RESET_READY_POLL_MS);
 }
+/**
+ * pcie_wait_for_link - Wait until link is active or inactive
+ * @pdev: Bridge device
+ * @active: waiting for active or inactive?
+ *
+ * Use this to wait till link becomes active or inactive.
+ */
+bool pcie_wait_for_link(struct pci_dev *pdev, bool active)
+{
+       int timeout = 1000;
+       bool ret;
+       u16 lnk_status;
+
+       for (;;) {
+               pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
+               ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA);
+               if (ret == active)
+                       return true;
+               if (timeout <= 0)
+                       break;
+               msleep(10);
+               timeout -= 10;
+       }
+
+       pci_info(pdev, "Data Link Layer Link Active not %s in 1000 msec\n",
+                active ? "set" : "cleared");
+
+       return false;
+}
 
 void pci_reset_secondary_bus(struct pci_dev *dev)
 {
index 023f7cf25bff475f32b97da88033810f593ae133..6af75952cac7541b3920f2c2f203073d309298d7 100644 (file)
@@ -353,6 +353,11 @@ static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
 
 void pci_enable_acs(struct pci_dev *dev);
 
+/* PCI error reporting and recovery */
+void pcie_do_fatal_recovery(struct pci_dev *dev, u32 service);
+void pcie_do_nonfatal_recovery(struct pci_dev *dev);
+
+bool pcie_wait_for_link(struct pci_dev *pdev, bool active);
 #ifdef CONFIG_PCIEASPM
 void pcie_aspm_init_link_state(struct pci_dev *pdev);
 void pcie_aspm_exit_link_state(struct pci_dev *pdev);
index 800e1d404a45500368d8f753a0f6ea07c5c331cc..03f4e0b3a1400475ee0187995075c24767e92ef8 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Makefile for PCI Express features and port driver
 
-pcieportdrv-y                  := portdrv_core.o portdrv_pci.o
+pcieportdrv-y                  := portdrv_core.o portdrv_pci.o err.o
 
 obj-$(CONFIG_PCIEPORTBUS)      += pcieportdrv.o
 
index 779b3879b1b5edb28736e3cc555a5a82428a707e..9735c19bf39cf5bc0b8e8d06627f2cc243a1998b 100644 (file)
@@ -94,7 +94,7 @@ static void set_downstream_devices_error_reporting(struct pci_dev *dev,
  */
 static void aer_enable_rootport(struct aer_rpc *rpc)
 {
-       struct pci_dev *pdev = rpc->rpd->port;
+       struct pci_dev *pdev = rpc->rpd;
        int aer_pos;
        u16 reg16;
        u32 reg32;
@@ -136,7 +136,7 @@ static void aer_enable_rootport(struct aer_rpc *rpc)
  */
 static void aer_disable_rootport(struct aer_rpc *rpc)
 {
-       struct pci_dev *pdev = rpc->rpd->port;
+       struct pci_dev *pdev = rpc->rpd;
        u32 reg32;
        int pos;
 
@@ -232,7 +232,7 @@ static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev)
        /* Initialize Root lock access, e_lock, to Root Error Status Reg */
        spin_lock_init(&rpc->e_lock);
 
-       rpc->rpd = dev;
+       rpc->rpd = dev->port;
        INIT_WORK(&rpc->dpc_handler, aer_isr);
        mutex_init(&rpc->rpc_mutex);
 
@@ -353,10 +353,7 @@ static void aer_error_resume(struct pci_dev *dev)
        pos = dev->aer_cap;
        pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
        pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
-       if (dev->error_state == pci_channel_io_normal)
-               status &= ~mask; /* Clear corresponding nonfatal bits */
-       else
-               status &= mask; /* Clear corresponding fatal bits */
+       status &= ~mask; /* Clear corresponding nonfatal bits */
        pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
 }
 
index 08b4584f62fe8bc03ae392f93314497d0ef8c626..6e0ad9a68fd9c2592c61df85efa3827b0d5ec98e 100644 (file)
@@ -58,7 +58,7 @@ struct aer_err_source {
 };
 
 struct aer_rpc {
-       struct pcie_device *rpd;        /* Root Port device */
+       struct pci_dev *rpd;            /* Root Port device */
        struct work_struct dpc_handler;
        struct aer_err_source e_sources[AER_ERROR_SOURCES_MAX];
        struct aer_err_info e_info;
@@ -76,36 +76,6 @@ struct aer_rpc {
                                         */
 };
 
-struct aer_broadcast_data {
-       enum pci_channel_state state;
-       enum pci_ers_result result;
-};
-
-static inline pci_ers_result_t merge_result(enum pci_ers_result orig,
-               enum pci_ers_result new)
-{
-       if (new == PCI_ERS_RESULT_NO_AER_DRIVER)
-               return PCI_ERS_RESULT_NO_AER_DRIVER;
-
-       if (new == PCI_ERS_RESULT_NONE)
-               return orig;
-
-       switch (orig) {
-       case PCI_ERS_RESULT_CAN_RECOVER:
-       case PCI_ERS_RESULT_RECOVERED:
-               orig = new;
-               break;
-       case PCI_ERS_RESULT_DISCONNECT:
-               if (new == PCI_ERS_RESULT_NEED_RESET)
-                       orig = PCI_ERS_RESULT_NEED_RESET;
-               break;
-       default:
-               break;
-       }
-
-       return orig;
-}
-
 extern struct bus_type pcie_port_bus_type;
 void aer_isr(struct work_struct *work);
 void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
index 0ea5acc40323385db1fbee15dbd4998e0ea54420..42d4f3f32282b60435a583e06f43219ff352b052 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/slab.h>
 #include <linux/kfifo.h>
 #include "aerdrv.h"
+#include "../../pci.h"
 
 #define        PCI_EXP_AER_FLAGS       (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \
                                 PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE)
@@ -227,329 +228,14 @@ static bool find_source_device(struct pci_dev *parent,
        return true;
 }
 
-static int report_error_detected(struct pci_dev *dev, void *data)
-{
-       pci_ers_result_t vote;
-       const struct pci_error_handlers *err_handler;
-       struct aer_broadcast_data *result_data;
-       result_data = (struct aer_broadcast_data *) data;
-
-       device_lock(&dev->dev);
-       dev->error_state = result_data->state;
-
-       if (!dev->driver ||
-               !dev->driver->err_handler ||
-               !dev->driver->err_handler->error_detected) {
-               if (result_data->state == pci_channel_io_frozen &&
-                       dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
-                       /*
-                        * In case of fatal recovery, if one of down-
-                        * stream device has no driver. We might be
-                        * unable to recover because a later insmod
-                        * of a driver for this device is unaware of
-                        * its hw state.
-                        */
-                       pci_printk(KERN_DEBUG, dev, "device has %s\n",
-                                  dev->driver ?
-                                  "no AER-aware driver" : "no driver");
-               }
-
-               /*
-                * If there's any device in the subtree that does not
-                * have an error_detected callback, returning
-                * PCI_ERS_RESULT_NO_AER_DRIVER prevents calling of
-                * the subsequent mmio_enabled/slot_reset/resume
-                * callbacks of "any" device in the subtree. All the
-                * devices in the subtree are left in the error state
-                * without recovery.
-                */
-
-               if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE)
-                       vote = PCI_ERS_RESULT_NO_AER_DRIVER;
-               else
-                       vote = PCI_ERS_RESULT_NONE;
-       } else {
-               err_handler = dev->driver->err_handler;
-               vote = err_handler->error_detected(dev, result_data->state);
-               pci_uevent_ers(dev, PCI_ERS_RESULT_NONE);
-       }
-
-       result_data->result = merge_result(result_data->result, vote);
-       device_unlock(&dev->dev);
-       return 0;
-}
-
-static int report_mmio_enabled(struct pci_dev *dev, void *data)
-{
-       pci_ers_result_t vote;
-       const struct pci_error_handlers *err_handler;
-       struct aer_broadcast_data *result_data;
-       result_data = (struct aer_broadcast_data *) data;
-
-       device_lock(&dev->dev);
-       if (!dev->driver ||
-               !dev->driver->err_handler ||
-               !dev->driver->err_handler->mmio_enabled)
-               goto out;
-
-       err_handler = dev->driver->err_handler;
-       vote = err_handler->mmio_enabled(dev);
-       result_data->result = merge_result(result_data->result, vote);
-out:
-       device_unlock(&dev->dev);
-       return 0;
-}
-
-static int report_slot_reset(struct pci_dev *dev, void *data)
-{
-       pci_ers_result_t vote;
-       const struct pci_error_handlers *err_handler;
-       struct aer_broadcast_data *result_data;
-       result_data = (struct aer_broadcast_data *) data;
-
-       device_lock(&dev->dev);
-       if (!dev->driver ||
-               !dev->driver->err_handler ||
-               !dev->driver->err_handler->slot_reset)
-               goto out;
-
-       err_handler = dev->driver->err_handler;
-       vote = err_handler->slot_reset(dev);
-       result_data->result = merge_result(result_data->result, vote);
-out:
-       device_unlock(&dev->dev);
-       return 0;
-}
-
-static int report_resume(struct pci_dev *dev, void *data)
-{
-       const struct pci_error_handlers *err_handler;
-
-       device_lock(&dev->dev);
-       dev->error_state = pci_channel_io_normal;
-
-       if (!dev->driver ||
-               !dev->driver->err_handler ||
-               !dev->driver->err_handler->resume)
-               goto out;
-
-       err_handler = dev->driver->err_handler;
-       err_handler->resume(dev);
-       pci_uevent_ers(dev, PCI_ERS_RESULT_RECOVERED);
-out:
-       device_unlock(&dev->dev);
-       return 0;
-}
-
-/**
- * broadcast_error_message - handle message broadcast to downstream drivers
- * @dev: pointer to from where in a hierarchy message is broadcasted down
- * @state: error state
- * @error_mesg: message to print
- * @cb: callback to be broadcasted
- *
- * Invoked during error recovery process. Once being invoked, the content
- * of error severity will be broadcasted to all downstream drivers in a
- * hierarchy in question.
- */
-static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
-       enum pci_channel_state state,
-       char *error_mesg,
-       int (*cb)(struct pci_dev *, void *))
-{
-       struct aer_broadcast_data result_data;
-
-       pci_printk(KERN_DEBUG, dev, "broadcast %s message\n", error_mesg);
-       result_data.state = state;
-       if (cb == report_error_detected)
-               result_data.result = PCI_ERS_RESULT_CAN_RECOVER;
-       else
-               result_data.result = PCI_ERS_RESULT_RECOVERED;
-
-       if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
-               /*
-                * If the error is reported by a bridge, we think this error
-                * is related to the downstream link of the bridge, so we
-                * do error recovery on all subordinates of the bridge instead
-                * of the bridge and clear the error status of the bridge.
-                */
-               if (cb == report_error_detected)
-                       dev->error_state = state;
-               pci_walk_bus(dev->subordinate, cb, &result_data);
-               if (cb == report_resume) {
-                       pci_cleanup_aer_uncorrect_error_status(dev);
-                       dev->error_state = pci_channel_io_normal;
-               }
-       } else {
-               /*
-                * If the error is reported by an end point, we think this
-                * error is related to the upstream link of the end point.
-                */
-               if (state == pci_channel_io_normal)
-                       /*
-                        * the error is non fatal so the bus is ok, just invoke
-                        * the callback for the function that logged the error.
-                        */
-                       cb(dev, &result_data);
-               else
-                       pci_walk_bus(dev->bus, cb, &result_data);
-       }
-
-       return result_data.result;
-}
-
-/**
- * default_reset_link - default reset function
- * @dev: pointer to pci_dev data structure
- *
- * Invoked when performing link reset on a Downstream Port or a
- * Root Port with no aer driver.
- */
-static pci_ers_result_t default_reset_link(struct pci_dev *dev)
-{
-       pci_reset_bridge_secondary_bus(dev);
-       pci_printk(KERN_DEBUG, dev, "downstream 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;
-
-       drv = (struct pcie_port_service_driver **) data;
-
-       if (device->bus == &pcie_port_bus_type && device->driver) {
-               service_driver = to_service_driver(device->driver);
-               if (service_driver->service == PCIE_PORT_SERVICE_AER) {
-                       *drv = service_driver;
-                       return 1;
-               }
-       }
-
-       return 0;
-}
-
-static struct pcie_port_service_driver *find_aer_service(struct pci_dev *dev)
-{
-       struct pcie_port_service_driver *drv = NULL;
-
-       device_for_each_child(&dev->dev, &drv, find_aer_service_iter);
-
-       return drv;
-}
-
-static pci_ers_result_t reset_link(struct pci_dev *dev)
-{
-       struct pci_dev *udev;
-       pci_ers_result_t status;
-       struct pcie_port_service_driver *driver;
-
-       if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
-               /* Reset this port for all subordinates */
-               udev = dev;
-       } 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 (driver && driver->reset_link) {
-               status = driver->reset_link(udev);
-       } else if (udev->has_secondary_link) {
-               status = default_reset_link(udev);
-       } else {
-               pci_printk(KERN_DEBUG, dev, "no link-reset support at upstream device %s\n",
-                       pci_name(udev));
-               return PCI_ERS_RESULT_DISCONNECT;
-       }
-
-       if (status != PCI_ERS_RESULT_RECOVERED) {
-               pci_printk(KERN_DEBUG, dev, "link reset at upstream device %s failed\n",
-                       pci_name(udev));
-               return PCI_ERS_RESULT_DISCONNECT;
-       }
-
-       return status;
-}
-
-/**
- * do_recovery - handle nonfatal/fatal error recovery process
- * @dev: pointer to a pci_dev data structure of agent detecting an error
- * @severity: error severity type
- *
- * Invoked when an error is nonfatal/fatal. Once being invoked, broadcast
- * error detected message to all downstream drivers within a hierarchy in
- * question and return the returned code.
- */
-static void do_recovery(struct pci_dev *dev, int severity)
-{
-       pci_ers_result_t status, result = PCI_ERS_RESULT_RECOVERED;
-       enum pci_channel_state state;
-
-       if (severity == AER_FATAL)
-               state = pci_channel_io_frozen;
-       else
-               state = pci_channel_io_normal;
-
-       status = broadcast_error_message(dev,
-                       state,
-                       "error_detected",
-                       report_error_detected);
-
-       if (severity == AER_FATAL) {
-               result = reset_link(dev);
-               if (result != PCI_ERS_RESULT_RECOVERED)
-                       goto failed;
-       }
-
-       if (status == PCI_ERS_RESULT_CAN_RECOVER)
-               status = broadcast_error_message(dev,
-                               state,
-                               "mmio_enabled",
-                               report_mmio_enabled);
-
-       if (status == PCI_ERS_RESULT_NEED_RESET) {
-               /*
-                * TODO: Should call platform-specific
-                * functions to reset slot before calling
-                * drivers' slot_reset callbacks?
-                */
-               status = broadcast_error_message(dev,
-                               state,
-                               "slot_reset",
-                               report_slot_reset);
-       }
-
-       if (status != PCI_ERS_RESULT_RECOVERED)
-               goto failed;
-
-       broadcast_error_message(dev,
-                               state,
-                               "resume",
-                               report_resume);
-
-       pci_info(dev, "AER: Device recovery successful\n");
-       return;
-
-failed:
-       pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT);
-       /* TODO: Should kernel panic here? */
-       pci_info(dev, "AER: Device recovery failed\n");
-}
-
 /**
  * handle_error_source - handle logging error into an event log
- * @aerdev: pointer to pcie_device data structure of the root port
  * @dev: pointer to pci_dev data structure of error source device
  * @info: comprehensive error information
  *
  * Invoked when an error being detected by Root Port.
  */
-static void handle_error_source(struct pcie_device *aerdev,
-       struct pci_dev *dev,
-       struct aer_err_info *info)
+static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
 {
        int pos;
 
@@ -562,12 +248,13 @@ static void handle_error_source(struct pcie_device *aerdev,
                if (pos)
                        pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS,
                                        info->status);
-       } else
-               do_recovery(dev, info->severity);
+       } else if (info->severity == AER_NONFATAL)
+               pcie_do_nonfatal_recovery(dev);
+       else if (info->severity == AER_FATAL)
+               pcie_do_fatal_recovery(dev, PCIE_PORT_SERVICE_AER);
 }
 
 #ifdef CONFIG_ACPI_APEI_PCIEAER
-static void aer_recover_work_func(struct work_struct *work);
 
 #define AER_RECOVER_RING_ORDER         4
 #define AER_RECOVER_RING_SIZE          (1 << AER_RECOVER_RING_ORDER)
@@ -582,6 +269,30 @@ struct aer_recover_entry {
 
 static DEFINE_KFIFO(aer_recover_ring, struct aer_recover_entry,
                    AER_RECOVER_RING_SIZE);
+
+static void aer_recover_work_func(struct work_struct *work)
+{
+       struct aer_recover_entry entry;
+       struct pci_dev *pdev;
+
+       while (kfifo_get(&aer_recover_ring, &entry)) {
+               pdev = pci_get_domain_bus_and_slot(entry.domain, entry.bus,
+                                                  entry.devfn);
+               if (!pdev) {
+                       pr_err("AER recover: Can not find pci_dev for %04x:%02x:%02x:%x\n",
+                              entry.domain, entry.bus,
+                              PCI_SLOT(entry.devfn), PCI_FUNC(entry.devfn));
+                       continue;
+               }
+               cper_print_aer(pdev, entry.severity, entry.regs);
+               if (entry.severity == AER_NONFATAL)
+                       pcie_do_nonfatal_recovery(pdev);
+               else if (entry.severity == AER_FATAL)
+                       pcie_do_fatal_recovery(pdev, PCIE_PORT_SERVICE_AER);
+               pci_dev_put(pdev);
+       }
+}
+
 /*
  * Mutual exclusion for writers of aer_recover_ring, reader side don't
  * need lock, because there is only one reader and lock is not needed
@@ -611,27 +322,6 @@ void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
        spin_unlock_irqrestore(&aer_recover_ring_lock, flags);
 }
 EXPORT_SYMBOL_GPL(aer_recover_queue);
-
-static void aer_recover_work_func(struct work_struct *work)
-{
-       struct aer_recover_entry entry;
-       struct pci_dev *pdev;
-
-       while (kfifo_get(&aer_recover_ring, &entry)) {
-               pdev = pci_get_domain_bus_and_slot(entry.domain, entry.bus,
-                                                  entry.devfn);
-               if (!pdev) {
-                       pr_err("AER recover: Can not find pci_dev for %04x:%02x:%02x:%x\n",
-                              entry.domain, entry.bus,
-                              PCI_SLOT(entry.devfn), PCI_FUNC(entry.devfn));
-                       continue;
-               }
-               cper_print_aer(pdev, entry.severity, entry.regs);
-               if (entry.severity != AER_CORRECTABLE)
-                       do_recovery(pdev, entry.severity);
-               pci_dev_put(pdev);
-       }
-}
 #endif
 
 /**
@@ -695,8 +385,7 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
        return 1;
 }
 
-static inline void aer_process_err_devices(struct pcie_device *p_device,
-                       struct aer_err_info *e_info)
+static inline void aer_process_err_devices(struct aer_err_info *e_info)
 {
        int i;
 
@@ -707,19 +396,19 @@ static inline void aer_process_err_devices(struct pcie_device *p_device,
        }
        for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) {
                if (get_device_error_info(e_info->dev[i], e_info))
-                       handle_error_source(p_device, e_info->dev[i], e_info);
+                       handle_error_source(e_info->dev[i], e_info);
        }
 }
 
 /**
  * aer_isr_one_error - consume an error detected by root port
- * @p_device: pointer to error root port service device
+ * @rpc: pointer to the root port which holds an error
  * @e_src: pointer to an error source
  */
-static void aer_isr_one_error(struct pcie_device *p_device,
+static void aer_isr_one_error(struct aer_rpc *rpc,
                struct aer_err_source *e_src)
 {
-       struct aer_rpc *rpc = get_service_data(p_device);
+       struct pci_dev *pdev = rpc->rpd;
        struct aer_err_info *e_info = &rpc->e_info;
 
        /*
@@ -734,11 +423,10 @@ static void aer_isr_one_error(struct pcie_device *p_device,
                        e_info->multi_error_valid = 1;
                else
                        e_info->multi_error_valid = 0;
+               aer_print_port_info(pdev, e_info);
 
-               aer_print_port_info(p_device->port, e_info);
-
-               if (find_source_device(p_device->port, e_info))
-                       aer_process_err_devices(p_device, e_info);
+               if (find_source_device(pdev, e_info))
+                       aer_process_err_devices(e_info);
        }
 
        if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) {
@@ -754,10 +442,10 @@ static void aer_isr_one_error(struct pcie_device *p_device,
                else
                        e_info->multi_error_valid = 0;
 
-               aer_print_port_info(p_device->port, e_info);
+               aer_print_port_info(pdev, e_info);
 
-               if (find_source_device(p_device->port, e_info))
-                       aer_process_err_devices(p_device, e_info);
+               if (find_source_device(pdev, e_info))
+                       aer_process_err_devices(e_info);
        }
 }
 
@@ -799,11 +487,10 @@ static int get_e_source(struct aer_rpc *rpc, struct aer_err_source *e_src)
 void aer_isr(struct work_struct *work)
 {
        struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler);
-       struct pcie_device *p_device = rpc->rpd;
        struct aer_err_source uninitialized_var(e_src);
 
        mutex_lock(&rpc->rpc_mutex);
        while (get_e_source(rpc, &e_src))
-               aer_isr_one_error(p_device, &e_src);
+               aer_isr_one_error(rpc, &e_src);
        mutex_unlock(&rpc->rpc_mutex);
 }
index cfc89dd578316dc5f32db14e5c302469c4aaf055..4985bdf64c2e75c3711b5d533519d3bbdf32930a 100644 (file)
@@ -163,17 +163,17 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
        int id = ((dev->bus->number << 8) | dev->devfn);
 
        if (!info->status) {
-               pci_err(dev, "PCIe Bus Error: severity=%s, type=Unaccessible, id=%04x(Unregistered Agent ID)\n",
-                       aer_error_severity_string[info->severity], id);
+               pci_err(dev, "PCIe Bus Error: severity=%s, type=Inaccessible, (Unregistered Agent ID)\n",
+                       aer_error_severity_string[info->severity]);
                goto out;
        }
 
        layer = AER_GET_LAYER_ERROR(info->severity, info->status);
        agent = AER_GET_AGENT(info->severity, info->status);
 
-       pci_err(dev, "PCIe Bus Error: severity=%s, type=%s, id=%04x(%s)\n",
+       pci_err(dev, "PCIe Bus Error: severity=%s, type=%s, (%s)\n",
                aer_error_severity_string[info->severity],
-               aer_error_layer[layer], id, aer_agent_string[agent]);
+               aer_error_layer[layer], aer_agent_string[agent]);
 
        pci_err(dev, "  device [%04x:%04x] error status/mask=%08x/%08x\n",
                dev->vendor, dev->device,
@@ -186,17 +186,21 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
 
 out:
        if (info->id && info->error_dev_num > 1 && info->id == id)
-               pci_err(dev, "  Error of this Agent(%04x) is reported first\n", id);
+               pci_err(dev, "  Error of this Agent is reported first\n");
 
        trace_aer_event(dev_name(&dev->dev), (info->status & ~info->mask),
-                       info->severity);
+                       info->severity, info->tlp_header_valid, &info->tlp);
 }
 
 void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info)
 {
-       pci_info(dev, "AER: %s%s error received: id=%04x\n",
+       u8 bus = info->id >> 8;
+       u8 devfn = info->id & 0xff;
+
+       pci_info(dev, "AER: %s%s error received: %04x:%02x:%02x.%d\n",
                info->multi_error_valid ? "Multiple " : "",
-               aer_error_severity_string[info->severity], info->id);
+               aer_error_severity_string[info->severity],
+               pci_domain_nr(dev->bus), bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
 }
 
 #ifdef CONFIG_ACPI_APEI_PCIEAER
@@ -216,28 +220,30 @@ EXPORT_SYMBOL_GPL(cper_severity_to_aer);
 void cper_print_aer(struct pci_dev *dev, int aer_severity,
                    struct aer_capability_regs *aer)
 {
-       int layer, agent, status_strs_size, tlp_header_valid = 0;
+       int layer, agent, tlp_header_valid = 0;
        u32 status, mask;
-       const char **status_strs;
+       struct aer_err_info info;
 
        if (aer_severity == AER_CORRECTABLE) {
                status = aer->cor_status;
                mask = aer->cor_mask;
-               status_strs = aer_correctable_error_string;
-               status_strs_size = ARRAY_SIZE(aer_correctable_error_string);
        } else {
                status = aer->uncor_status;
                mask = aer->uncor_mask;
-               status_strs = aer_uncorrectable_error_string;
-               status_strs_size = ARRAY_SIZE(aer_uncorrectable_error_string);
                tlp_header_valid = status & AER_LOG_TLP_MASKS;
        }
 
        layer = AER_GET_LAYER_ERROR(aer_severity, status);
        agent = AER_GET_AGENT(aer_severity, status);
 
+       memset(&info, 0, sizeof(info));
+       info.severity = aer_severity;
+       info.status = status;
+       info.mask = mask;
+       info.first_error = PCI_ERR_CAP_FEP(aer->cap_control);
+
        pci_err(dev, "aer_status: 0x%08x, aer_mask: 0x%08x\n", status, mask);
-       cper_print_bits("", status, status_strs, status_strs_size);
+       __aer_print_error(dev, &info);
        pci_err(dev, "aer_layer=%s, aer_agent=%s\n",
                aer_error_layer[layer], aer_agent_string[agent]);
 
@@ -249,6 +255,6 @@ void cper_print_aer(struct pci_dev *dev, int aer_severity,
                __print_tlp_header(dev, &aer->header_log);
 
        trace_aer_event(dev_name(&dev->dev), (status & ~mask),
-                       aer_severity);
+                       aer_severity, tlp_header_valid, &aer->header_log);
 }
 #endif
index f76eb7704f646853bb33e03e4d7719a695ea65c8..c687c817b47d0cd6cb2d5d896f08c816048a03a8 100644 (file)
@@ -400,6 +400,15 @@ static void pcie_get_aspm_reg(struct pci_dev *pdev,
                info->l1ss_cap = 0;
                return;
        }
+
+       /*
+        * If we don't have LTR for the entire path from the Root Complex
+        * to this device, we can't use ASPM L1.2 because it relies on the
+        * LTR_L1.2_THRESHOLD.  See PCIe r4.0, secs 5.5.4, 6.18.
+        */
+       if (!pdev->ltr_path)
+               info->l1ss_cap &= ~PCI_L1SS_CAP_ASPM_L1_2;
+
        pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CTL1,
                              &info->l1ss_ctl1);
        pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CTL2,
index 8c57d607e603e3e3c56d678e0ab7ec9095e1693f..d6436681c5354110f5341a376581ad27668aa925 100644 (file)
@@ -68,44 +68,35 @@ static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
 
 static void dpc_wait_link_inactive(struct dpc_dev *dpc)
 {
-       unsigned long timeout = jiffies + HZ;
        struct pci_dev *pdev = dpc->dev->port;
-       struct device *dev = &dpc->dev->device;
-       u16 lnk_status;
 
-       pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
-       while (lnk_status & PCI_EXP_LNKSTA_DLLLA &&
-                                       !time_after(jiffies, timeout)) {
-               msleep(10);
-               pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
-       }
-       if (lnk_status & PCI_EXP_LNKSTA_DLLLA)
-               dev_warn(dev, "Link state not disabled for DPC event\n");
+       pcie_wait_for_link(pdev, false);
 }
 
-static void dpc_work(struct work_struct *work)
+static pci_ers_result_t dpc_reset_link(struct pci_dev *pdev)
 {
-       struct dpc_dev *dpc = container_of(work, struct dpc_dev, work);
-       struct pci_dev *dev, *temp, *pdev = dpc->dev->port;
-       struct pci_bus *parent = pdev->subordinate;
-       u16 cap = dpc->cap_pos, ctl;
-
-       pci_lock_rescan_remove();
-       list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
-                                        bus_list) {
-               pci_dev_get(dev);
-               pci_dev_set_disconnected(dev, NULL);
-               if (pci_has_subordinate(dev))
-                       pci_walk_bus(dev->subordinate,
-                                    pci_dev_set_disconnected, NULL);
-               pci_stop_and_remove_bus_device(dev);
-               pci_dev_put(dev);
-       }
-       pci_unlock_rescan_remove();
-
+       struct dpc_dev *dpc;
+       struct pcie_device *pciedev;
+       struct device *devdpc;
+       u16 cap, ctl;
+
+       /*
+        * DPC disables the Link automatically in hardware, so it has
+        * already been reset by the time we get here.
+        */
+       devdpc = pcie_port_find_device(pdev, PCIE_PORT_SERVICE_DPC);
+       pciedev = to_pcie_device(devdpc);
+       dpc = get_service_data(pciedev);
+       cap = dpc->cap_pos;
+
+       /*
+        * Wait until the Link is inactive, then clear DPC Trigger Status
+        * to allow the Port to leave DPC.
+        */
        dpc_wait_link_inactive(dpc);
+
        if (dpc->rp_extensions && dpc_wait_rp_inactive(dpc))
-               return;
+               return PCI_ERS_RESULT_DISCONNECT;
        if (dpc->rp_extensions && dpc->rp_pio_status) {
                pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS,
                                       dpc->rp_pio_status);
@@ -113,11 +104,22 @@ static void dpc_work(struct work_struct *work)
        }
 
        pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
-               PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT);
+                             PCI_EXP_DPC_STATUS_TRIGGER);
 
        pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl);
        pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL,
                              ctl | PCI_EXP_DPC_CTL_INT_EN);
+
+       return PCI_ERS_RESULT_RECOVERED;
+}
+
+static void dpc_work(struct work_struct *work)
+{
+       struct dpc_dev *dpc = container_of(work, struct dpc_dev, work);
+       struct pci_dev *pdev = dpc->dev->port;
+
+       /* We configure DPC so it only triggers on ERR_FATAL */
+       pcie_do_fatal_recovery(pdev, PCIE_PORT_SERVICE_DPC);
 }
 
 static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
@@ -223,6 +225,9 @@ static irqreturn_t dpc_irq(int irq, void *context)
        if (dpc->rp_extensions && reason == 3 && ext_reason == 0)
                dpc_process_rp_pio_error(dpc);
 
+       pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
+                             PCI_EXP_DPC_STATUS_INTERRUPT);
+
        schedule_work(&dpc->work);
 
        return IRQ_HANDLED;
@@ -270,7 +275,7 @@ static int dpc_probe(struct pcie_device *dev)
                }
        }
 
-       ctl = (ctl & 0xfff4) | PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN;
+       ctl = (ctl & 0xfff4) | PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN;
        pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
 
        dev_info(device, "DPC error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n",
@@ -288,7 +293,7 @@ static void dpc_remove(struct pcie_device *dev)
        u16 ctl;
 
        pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
-       ctl &= ~(PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN);
+       ctl &= ~(PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN);
        pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
 }
 
@@ -298,6 +303,7 @@ static struct pcie_port_service_driver dpcdriver = {
        .service        = PCIE_PORT_SERVICE_DPC,
        .probe          = dpc_probe,
        .remove         = dpc_remove,
+       .reset_link     = dpc_reset_link,
 };
 
 static int __init dpc_service_init(void)
diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c
new file mode 100644 (file)
index 0000000..f7ce0cb
--- /dev/null
@@ -0,0 +1,388 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file implements the error recovery as a core part of PCIe error
+ * reporting. When a PCIe error is delivered, an error message will be
+ * collected and printed to console, then, an error recovery procedure
+ * will be executed by following the PCI error recovery rules.
+ *
+ * Copyright (C) 2006 Intel Corp.
+ *     Tom Long Nguyen (tom.l.nguyen@intel.com)
+ *     Zhang Yanmin (yanmin.zhang@intel.com)
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/aer.h>
+#include "portdrv.h"
+#include "../pci.h"
+
+struct aer_broadcast_data {
+       enum pci_channel_state state;
+       enum pci_ers_result result;
+};
+
+static pci_ers_result_t merge_result(enum pci_ers_result orig,
+                                 enum pci_ers_result new)
+{
+       if (new == PCI_ERS_RESULT_NO_AER_DRIVER)
+               return PCI_ERS_RESULT_NO_AER_DRIVER;
+
+       if (new == PCI_ERS_RESULT_NONE)
+               return orig;
+
+       switch (orig) {
+       case PCI_ERS_RESULT_CAN_RECOVER:
+       case PCI_ERS_RESULT_RECOVERED:
+               orig = new;
+               break;
+       case PCI_ERS_RESULT_DISCONNECT:
+               if (new == PCI_ERS_RESULT_NEED_RESET)
+                       orig = PCI_ERS_RESULT_NEED_RESET;
+               break;
+       default:
+               break;
+       }
+
+       return orig;
+}
+
+static int report_error_detected(struct pci_dev *dev, void *data)
+{
+       pci_ers_result_t vote;
+       const struct pci_error_handlers *err_handler;
+       struct aer_broadcast_data *result_data;
+
+       result_data = (struct aer_broadcast_data *) data;
+
+       device_lock(&dev->dev);
+       dev->error_state = result_data->state;
+
+       if (!dev->driver ||
+               !dev->driver->err_handler ||
+               !dev->driver->err_handler->error_detected) {
+               if (result_data->state == pci_channel_io_frozen &&
+                       dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
+                       /*
+                        * In case of fatal recovery, if one of down-
+                        * stream device has no driver. We might be
+                        * unable to recover because a later insmod
+                        * of a driver for this device is unaware of
+                        * its hw state.
+                        */
+                       pci_printk(KERN_DEBUG, dev, "device has %s\n",
+                                  dev->driver ?
+                                  "no AER-aware driver" : "no driver");
+               }
+
+               /*
+                * If there's any device in the subtree that does not
+                * have an error_detected callback, returning
+                * PCI_ERS_RESULT_NO_AER_DRIVER prevents calling of
+                * the subsequent mmio_enabled/slot_reset/resume
+                * callbacks of "any" device in the subtree. All the
+                * devices in the subtree are left in the error state
+                * without recovery.
+                */
+
+               if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE)
+                       vote = PCI_ERS_RESULT_NO_AER_DRIVER;
+               else
+                       vote = PCI_ERS_RESULT_NONE;
+       } else {
+               err_handler = dev->driver->err_handler;
+               vote = err_handler->error_detected(dev, result_data->state);
+               pci_uevent_ers(dev, PCI_ERS_RESULT_NONE);
+       }
+
+       result_data->result = merge_result(result_data->result, vote);
+       device_unlock(&dev->dev);
+       return 0;
+}
+
+static int report_mmio_enabled(struct pci_dev *dev, void *data)
+{
+       pci_ers_result_t vote;
+       const struct pci_error_handlers *err_handler;
+       struct aer_broadcast_data *result_data;
+
+       result_data = (struct aer_broadcast_data *) data;
+
+       device_lock(&dev->dev);
+       if (!dev->driver ||
+               !dev->driver->err_handler ||
+               !dev->driver->err_handler->mmio_enabled)
+               goto out;
+
+       err_handler = dev->driver->err_handler;
+       vote = err_handler->mmio_enabled(dev);
+       result_data->result = merge_result(result_data->result, vote);
+out:
+       device_unlock(&dev->dev);
+       return 0;
+}
+
+static int report_slot_reset(struct pci_dev *dev, void *data)
+{
+       pci_ers_result_t vote;
+       const struct pci_error_handlers *err_handler;
+       struct aer_broadcast_data *result_data;
+
+       result_data = (struct aer_broadcast_data *) data;
+
+       device_lock(&dev->dev);
+       if (!dev->driver ||
+               !dev->driver->err_handler ||
+               !dev->driver->err_handler->slot_reset)
+               goto out;
+
+       err_handler = dev->driver->err_handler;
+       vote = err_handler->slot_reset(dev);
+       result_data->result = merge_result(result_data->result, vote);
+out:
+       device_unlock(&dev->dev);
+       return 0;
+}
+
+static int report_resume(struct pci_dev *dev, void *data)
+{
+       const struct pci_error_handlers *err_handler;
+
+       device_lock(&dev->dev);
+       dev->error_state = pci_channel_io_normal;
+
+       if (!dev->driver ||
+               !dev->driver->err_handler ||
+               !dev->driver->err_handler->resume)
+               goto out;
+
+       err_handler = dev->driver->err_handler;
+       err_handler->resume(dev);
+       pci_uevent_ers(dev, PCI_ERS_RESULT_RECOVERED);
+out:
+       device_unlock(&dev->dev);
+       return 0;
+}
+
+/**
+ * default_reset_link - default reset function
+ * @dev: pointer to pci_dev data structure
+ *
+ * Invoked when performing link reset on a Downstream Port or a
+ * Root Port with no aer driver.
+ */
+static pci_ers_result_t default_reset_link(struct pci_dev *dev)
+{
+       pci_reset_bridge_secondary_bus(dev);
+       pci_printk(KERN_DEBUG, dev, "downstream link has been reset\n");
+       return PCI_ERS_RESULT_RECOVERED;
+}
+
+static pci_ers_result_t reset_link(struct pci_dev *dev, u32 service)
+{
+       struct pci_dev *udev;
+       pci_ers_result_t status;
+       struct pcie_port_service_driver *driver = NULL;
+
+       if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+               /* Reset this port for all subordinates */
+               udev = dev;
+       } else {
+               /* Reset the upstream component (likely downstream port) */
+               udev = dev->bus->self;
+       }
+
+       /* Use the aer driver of the component firstly */
+       driver = pcie_port_find_service(udev, service);
+
+       if (driver && driver->reset_link) {
+               status = driver->reset_link(udev);
+       } else if (udev->has_secondary_link) {
+               status = default_reset_link(udev);
+       } else {
+               pci_printk(KERN_DEBUG, dev, "no link-reset support at upstream device %s\n",
+                       pci_name(udev));
+               return PCI_ERS_RESULT_DISCONNECT;
+       }
+
+       if (status != PCI_ERS_RESULT_RECOVERED) {
+               pci_printk(KERN_DEBUG, dev, "link reset at upstream device %s failed\n",
+                       pci_name(udev));
+               return PCI_ERS_RESULT_DISCONNECT;
+       }
+
+       return status;
+}
+
+/**
+ * broadcast_error_message - handle message broadcast to downstream drivers
+ * @dev: pointer to from where in a hierarchy message is broadcasted down
+ * @state: error state
+ * @error_mesg: message to print
+ * @cb: callback to be broadcasted
+ *
+ * Invoked during error recovery process. Once being invoked, the content
+ * of error severity will be broadcasted to all downstream drivers in a
+ * hierarchy in question.
+ */
+static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
+       enum pci_channel_state state,
+       char *error_mesg,
+       int (*cb)(struct pci_dev *, void *))
+{
+       struct aer_broadcast_data result_data;
+
+       pci_printk(KERN_DEBUG, dev, "broadcast %s message\n", error_mesg);
+       result_data.state = state;
+       if (cb == report_error_detected)
+               result_data.result = PCI_ERS_RESULT_CAN_RECOVER;
+       else
+               result_data.result = PCI_ERS_RESULT_RECOVERED;
+
+       if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+               /*
+                * If the error is reported by a bridge, we think this error
+                * is related to the downstream link of the bridge, so we
+                * do error recovery on all subordinates of the bridge instead
+                * of the bridge and clear the error status of the bridge.
+                */
+               if (cb == report_error_detected)
+                       dev->error_state = state;
+               pci_walk_bus(dev->subordinate, cb, &result_data);
+               if (cb == report_resume) {
+                       pci_cleanup_aer_uncorrect_error_status(dev);
+                       dev->error_state = pci_channel_io_normal;
+               }
+       } else {
+               /*
+                * If the error is reported by an end point, we think this
+                * error is related to the upstream link of the end point.
+                */
+               if (state == pci_channel_io_normal)
+                       /*
+                        * the error is non fatal so the bus is ok, just invoke
+                        * the callback for the function that logged the error.
+                        */
+                       cb(dev, &result_data);
+               else
+                       pci_walk_bus(dev->bus, cb, &result_data);
+       }
+
+       return result_data.result;
+}
+
+/**
+ * pcie_do_fatal_recovery - handle fatal error recovery process
+ * @dev: pointer to a pci_dev data structure of agent detecting an error
+ *
+ * Invoked when an error is fatal. Once being invoked, removes the devices
+ * beneath this AER agent, followed by reset link e.g. secondary bus reset
+ * followed by re-enumeration of devices.
+ */
+void pcie_do_fatal_recovery(struct pci_dev *dev, u32 service)
+{
+       struct pci_dev *udev;
+       struct pci_bus *parent;
+       struct pci_dev *pdev, *temp;
+       pci_ers_result_t result;
+
+       if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
+               udev = dev;
+       else
+               udev = dev->bus->self;
+
+       parent = udev->subordinate;
+       pci_lock_rescan_remove();
+       list_for_each_entry_safe_reverse(pdev, temp, &parent->devices,
+                                        bus_list) {
+               pci_dev_get(pdev);
+               pci_dev_set_disconnected(pdev, NULL);
+               if (pci_has_subordinate(pdev))
+                       pci_walk_bus(pdev->subordinate,
+                                    pci_dev_set_disconnected, NULL);
+               pci_stop_and_remove_bus_device(pdev);
+               pci_dev_put(pdev);
+       }
+
+       result = reset_link(udev, service);
+
+       if ((service == PCIE_PORT_SERVICE_AER) &&
+           (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) {
+               /*
+                * If the error is reported by a bridge, we think this error
+                * is related to the downstream link of the bridge, so we
+                * do error recovery on all subordinates of the bridge instead
+                * of the bridge and clear the error status of the bridge.
+                */
+               pci_cleanup_aer_uncorrect_error_status(dev);
+       }
+
+       if (result == PCI_ERS_RESULT_RECOVERED) {
+               if (pcie_wait_for_link(udev, true))
+                       pci_rescan_bus(udev->bus);
+               pci_info(dev, "Device recovery from fatal error successful\n");
+       } else {
+               pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT);
+               pci_info(dev, "Device recovery from fatal error failed\n");
+       }
+
+       pci_unlock_rescan_remove();
+}
+
+/**
+ * pcie_do_nonfatal_recovery - handle nonfatal error recovery process
+ * @dev: pointer to a pci_dev data structure of agent detecting an error
+ *
+ * Invoked when an error is nonfatal/fatal. Once being invoked, broadcast
+ * error detected message to all downstream drivers within a hierarchy in
+ * question and return the returned code.
+ */
+void pcie_do_nonfatal_recovery(struct pci_dev *dev)
+{
+       pci_ers_result_t status;
+       enum pci_channel_state state;
+
+       state = pci_channel_io_normal;
+
+       status = broadcast_error_message(dev,
+                       state,
+                       "error_detected",
+                       report_error_detected);
+
+       if (status == PCI_ERS_RESULT_CAN_RECOVER)
+               status = broadcast_error_message(dev,
+                               state,
+                               "mmio_enabled",
+                               report_mmio_enabled);
+
+       if (status == PCI_ERS_RESULT_NEED_RESET) {
+               /*
+                * TODO: Should call platform-specific
+                * functions to reset slot before calling
+                * drivers' slot_reset callbacks?
+                */
+               status = broadcast_error_message(dev,
+                               state,
+                               "slot_reset",
+                               report_slot_reset);
+       }
+
+       if (status != PCI_ERS_RESULT_RECOVERED)
+               goto failed;
+
+       broadcast_error_message(dev,
+                               state,
+                               "resume",
+                               report_resume);
+
+       pci_info(dev, "AER: Device recovery successful\n");
+       return;
+
+failed:
+       pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT);
+
+       /* TODO: Should kernel panic here? */
+       pci_info(dev, "AER: Device recovery failed\n");
+}
index d0c6783dbfe3307e48ef981cfa52a20908fb01ed..d82401acb831b577b35711e6abccd3e4a364b950 100644 (file)
@@ -112,4 +112,7 @@ static inline bool pcie_pme_no_msi(void) { return false; }
 static inline void pcie_pme_interrupt_enable(struct pci_dev *dev, bool en) {}
 #endif /* !CONFIG_PCIE_PME */
 
+struct pcie_port_service_driver *pcie_port_find_service(struct pci_dev *dev,
+                                                       u32 service);
+struct device *pcie_port_find_device(struct pci_dev *dev, u32 service);
 #endif /* _PORTDRV_H_ */
index c9c0663db282d8a650ad236764bdeab9b19e3090..a9c581df7a627aea8438192c5cd40602b4ccf1df 100644 (file)
 #include "../pci.h"
 #include "portdrv.h"
 
+struct portdrv_service_data {
+       struct pcie_port_service_driver *drv;
+       struct device *dev;
+       u32 service;
+};
+
 /**
  * release_pcie_device - free PCI Express port service device structure
  * @dev: Port service device to release
@@ -398,6 +404,69 @@ static int remove_iter(struct device *dev, void *data)
        return 0;
 }
 
+static int find_service_iter(struct device *device, void *data)
+{
+       struct pcie_port_service_driver *service_driver;
+       struct portdrv_service_data *pdrvs;
+       u32 service;
+
+       pdrvs = (struct portdrv_service_data *) data;
+       service = pdrvs->service;
+
+       if (device->bus == &pcie_port_bus_type && device->driver) {
+               service_driver = to_service_driver(device->driver);
+               if (service_driver->service == service) {
+                       pdrvs->drv = service_driver;
+                       pdrvs->dev = device;
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * pcie_port_find_service - find the service driver
+ * @dev: PCI Express port the service is associated with
+ * @service: Service to find
+ *
+ * Find PCI Express port service driver associated with given service
+ */
+struct pcie_port_service_driver *pcie_port_find_service(struct pci_dev *dev,
+                                                       u32 service)
+{
+       struct pcie_port_service_driver *drv;
+       struct portdrv_service_data pdrvs;
+
+       pdrvs.drv = NULL;
+       pdrvs.service = service;
+       device_for_each_child(&dev->dev, &pdrvs, find_service_iter);
+
+       drv = pdrvs.drv;
+       return drv;
+}
+
+/**
+ * pcie_port_find_device - find the struct device
+ * @dev: PCI Express port the service is associated with
+ * @service: For the service to find
+ *
+ * Find the struct device associated with given service on a pci_dev
+ */
+struct device *pcie_port_find_device(struct pci_dev *dev,
+                                     u32 service)
+{
+       struct device *device;
+       struct portdrv_service_data pdrvs;
+
+       pdrvs.dev = NULL;
+       pdrvs.service = service;
+       device_for_each_child(&dev->dev, &pdrvs, find_service_iter);
+
+       device = pdrvs.dev;
+       return device;
+}
+
 /**
  * pcie_port_device_remove - unregister PCI Express port service devices
  * @dev: PCI Express port the service devices to unregister are associated with
index dcc41de1c2c89e1af1b6f93ea481728963f960da..4fd402946b43249c48f5aa237ecbc2a2125b72b6 100644 (file)
@@ -554,6 +554,7 @@ struct pci_host_bridge *pci_alloc_host_bridge(size_t priv)
        bridge->native_aer = 1;
        bridge->native_hotplug = 1;
        bridge->native_pme = 1;
+       bridge->native_ltr = 1;
 
        return bridge;
 }
@@ -2006,9 +2007,13 @@ static void pci_configure_relaxed_ordering(struct pci_dev *dev)
 static void pci_configure_ltr(struct pci_dev *dev)
 {
 #ifdef CONFIG_PCIEASPM
+       struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
        u32 cap;
        struct pci_dev *bridge;
 
+       if (!host->native_ltr)
+               return;
+
        if (!pci_is_pcie(dev))
                return;
 
index 15bfb15c2fa5e1c369184c3839aa2131a416b0bd..49f63c67a9d140e1dbc2b1b1be60b25982cc8958 100644 (file)
@@ -506,7 +506,8 @@ extern bool osc_pc_lpi_support_confirmed;
 #define OSC_PCI_EXPRESS_PME_CONTROL            0x00000004
 #define OSC_PCI_EXPRESS_AER_CONTROL            0x00000008
 #define OSC_PCI_EXPRESS_CAPABILITY_CONTROL     0x00000010
-#define OSC_PCI_CONTROL_MASKS                  0x0000001f
+#define OSC_PCI_EXPRESS_LTR_CONTROL            0x00000020
+#define OSC_PCI_CONTROL_MASKS                  0x0000003f
 
 #define ACPI_GSB_ACCESS_ATTRIB_QUICK           0x00000002
 #define ACPI_GSB_ACCESS_ATTRIB_SEND_RCV         0x00000004
index 8f87bbeceef41948fd0dc22cf16db22534b04e71..514bffa11dbbd713ac765623299e973d2851efd7 100644 (file)
@@ -14,6 +14,7 @@
 #define AER_NONFATAL                   0
 #define AER_FATAL                      1
 #define AER_CORRECTABLE                        2
+#define DPC_FATAL                      3
 
 struct pci_dev;
 
index f7aa6d9f8999a71cf7317f872b5b61cfb2858b47..2619ab35dae9d448e99ebb52c91c4c9572a41c5d 100644 (file)
@@ -474,6 +474,7 @@ struct pci_host_bridge {
        unsigned int    native_aer:1;           /* OS may use PCIe AER */
        unsigned int    native_hotplug:1;       /* OS may use PCIe hotplug */
        unsigned int    native_pme:1;           /* OS may use PCIe PME */
+       unsigned int    native_ltr:1;           /* OS may use PCIe LTR */
        /* Resource alignment requirements */
        resource_size_t (*align_resource)(struct pci_dev *dev,
                        const struct resource *res,
@@ -2283,7 +2284,7 @@ static inline bool pci_is_thunderbolt_attached(struct pci_dev *pdev)
        return false;
 }
 
-#if defined(CONFIG_PCIEAER) || defined(CONFIG_EEH)
+#if defined(CONFIG_PCIEPORTBUS) || defined(CONFIG_EEH)
 void pci_uevent_ers(struct pci_dev *pdev, enum  pci_ers_result err_type);
 #endif
 
index 9c689868eb4d90869f9400a973f6b7005a0fd927..a0794632fd01a4651942a326aecbaddaab5a93fa 100644 (file)
@@ -298,30 +298,44 @@ TRACE_EVENT(non_standard_event,
 TRACE_EVENT(aer_event,
        TP_PROTO(const char *dev_name,
                 const u32 status,
-                const u8 severity),
+                const u8 severity,
+                const u8 tlp_header_valid,
+                struct aer_header_log_regs *tlp),
 
-       TP_ARGS(dev_name, status, severity),
+       TP_ARGS(dev_name, status, severity, tlp_header_valid, tlp),
 
        TP_STRUCT__entry(
                __string(       dev_name,       dev_name        )
                __field(        u32,            status          )
                __field(        u8,             severity        )
+               __field(        u8,             tlp_header_valid)
+               __array(        u32,            tlp_header, 4   )
        ),
 
        TP_fast_assign(
                __assign_str(dev_name, dev_name);
                __entry->status         = status;
                __entry->severity       = severity;
+               __entry->tlp_header_valid = tlp_header_valid;
+               if (tlp_header_valid) {
+                       __entry->tlp_header[0] = tlp->dw0;
+                       __entry->tlp_header[1] = tlp->dw1;
+                       __entry->tlp_header[2] = tlp->dw2;
+                       __entry->tlp_header[3] = tlp->dw3;
+               }
        ),
 
-       TP_printk("%s PCIe Bus Error: severity=%s, %s\n",
+       TP_printk("%s PCIe Bus Error: severity=%s, %s, TLP Header=%s\n",
                __get_str(dev_name),
                __entry->severity == AER_CORRECTABLE ? "Corrected" :
                        __entry->severity == AER_FATAL ?
                        "Fatal" : "Uncorrected, non-fatal",
                __entry->severity == AER_CORRECTABLE ?
                __print_flags(__entry->status, "|", aer_correctable_errors) :
-               __print_flags(__entry->status, "|", aer_uncorrectable_errors))
+               __print_flags(__entry->status, "|", aer_uncorrectable_errors),
+               __entry->tlp_header_valid ?
+                       __print_array(__entry->tlp_header, 4, 4) :
+                       "Not available")
 );
 
 /*
index 103ba797a8f31ea0dbaf783df576a5b14c0fe4be..5182e0dda0831e5b9dd0daeabe839f5a83cb3112 100644 (file)
 #define  PCI_EXP_DPC_CAP_DL_ACTIVE     0x1000  /* ERR_COR signal on DL_Active supported */
 
 #define PCI_EXP_DPC_CTL                        6       /* DPC control */
+#define  PCI_EXP_DPC_CTL_EN_FATAL      0x0001  /* Enable trigger on ERR_FATAL message */
 #define  PCI_EXP_DPC_CTL_EN_NONFATAL   0x0002  /* Enable trigger on ERR_NONFATAL message */
 #define  PCI_EXP_DPC_CTL_INT_EN        0x0008  /* DPC Interrupt Enable */