Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 20b34497 authored by Sam Bobroff's avatar Sam Bobroff Committed by Michael Ellerman
Browse files

powerpc/eeh: Refactor report functions



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: default avatarSam Bobroff <sbobroff@linux.ibm.com>
[mpe: Format with clang-format]
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent 665012c5
Loading
Loading
Loading
Loading
+157 −150
Original line number Original line Diff line number Diff line
@@ -60,6 +60,44 @@ static int eeh_result_priority(enum pci_ers_result result)
	}
	}
};
};


const char *pci_ers_result_name(enum pci_ers_result result)
{
	switch (result) {
	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: %d\n", (int)result);
		return "unknown";
	}
};

static __printf(2, 3) void eeh_edev_info(const struct eeh_dev *edev,
					 const char *fmt, ...)
{
	struct va_format vaf;
	va_list args;

	va_start(args, fmt);

	vaf.fmt = fmt;
	vaf.va = &args;

	printk(KERN_INFO "EEH: PE#%x (PCI %s): %pV\n", edev->pe_config_addr,
	       edev->pdev ? dev_name(&edev->pdev->dev) : "none", &vaf);

	va_end(args);
}

static enum pci_ers_result pci_ers_merge_result(enum pci_ers_result old,
static enum pci_ers_result pci_ers_merge_result(enum pci_ers_result old,
						enum pci_ers_result new)
						enum pci_ers_result new)
{
{
@@ -235,123 +273,117 @@ static void eeh_set_irq_state(struct eeh_pe *root, bool enable)
	}
	}
}
}


/**
typedef enum pci_ers_result (*eeh_report_fn)(struct eeh_dev *,
 * eeh_report_error - Report pci error to each device driver
					     struct pci_driver *);
 * @data: eeh device
static void eeh_pe_report_edev(struct eeh_dev *edev, eeh_report_fn fn,
 * @userdata: return value
			       enum pci_ers_result *result)
 *
 * Report an EEH error to each device driver, collect up and
 * merge the device driver responses. Cumulative response
 * passed back in "userdata".
 */
static void *eeh_report_error(struct eeh_dev *edev, void *userdata)
{
{
	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
	enum pci_ers_result rc, *res = userdata;
	struct pci_driver *driver;
	struct pci_driver *driver;
	enum pci_ers_result new_result;

	device_lock(&edev->pdev->dev);
	if (eeh_edev_actionable(edev)) {
		driver = eeh_pcid_get(edev->pdev);

		if (!driver)
			eeh_edev_info(edev, "no driver");
		else if (!driver->err_handler)
			eeh_edev_info(edev, "driver not EEH aware");
		else if (edev->mode & EEH_DEV_NO_HANDLER)
			eeh_edev_info(edev, "driver bound too late");
		else {
			new_result = fn(edev, driver);
			eeh_edev_info(edev, "%s driver reports: '%s'",
				      driver->name,
				      pci_ers_result_name(new_result));
			if (result)
				*result = pci_ers_merge_result(*result,
							       new_result);
		}
		if (driver)
			eeh_pcid_put(edev->pdev);
	} else {
		eeh_edev_info(edev, "not actionable (%d,%d,%d)", !!edev->pdev,
			      !eeh_dev_removed(edev), !eeh_pe_passed(edev->pe));
	}
	device_unlock(&edev->pdev->dev);
}


	if (!eeh_edev_actionable(edev))
static void eeh_pe_report(const char *name, struct eeh_pe *root,
		return NULL;
			  eeh_report_fn fn, enum pci_ers_result *result)
{
	struct eeh_pe *pe;
	struct eeh_dev *edev, *tmp;


	device_lock(&dev->dev);
	pr_info("EEH: Beginning: '%s'\n", name);
	eeh_for_each_pe(root, pe) eeh_pe_for_each_dev(pe, edev, tmp)
		eeh_pe_report_edev(edev, fn, result);
	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);
}


	driver = eeh_pcid_get(dev);
/**
	if (!driver) goto out_no_dev;
 * eeh_report_error - Report pci error to each device driver
 * @edev: eeh device
 * @driver: device's PCI driver
 *
 * Report an EEH error to each device driver.
 */
static enum pci_ers_result eeh_report_error(struct eeh_dev *edev,
					    struct pci_driver *driver)
{
	enum pci_ers_result rc;
	struct pci_dev *dev = edev->pdev;


	if (!driver->err_handler ||
	if (!driver->err_handler->error_detected)
	    !driver->err_handler->error_detected)
		return PCI_ERS_RESULT_NONE;
		goto out;


	eeh_edev_info(edev, "Invoking %s->error_detected(IO frozen)",
		      driver->name);
	rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen);
	rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen);


	*res = pci_ers_merge_result(*res, rc);

	edev->in_error = true;
	edev->in_error = true;
	pci_uevent_ers(dev, PCI_ERS_RESULT_NONE);
	pci_uevent_ers(dev, PCI_ERS_RESULT_NONE);

	return rc;
out:
	eeh_pcid_put(dev);
