|
@@ -24,6 +24,7 @@
|
|
|
#include <linux/suspend.h>
|
|
|
#include <linux/delay.h>
|
|
|
#include <linux/slab.h>
|
|
|
+#include <linux/kfifo.h>
|
|
|
#include "aerdrv.h"
|
|
|
|
|
|
static int forceload;
|
|
@@ -445,8 +446,7 @@ static struct pcie_port_service_driver *find_aer_service(struct pci_dev *dev)
|
|
|
return drv;
|
|
|
}
|
|
|
|
|
|
-static pci_ers_result_t reset_link(struct pcie_device *aerdev,
|
|
|
- struct pci_dev *dev)
|
|
|
+static pci_ers_result_t reset_link(struct pci_dev *dev)
|
|
|
{
|
|
|
struct pci_dev *udev;
|
|
|
pci_ers_result_t status;
|
|
@@ -486,7 +486,6 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev,
|
|
|
|
|
|
/**
|
|
|
* do_recovery - handle nonfatal/fatal error recovery process
|
|
|
- * @aerdev: pointer to a pcie_device data structure of root port
|
|
|
* @dev: pointer to a pci_dev data structure of agent detecting an error
|
|
|
* @severity: error severity type
|
|
|
*
|
|
@@ -494,8 +493,7 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev,
|
|
|
* error detected message to all downstream drivers within a hierarchy in
|
|
|
* question and return the returned code.
|
|
|
*/
|
|
|
-static void do_recovery(struct pcie_device *aerdev, struct pci_dev *dev,
|
|
|
- int severity)
|
|
|
+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;
|
|
@@ -511,7 +509,7 @@ static void do_recovery(struct pcie_device *aerdev, struct pci_dev *dev,
|
|
|
report_error_detected);
|
|
|
|
|
|
if (severity == AER_FATAL) {
|
|
|
- result = reset_link(aerdev, dev);
|
|
|
+ result = reset_link(dev);
|
|
|
if (result != PCI_ERS_RESULT_RECOVERED)
|
|
|
goto failed;
|
|
|
}
|
|
@@ -576,9 +574,73 @@ static void handle_error_source(struct pcie_device *aerdev,
|
|
|
pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS,
|
|
|
info->status);
|
|
|
} else
|
|
|
- do_recovery(aerdev, dev, info->severity);
|
|
|
+ do_recovery(dev, info->severity);
|
|
|
}
|
|
|
|
|
|
+#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)
|
|
|
+
|
|
|
+struct aer_recover_entry
|
|
|
+{
|
|
|
+ u8 bus;
|
|
|
+ u8 devfn;
|
|
|
+ u16 domain;
|
|
|
+ int severity;
|
|
|
+};
|
|
|
+
|
|
|
+static DEFINE_KFIFO(aer_recover_ring, struct aer_recover_entry,
|
|
|
+ AER_RECOVER_RING_SIZE);
|
|
|
+/*
|
|
|
+ * 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
|
|
|
+ * between reader and writer.
|
|
|
+ */
|
|
|
+static DEFINE_SPINLOCK(aer_recover_ring_lock);
|
|
|
+static DECLARE_WORK(aer_recover_work, aer_recover_work_func);
|
|
|
+
|
|
|
+void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
|
|
|
+ int severity)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+ struct aer_recover_entry entry = {
|
|
|
+ .bus = bus,
|
|
|
+ .devfn = devfn,
|
|
|
+ .domain = domain,
|
|
|
+ .severity = severity,
|
|
|
+ };
|
|
|
+
|
|
|
+ spin_lock_irqsave(&aer_recover_ring_lock, flags);
|
|
|
+ if (kfifo_put(&aer_recover_ring, &entry))
|
|
|
+ schedule_work(&aer_recover_work);
|
|
|
+ else
|
|
|
+ pr_err("AER recover: Buffer overflow when recovering AER for %04x:%02x:%02x:%x\n",
|
|
|
+ domain, bus, PCI_SLOT(devfn), PCI_FUNC(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;
|
|
|
+ }
|
|
|
+ do_recovery(pdev, entry.severity);
|
|
|
+ }
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
/**
|
|
|
* get_device_error_info - read error status from dev and store it to info
|
|
|
* @dev: pointer to the device expected to have a error record
|