[PATCH 13/13] powerpc/eeh: Refactor report functions
Sam Bobroff
sbobroff at linux.ibm.com
Wed May 2 16:36:56 AEST 2018
The EEH report functions now share a fair bit of code around the start
and end of each function.
So factor out as much as possible, and move the traversal into a
custom function. This also allows accurate debug to be generated more
easily.
Signed-off-by: Sam Bobroff <sbobroff at linux.ibm.com>
---
arch/powerpc/kernel/eeh_driver.c | 289 +++++++++++++++++++--------------------
1 file changed, 138 insertions(+), 151 deletions(-)
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
index eb4feee81ff4..1c4336dcf9f5 100644
--- a/arch/powerpc/kernel/eeh_driver.c
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -54,6 +54,25 @@ static int eeh_result_priority(enum pci_ers_result result)
}
};
+const char *pci_ers_result_name(enum pci_ers_result r)
+{
+ switch (r) {
+ case PCI_ERS_RESULT_NONE: return "none";
+ case PCI_ERS_RESULT_CAN_RECOVER: return "can recover";
+ case PCI_ERS_RESULT_NEED_RESET: return "need reset";
+ case PCI_ERS_RESULT_DISCONNECT: return "disconnect";
+ case PCI_ERS_RESULT_RECOVERED: return "recovered";
+ case PCI_ERS_RESULT_NO_AER_DRIVER: return "no AER driver";
+ default:
+ WARN_ONCE(1, "Unknown result type");
+ return "unknown";
+ }
+};
+
+#define eeh_infoline(EDEV, FMT, ...) \
+pr_info("EEH: PE#%x (PCI %s): " pr_fmt(FMT) "\n", EDEV->pe_config_addr, \
+((EDEV->pdev) ? dev_name(&EDEV->pdev->dev) : "NONE"), ##__VA_ARGS__)
+
static enum pci_ers_result merge_result(enum pci_ers_result old,
enum pci_ers_result new)
{
@@ -223,123 +242,118 @@ static void eeh_set_irq_state(struct eeh_pe *root, bool enable)
}
}
+static void eeh_pe_report(const char *name, struct eeh_pe *root,
+ enum pci_ers_result (*fn)(struct eeh_dev *,
+ struct pci_driver *),
+ enum pci_ers_result *result)
+{
+ struct eeh_pe *pe;
+ struct eeh_dev *edev, *tmp;
+ enum pci_ers_result new_result;
+
+ pr_info("EEH: Beginning: '%s'\n", name);
+ eeh_for_each_pe(root, pe) {
+ eeh_pe_for_each_dev(pe, edev, tmp) {
+ device_lock(&edev->pdev->dev);
+ if (eeh_edev_actionable(edev)) {
+ struct pci_driver *driver;
+
+ driver = eeh_pcid_get(edev->pdev);
+
+ if (!driver)
+ eeh_infoline(edev, "no driver");
+ else if (!driver->err_handler)
+ eeh_infoline(edev,
+ "driver not EEH aware");
+ else if (edev->mode & EEH_DEV_NO_HANDLER)
+ eeh_infoline(edev,
+ "driver bound too late");
+ else {
+ new_result = fn(edev, driver);
+ eeh_infoline(edev,
+ "%s driver reports: '%s'",
+ driver->name,
+ pci_ers_result_name(new_result));
+ if (result)
+ *result = merge_result(*result,
+ new_result);
+ }
+ if (driver)
+ eeh_pcid_put(edev->pdev);
+ } else {
+ eeh_infoline(edev, "not actionable (%d,%d,%d)",
+ !!edev->pdev,
+ !eeh_dev_removed(edev),
+ !eeh_pe_passed(edev->pe));
+ }
+ device_unlock(&edev->pdev->dev);
+ }
+ }
+ if (result)
+ pr_info("EEH: Finished:'%s' with aggregate recovery state:'%s'\n",
+ name, pci_ers_result_name(*result));
+ else
+ pr_info("EEH: Finished:'%s'", name);
+}
+
/**
* eeh_report_error - Report pci error to each device driver
- * @data: eeh device
- * @userdata: return value
+ * @edev: eeh device
+ * @driver: device's PCI driver
*
- * Report an EEH error to each device driver, collect up and
- * merge the device driver responses. Cumulative response
- * passed back in "userdata".
+ * Report an EEH error to each device driver.
*/
-static void *eeh_report_error(struct eeh_dev *edev, void *userdata)
+static enum pci_ers_result eeh_report_error(struct eeh_dev *edev,
+ struct pci_driver *driver)
{
- struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
- enum pci_ers_result rc, *res = userdata;
- struct pci_driver *driver;
+ enum pci_ers_result rc;
+ struct pci_dev *dev = edev->pdev;
- if (!eeh_edev_actionable(edev))
- return NULL;
-
- device_lock(&dev->dev);
-
- driver = eeh_pcid_get(dev);
- if (!driver) goto out_no_dev;
-
- if (!driver->err_handler ||
- !driver->err_handler->error_detected)
- goto out;
+ if (!driver->err_handler->error_detected)
+ return PCI_ERS_RESULT_NONE;
+ eeh_infoline(edev, "Invoking %s->error_detected(IO frozen)", driver->name);
rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen);
- *res = merge_result(*res, rc);
-
edev->in_error = true;
pci_uevent_ers(dev, PCI_ERS_RESULT_NONE);
-
-out:
- eeh_pcid_put(dev);
-out_no_dev:
- device_unlock(&dev->dev);
- return NULL;
+ return rc;
}
/**
* eeh_report_mmio_enabled - Tell drivers that MMIO has been enabled
- * @data: eeh device
- * @userdata: return value
+ * @edev: eeh device
+ * @driver: device's PCI driver
*
* Tells each device driver that IO ports, MMIO and config space I/O
- * are now enabled. Collects up and merges the device driver responses.
- * Cumulative response passed back in "userdata".
+ * are now enabled.
*/
-static void *eeh_report_mmio_enabled(struct eeh_dev *edev, void *userdata)
+static enum pci_ers_result eeh_report_mmio_enabled(struct eeh_dev *edev,
+ struct pci_driver *driver)
{
- struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
- enum pci_ers_result rc, *res = userdata;
- struct pci_driver *driver;
-
- if (!eeh_edev_actionable(edev))
- return NULL;
-
- device_lock(&dev->dev);
- driver = eeh_pcid_get(dev);
- if (!driver) goto out_no_dev;
-
- if (!driver->err_handler ||
- !driver->err_handler->mmio_enabled ||
- (edev->mode & EEH_DEV_NO_HANDLER))
- goto out;
-
- rc = driver->err_handler->mmio_enabled(dev);
-
- *res = merge_result(*res, rc);
-
-out:
- eeh_pcid_put(dev);
-out_no_dev:
- device_unlock(&dev->dev);
- return NULL;
+ if (!driver->err_handler->mmio_enabled)
+ return PCI_ERS_RESULT_NONE;
+ eeh_infoline(edev, "Invoking %s->mmio_enabled()", driver->name);
+ return driver->err_handler->mmio_enabled(edev->pdev);
}
/**
* eeh_report_reset - Tell device that slot has been reset
- * @data: eeh device
- * @userdata: return value
+ * @edev: eeh device
+ * @driver: device's PCI driver
*
* This routine must be called while EEH tries to reset particular
* PCI device so that the associated PCI device driver could take
* some actions, usually to save data the driver needs so that the
* driver can work again while the device is recovered.
*/
-static void *eeh_report_reset(struct eeh_dev *edev, void *userdata)
+static enum pci_ers_result eeh_report_reset(struct eeh_dev *edev,
+ struct pci_driver *driver)
{
- struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
- enum pci_ers_result rc, *res = userdata;
- struct pci_driver *driver;
-
- if (!eeh_edev_actionable(edev))
- return NULL;
-
- device_lock(&dev->dev);
-
- driver = eeh_pcid_get(dev);
- if (!driver) goto out_no_dev;
-
- if (!driver->err_handler ||
- !driver->err_handler->slot_reset ||
- (edev->mode & EEH_DEV_NO_HANDLER) ||
- (!edev->in_error))
- goto out;
-
- rc = driver->err_handler->slot_reset(dev);
- *res = merge_result(*res, rc);
-
-out:
- eeh_pcid_put(dev);
-out_no_dev:
- device_unlock(&dev->dev);
- return NULL;
+ if (!driver->err_handler->slot_reset || !edev->in_error)
+ return PCI_ERS_RESULT_NONE;
+ eeh_infoline(edev, "Invoking %s->slot_reset()", driver->name);
+ return driver->err_handler->slot_reset(edev->pdev);
}
static void *eeh_dev_restore_state(struct eeh_dev *edev, void *userdata)
@@ -372,84 +386,52 @@ static void *eeh_dev_restore_state(struct eeh_dev *edev, void *userdata)
/**
* eeh_report_resume - Tell device to resume normal operations
- * @data: eeh device
- * @userdata: return value
+ * @edev: eeh device
+ * @driver: device's PCI driver
*
* This routine must be called to notify the device driver that it
* could resume so that the device driver can do some initialization
* to make the recovered device work again.
*/
-static void *eeh_report_resume(struct eeh_dev *edev, void *userdata)
+static enum pci_ers_result eeh_report_resume(struct eeh_dev *edev,
+ struct pci_driver *driver)
{
- struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
- bool was_in_error;
- struct pci_driver *driver;
-
- if (!eeh_edev_actionable(edev))
- return NULL;
+ if (!driver->err_handler->resume || !edev->in_error)
+ return PCI_ERS_RESULT_NONE;
- device_lock(&dev->dev);
+ eeh_infoline(edev, "Invoking %s->resume()", driver->name);
+ driver->err_handler->resume(edev->pdev);
- driver = eeh_pcid_get(dev);
- if (!driver) goto out_no_dev;
-
- was_in_error = edev->in_error;
- edev->in_error = false;
-
- if (!driver->err_handler ||
- !driver->err_handler->resume ||
- (edev->mode & EEH_DEV_NO_HANDLER) || !was_in_error) {
- goto out;
- }
-
- driver->err_handler->resume(dev);
-
- pci_uevent_ers(dev, PCI_ERS_RESULT_RECOVERED);
-out:
- eeh_pcid_put(dev);
+ pci_uevent_ers(edev->pdev, PCI_ERS_RESULT_RECOVERED);
#ifdef CONFIG_PCI_IOV
if (eeh_ops->notify_resume && eeh_dev_to_pdn(edev))
eeh_ops->notify_resume(eeh_dev_to_pdn(edev));
#endif
-out_no_dev:
- device_unlock(&dev->dev);
- return NULL;
+ return PCI_ERS_RESULT_NONE;
}
/**
* eeh_report_failure - Tell device driver that device is dead.
- * @data: eeh device
- * @userdata: return value
+ * @edev: eeh device
+ * @driver: device's PCI driver
*
* This informs the device driver that the device is permanently
* dead, and that no further recovery attempts will be made on it.
*/
-static void *eeh_report_failure(struct eeh_dev *edev, void *userdata)
+static enum pci_ers_result eeh_report_failure(struct eeh_dev *edev,
+ struct pci_driver *driver)
{
- struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
- struct pci_driver *driver;
+ enum pci_ers_result rc;
- if (!eeh_edev_actionable(edev))
- return NULL;
+ if (!driver->err_handler->error_detected)
+ return PCI_ERS_RESULT_NONE;
- device_lock(&dev->dev);
- dev->error_state = pci_channel_io_perm_failure;
+ eeh_infoline(edev, "Invoking %s->error_detected(permanent failure)",
+ driver->name);
+ rc = driver->err_handler->error_detected(edev->pdev, pci_channel_io_perm_failure);
- driver = eeh_pcid_get(dev);
- if (!driver) goto out_no_dev;
-
- if (!driver->err_handler ||
- !driver->err_handler->error_detected)
- goto out;
-
- driver->err_handler->error_detected(dev, pci_channel_io_perm_failure);
-
- pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT);
-out:
- eeh_pcid_put(dev);
-out_no_dev:
- device_unlock(&dev->dev);
- return NULL;
+ pci_uevent_ers(edev->pdev, PCI_ERS_RESULT_DISCONNECT);
+ return rc;
}
static void *eeh_add_virt_device(void *data, void *userdata)
@@ -811,7 +793,8 @@ void eeh_handle_normal_event(struct eeh_pe *pe)
pr_info("EEH: Notify device drivers to shutdown\n");
eeh_set_channel_state(pe, pci_channel_io_frozen);
eeh_set_irq_state(pe, false);
- eeh_pe_dev_traverse(pe, eeh_report_error, &result);
+ eeh_pe_report("error_detected(IO frozen)", pe,
+ eeh_report_error, &result);
if ((pe->type & EEH_PE_PHB) &&
result != PCI_ERS_RESULT_NONE &&
result != PCI_ERS_RESULT_NEED_RESET)
@@ -858,7 +841,8 @@ void eeh_handle_normal_event(struct eeh_pe *pe)
result = PCI_ERS_RESULT_NEED_RESET;
} else {
pr_info("EEH: Notify device drivers to resume I/O\n");
- eeh_pe_dev_traverse(pe, eeh_report_mmio_enabled, &result);
+ eeh_pe_report("mmio_enabled", pe,
+ eeh_report_mmio_enabled, &result);
}
}
@@ -903,7 +887,7 @@ void eeh_handle_normal_event(struct eeh_pe *pe)
result = PCI_ERS_RESULT_NONE;
eeh_set_channel_state(pe, pci_channel_io_normal);
eeh_set_irq_state(pe, true);
- eeh_pe_dev_traverse(pe, eeh_report_reset, &result);
+ eeh_pe_report("slot_reset", pe, eeh_report_reset, &result);
}
/* All devices should claim they have recovered by now. */
@@ -926,11 +910,13 @@ void eeh_handle_normal_event(struct eeh_pe *pe)
pr_info("EEH: Notify device driver to resume\n");
eeh_set_channel_state(pe, pci_channel_io_normal);
eeh_set_irq_state(pe, true);
- eeh_pe_dev_traverse(pe, eeh_report_resume, NULL);
-
- eeh_for_each_pe(pe, tmp_pe)
- eeh_pe_for_each_dev(tmp_pe, edev, tmp)
+ eeh_pe_report("resume", pe, eeh_report_resume, NULL);
+ eeh_for_each_pe(pe, tmp_pe) {
+ eeh_pe_for_each_dev(tmp_pe, edev, tmp) {
edev->mode &= ~EEH_DEV_NO_HANDLER;
+ edev->in_error = false;
+ }
+ }
pr_info("EEH: Recovery successful.\n");
goto final;
@@ -950,7 +936,8 @@ void eeh_handle_normal_event(struct eeh_pe *pe)
/* Notify all devices that they're about to go down. */
eeh_set_channel_state(pe, pci_channel_io_perm_failure);
eeh_set_irq_state(pe, false);
- eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);
+ eeh_pe_report("error_detected(permanent failure)", pe,
+ eeh_report_failure, NULL);
/* Mark the PE to be removed permanently */
eeh_pe_state_mark(pe, EEH_PE_REMOVED);
@@ -1060,8 +1047,8 @@ void eeh_handle_special_event(void)
/* Notify all devices to be down */
eeh_pe_state_clear(pe, EEH_PE_PRI_BUS);
eeh_set_channel_state(pe, pci_channel_io_perm_failure);
- eeh_pe_dev_traverse(pe,
- eeh_report_failure, NULL);
+ eeh_pe_report("error_detected(permanent failure)",
+ pe, eeh_report_failure, NULL);
bus = eeh_pe_bus_get(phb_pe);
if (!bus) {
pr_err("%s: Cannot find PCI bus for "
--
2.16.1.74.g9b0b1f47b
More information about the Linuxppc-dev
mailing list