out_no_dev:
	device_unlock(&dev->dev);
	return NULL;
}
}


/**
/**
 * eeh_report_mmio_enabled - Tell drivers that MMIO has been enabled
 * eeh_report_mmio_enabled - Tell drivers that MMIO has been enabled
 * @data: eeh device
 * @edev: eeh device
 * @userdata: return value
 * @driver: device's PCI driver
 *
 *
 * Tells each device driver that IO ports, MMIO and config space I/O
 * Tells each device driver that IO ports, MMIO and config space I/O
 * are now enabled. Collects up and merges the device driver responses.
 * are now enabled.
 * Cumulative response passed back in "userdata".
 */
 */
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);
	if (!driver->err_handler->mmio_enabled)
	enum pci_ers_result rc, *res = userdata;
		return PCI_ERS_RESULT_NONE;
	struct pci_driver *driver;
	eeh_edev_info(edev, "Invoking %s->mmio_enabled()", driver->name);

	return driver->err_handler->mmio_enabled(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->mmio_enabled ||
	    (edev->mode & EEH_DEV_NO_HANDLER))
		goto out;

	rc = driver->err_handler->mmio_enabled(dev);

	*res = pci_ers_merge_result(*res, rc);

out:
	eeh_pcid_put(dev);
out_no_dev:
	device_unlock(&dev->dev);
	return NULL;
}
}


/**
/**
 * eeh_report_reset - Tell device that slot has been reset
 * eeh_report_reset - Tell device that slot has been reset
 * @data: eeh device
 * @edev: eeh device
 * @userdata: return value
 * @driver: device's PCI driver
 *
 *
 * This routine must be called while EEH tries to reset particular
 * This routine must be called while EEH tries to reset particular
 * PCI device so that the associated PCI device driver could take
 * PCI device so that the associated PCI device driver could take
 * some actions, usually to save data the driver needs so that the
 * some actions, usually to save data the driver needs so that the
 * driver can work again while the device is recovered.
 * 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);
	if (!driver->err_handler->slot_reset || !edev->in_error)
	enum pci_ers_result rc, *res = userdata;
		return PCI_ERS_RESULT_NONE;
	struct pci_driver *driver;
	eeh_edev_info(edev, "Invoking %s->slot_reset()", driver->name);

	return driver->err_handler->slot_reset(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->slot_reset ||
	    (edev->mode & EEH_DEV_NO_HANDLER) ||
	    (!edev->in_error))
		goto out;

	rc = driver->err_handler->slot_reset(dev);
	*res = pci_ers_merge_result(*res, rc);

out:
	eeh_pcid_put(dev);
out_no_dev:
	device_unlock(&dev->dev);
	return NULL;
}
}


static void *eeh_dev_restore_state(struct eeh_dev *edev, void *userdata)
static void *eeh_dev_restore_state(struct eeh_dev *edev, void *userdata)
@@ -384,84 +416,53 @@ static void *eeh_dev_restore_state(struct eeh_dev *edev, void *userdata)


/**
/**
 * eeh_report_resume - Tell device to resume normal operations
 * eeh_report_resume - Tell device to resume normal operations
 * @data: eeh device
 * @edev: eeh device
 * @userdata: return value
 * @driver: device's PCI driver
 *
 *
 * This routine must be called to notify the device driver that it
 * This routine must be called to notify the device driver that it
 * could resume so that the device driver can do some initialization
 * could resume so that the device driver can do some initialization
 * to make the recovered device work again.
 * 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);
	if (!driver->err_handler->resume || !edev->in_error)
	bool was_in_error;
		return PCI_ERS_RESULT_NONE;
	struct pci_driver *driver;

	if (!eeh_edev_actionable(edev))
		return NULL;


	device_lock(&dev->dev);
	eeh_edev_info(edev, "Invoking %s->resume()", driver->name);
	driver->err_handler->resume(edev->pdev);


	driver = eeh_pcid_get(dev);
	pci_uevent_ers(edev->pdev, PCI_ERS_RESULT_RECOVERED);
	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);
#ifdef CONFIG_PCI_IOV
#ifdef CONFIG_PCI_IOV
	if (eeh_ops->notify_resume && eeh_dev_to_pdn(edev))
	if (eeh_ops->notify_resume && eeh_dev_to_pdn(edev))
		eeh_ops->notify_resume(eeh_dev_to_pdn(edev));
		eeh_ops->notify_resume(eeh_dev_to_pdn(edev));
#endif
#endif
out_no_dev:
	return PCI_ERS_RESULT_NONE;
	device_unlock(&dev->dev);
	return NULL;
}
}


/**
/**
 * eeh_report_failure - Tell device driver that device is dead.
 * eeh_report_failure - Tell device driver that device is dead.
 * @data: eeh device
 * @edev: eeh device
 * @userdata: return value
 * @driver: device's PCI driver
 *
 *
 * This informs the device driver that the device is permanently
 * This informs the device driver that the device is permanently
 * dead, and that no further recovery attempts will be made on it.
 * 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);
	enum pci_ers_result rc;
	struct pci_driver *driver;


	if (!eeh_edev_actionable(edev))
	if (!driver->err_handler->error_detected)
		return NULL;
		return PCI_ERS_RESULT_NONE;


	device_lock(&dev->dev);
	eeh_edev_info(edev, "Invoking %s->error_detected(permanent failure)",
	dev->error_state = pci_channel_io_perm_failure;
		      driver->name);
	rc = driver->err_handler->error_detected(edev->pdev,
						 pci_channel_io_perm_failure);


	driver = eeh_pcid_get(dev);
	pci_uevent_ers(edev->pdev, PCI_ERS_RESULT_DISCONNECT);
	if (!driver) goto out_no_dev;
	return rc;

	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;
}
}


static void *eeh_add_virt_device(void *data, void *userdata)
static void *eeh_add_virt_device(void *data, void *userdata)
@@ -823,7 +824,8 @@ void eeh_handle_normal_event(struct eeh_pe *pe)
	pr_info("EEH: Notify device drivers to shutdown\n");
	pr_info("EEH: Notify device drivers to shutdown\n");
	eeh_set_channel_state(pe, pci_channel_io_frozen);
	eeh_set_channel_state(pe, pci_channel_io_frozen);
	eeh_set_irq_state(pe, false);
	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) &&
	if ((pe->type & EEH_PE_PHB) &&
	    result != PCI_ERS_RESULT_NONE &&
	    result != PCI_ERS_RESULT_NONE &&
	    result != PCI_ERS_RESULT_NEED_RESET)
	    result != PCI_ERS_RESULT_NEED_RESET)
@@ -870,7 +872,8 @@ void eeh_handle_normal_event(struct eeh_pe *pe)
			result = PCI_ERS_RESULT_NEED_RESET;
			result = PCI_ERS_RESULT_NEED_RESET;
		} else {
		} else {
			pr_info("EEH: Notify device drivers to resume I/O\n");
			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);
		}
		}
	}
	}


@@ -915,7 +918,7 @@ void eeh_handle_normal_event(struct eeh_pe *pe)
		result = PCI_ERS_RESULT_NONE;
		result = PCI_ERS_RESULT_NONE;
		eeh_set_channel_state(pe, pci_channel_io_normal);
		eeh_set_channel_state(pe, pci_channel_io_normal);
		eeh_set_irq_state(pe, true);
		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. */
	/* All devices should claim they have recovered by now. */
@@ -938,11 +941,13 @@ void eeh_handle_normal_event(struct eeh_pe *pe)
	pr_info("EEH: Notify device driver to resume\n");
	pr_info("EEH: Notify device driver to resume\n");
	eeh_set_channel_state(pe, pci_channel_io_normal);
	eeh_set_channel_state(pe, pci_channel_io_normal);
	eeh_set_irq_state(pe, true);
	eeh_set_irq_state(pe, true);
	eeh_pe_dev_traverse(pe, eeh_report_resume, NULL);
	eeh_pe_report("resume", pe, eeh_report_resume, NULL);

	eeh_for_each_pe(pe, tmp_pe) {
	eeh_for_each_pe(pe, tmp_pe)
		eeh_pe_for_each_dev(tmp_pe, edev, tmp) {
		eeh_pe_for_each_dev(tmp_pe, edev, tmp)
			edev->mode &= ~EEH_DEV_NO_HANDLER;
			edev->mode &= ~EEH_DEV_NO_HANDLER;
			edev->in_error = false;
		}
	}


	pr_info("EEH: Recovery successful.\n");
	pr_info("EEH: Recovery successful.\n");
	goto final;
	goto final;
@@ -962,7 +967,8 @@ void eeh_handle_normal_event(struct eeh_pe *pe)
	/* Notify all devices that they're about to go down. */
	/* Notify all devices that they're about to go down. */
	eeh_set_channel_state(pe, pci_channel_io_perm_failure);
	eeh_set_channel_state(pe, pci_channel_io_perm_failure);
	eeh_set_irq_state(pe, false);
	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 */
	/* Mark the PE to be removed permanently */
	eeh_pe_state_mark(pe, EEH_PE_REMOVED);
	eeh_pe_state_mark(pe, EEH_PE_REMOVED);
@@ -1072,7 +1078,8 @@ void eeh_handle_special_event(void)
				/* Notify all devices to be down */
				/* Notify all devices to be down */
				eeh_pe_state_clear(pe, EEH_PE_PRI_BUS);
				eeh_pe_state_clear(pe, EEH_PE_PRI_BUS);
				eeh_set_channel_state(pe, pci_channel_io_perm_failure);
				eeh_set_channel_state(pe, pci_channel_io_perm_failure);
				eeh_pe_dev_traverse(pe,
				eeh_pe_report(
					"error_detected(permanent failure)", pe,
					eeh_report_failure, NULL);
					eeh_report_failure, NULL);
				bus = eeh_pe_bus_get(phb_pe);
				bus = eeh_pe_bus_get(phb_pe);
				if (!bus) {
				if (!bus) {