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

Commit 9b1e2658 authored by Tejun Heo's avatar Tejun Heo Committed by Jeff Garzik
Browse files

libata-link: update EH to deal with PMP links



Update ata_eh_autopsy(), ata_eh_report(),
ata_eh_revalidate_and_attach() and ata_eh_recover() to deal with PMP
links.  ata_eh_autopsy() and ata_eh_report() updates are
straightforward.  They just repeat the same operation over all
configured links.  The only change to ata_eh_revalidate_and_attach()
is avoiding calling ->cable_select() on non-host ports.

ata_eh_recover() update is more complex as it first processes all
resets and then performs the rest.  This is necessary as thawing with
some links in unknown state can be dangerous.  ehi->action is cleared
on successful recovery of a link to avoid repeating recovery due to
failures in other links.

ata_eh_recover() iterates over only PMP links if PMP is attached, and,
on failure, the failing link is returned in @failed_link instead of
disabling devices directly.  These are to integrate ata_eh_recover()
into PMP EH later.

Signed-off-by: default avatarTejun Heo <htejun@gmail.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent cf1b86c8
Loading
Loading
Loading
Loading
+158 −70
Original line number Diff line number Diff line
@@ -1578,8 +1578,8 @@ static unsigned int ata_eh_speed_down(struct ata_device *dev, int is_io,
}

/**
 *	ata_eh_autopsy - analyze error and determine recovery action
 *	@link: ATA link to perform autopsy on
 *	ata_eh_link_autopsy - analyze error and determine recovery action
 *	@link: host link to perform autopsy on
 *
 *	Analyze why @link failed and determine which recovery actions
 *	are needed.  This function also sets more detailed AC_ERR_*
@@ -1588,7 +1588,7 @@ static unsigned int ata_eh_speed_down(struct ata_device *dev, int is_io,
 *	LOCKING:
 *	Kernel thread context (may sleep).
 */
static void ata_eh_autopsy(struct ata_link *link)
static void ata_eh_link_autopsy(struct ata_link *link)
{
	struct ata_port *ap = link->ap;
	struct ata_eh_context *ehc = &link->eh_context;
@@ -1680,7 +1680,25 @@ static void ata_eh_autopsy(struct ata_link *link)
}

/**
 *	ata_eh_report - report error handling to user
 *	ata_eh_autopsy - analyze error and determine recovery action
 *	@ap: host port to perform autopsy on
 *
 *	Analyze all links of @ap and determine why they failed and
 *	which recovery actions are needed.
 *
 *	LOCKING:
 *	Kernel thread context (may sleep).
 */
static void ata_eh_autopsy(struct ata_port *ap)
{
	struct ata_link *link;

	__ata_port_for_each_link(link, ap)
		ata_eh_link_autopsy(link);
}

/**
 *	ata_eh_link_report - report error handling to user
 *	@link: ATA link EH is going on
 *
 *	Report EH to user.
@@ -1688,7 +1706,7 @@ static void ata_eh_autopsy(struct ata_link *link)
 *	LOCKING:
 *	None.
 */
static void ata_eh_report(struct ata_link *link)
static void ata_eh_link_report(struct ata_link *link)
{
	struct ata_port *ap = link->ap;
	struct ata_eh_context *ehc = &link->eh_context;
@@ -1767,6 +1785,23 @@ static void ata_eh_report(struct ata_link *link)
	}
}

/**
 *	ata_eh_report - report error handling to user
 *	@ap: ATA port to report EH about
 *
 *	Report EH to user.
 *
 *	LOCKING:
 *	None.
 */
static void ata_eh_report(struct ata_port *ap)
{
	struct ata_link *link;

	__ata_port_for_each_link(link, ap)
		ata_eh_link_report(link);
}

static int ata_do_reset(struct ata_link *link, ata_reset_fn_t reset,
			unsigned int *classes, unsigned long deadline)
{
@@ -2036,7 +2071,8 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
	}

	/* PDIAG- should have been released, ask cable type if post-reset */
	if ((ehc->i.flags & ATA_EHI_DID_RESET) && ap->ops->cable_detect)
	if (ata_is_host_link(link) && ap->ops->cable_detect &&
	    (ehc->i.flags & ATA_EHI_DID_RESET))
		ap->cbl = ap->ops->cable_detect(ap);

	/* Configure new devices forward such that user doesn't see
@@ -2110,7 +2146,7 @@ static int ata_eh_skip_recovery(struct ata_link *link)
	return 1;
}

static void ata_eh_handle_dev_fail(struct ata_device *dev, int err)
static int ata_eh_handle_dev_fail(struct ata_device *dev, int err)
{
	struct ata_eh_context *ehc = &dev->link->eh_context;

@@ -2151,12 +2187,16 @@ static void ata_eh_handle_dev_fail(struct ata_device *dev, int err)
			ehc->did_probe_mask |= (1 << dev->devno);
			ehc->i.action |= ATA_EH_SOFTRESET;
		}

		return 1;
	} else {
		/* soft didn't work?  be haaaaard */
		if (ehc->i.flags & ATA_EHI_DID_RESET)
			ehc->i.action |= ATA_EH_HARDRESET;
		else
			ehc->i.action |= ATA_EH_SOFTRESET;

		return 0;
	}
}

@@ -2167,12 +2207,13 @@ static void ata_eh_handle_dev_fail(struct ata_device *dev, int err)
 *	@softreset: softreset method (can be NULL)
 *	@hardreset: hardreset method (can be NULL)
 *	@postreset: postreset method (can be NULL)
 *	@r_failed_link: out parameter for failed link
 *
 *	This is the alpha and omega, eum and yang, heart and soul of
 *	libata exception handling.  On entry, actions required to
 *	recover the port and hotplug requests are recorded in
 *	eh_context.  This function executes all the operations with
 *	appropriate retrials and fallbacks to resurrect failed
 *	recover each link and hotplug requests are recorded in the
 *	link's eh_context.  This function executes all the operations
 *	with appropriate retrials and fallbacks to resurrect failed
 *	devices, detach goners and greet newcomers.
 *
 *	LOCKING:
@@ -2183,22 +2224,26 @@ static void ata_eh_handle_dev_fail(struct ata_device *dev, int err)
 */
static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
			  ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
			  ata_postreset_fn_t postreset)
			  ata_postreset_fn_t postreset,
			  struct ata_link **r_failed_link)
{
	struct ata_link *link = &ap->link;
	struct ata_eh_context *ehc = &link->eh_context;
	struct ata_link *link;
	struct ata_device *dev;
	int rc;
	int nr_failed_devs, nr_disabled_devs;
	int reset, rc;

	DPRINTK("ENTER\n");

	/* prep for recovery */
	ata_port_for_each_link(link, ap) {
		struct ata_eh_context *ehc = &link->eh_context;

		ata_link_for_each_dev(dev, link) {
			ehc->tries[dev->devno] = ATA_EH_DEV_TRIES;

			/* collect port action mask recorded in dev actions */
		ehc->i.action |=
			ehc->i.dev_action[dev->devno] & ~ATA_EH_PERDEV_MASK;
			ehc->i.action |= ehc->i.dev_action[dev->devno] &
					 ~ATA_EH_PERDEV_MASK;
			ehc->i.dev_action[dev->devno] &= ATA_EH_PERDEV_MASK;

			/* process hotplug request */
@@ -2214,36 +2259,61 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
				ehc->i.action |= ATA_EH_SOFTRESET;
			}
		}
	}

 retry:
	rc = 0;
	nr_failed_devs = 0;
	nr_disabled_devs = 0;
	reset = 0;

	/* if UNLOADING, finish immediately */
	if (ap->pflags & ATA_PFLAG_UNLOADING)
		goto out;

	/* prep for EH */
	ata_port_for_each_link(link, ap) {
		struct ata_eh_context *ehc = &link->eh_context;

		/* skip EH if possible. */
		if (ata_eh_skip_recovery(link))
			ehc->i.action = 0;

		/* do we need to reset? */
		if (ehc->i.action & ATA_EH_RESET_MASK)
			reset = 1;

		ata_link_for_each_dev(dev, link)
			ehc->classes[dev->devno] = ATA_DEV_UNKNOWN;
	}

	/* reset */
	if (ehc->i.action & ATA_EH_RESET_MASK) {
	if (reset) {
		ata_eh_freeze_port(ap);

		rc = ata_eh_reset(link, ata_link_nr_vacant(link), prereset,
				  softreset, hardreset, postreset);
		ata_port_for_each_link(link, ap) {
			struct ata_eh_context *ehc = &link->eh_context;

			if (!(ehc->i.action & ATA_EH_RESET_MASK))
				continue;

			rc = ata_eh_reset(link, ata_link_nr_vacant(link),
					  prereset, softreset, hardreset,
					  postreset);
			if (rc) {
				ata_link_printk(link, KERN_ERR,
						"reset failed, giving up\n");
				goto out;
			}
		}

		ata_eh_thaw_port(ap);
	}

	/* the rest */
	ata_port_for_each_link(link, ap) {
		struct ata_eh_context *ehc = &link->eh_context;

		/* revalidate existing devices and attach new ones */
		rc = ata_eh_revalidate_and_attach(link, &dev);
		if (rc)
@@ -2257,27 +2327,35 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
			ehc->i.flags &= ~ATA_EHI_SETMODE;
		}

	goto out;
		/* this link is okay now */
		ehc->i.flags = 0;
		continue;

	dev_fail:
	ata_eh_handle_dev_fail(dev, rc);
		nr_failed_devs++;
		if (ata_eh_handle_dev_fail(dev, rc))
			nr_disabled_devs++;

		if (ap->pflags & ATA_PFLAG_FROZEN)
			break;
	}

	if (ata_link_nr_enabled(link)) {
		ata_link_printk(link, KERN_WARNING, "failed to recover some "
				"devices, retrying in 5 secs\n");
	if (nr_failed_devs) {
		if (nr_failed_devs != nr_disabled_devs) {
			ata_port_printk(ap, KERN_WARNING, "failed to recover "
					"some devices, retrying in 5 secs\n");
			ssleep(5);
		} else {
		/* no device left, repeat fast */
			/* no device left to recover, repeat fast */
			msleep(500);
		}

		goto retry;
	}

 out:
	if (rc) {
		ata_link_for_each_dev(dev, link);
			ata_dev_disable(dev);
	}
	if (rc && r_failed_link)
		*r_failed_link = link;

	DPRINTK("EXIT, rc=%d\n", rc);
	return rc;
@@ -2342,9 +2420,19 @@ void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset,
	       ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
	       ata_postreset_fn_t postreset)
{
	ata_eh_autopsy(&ap->link);
	ata_eh_report(&ap->link);
	ata_eh_recover(ap, prereset, softreset, hardreset, postreset);
	struct ata_device *dev;
	int rc;

	ata_eh_autopsy(ap);
	ata_eh_report(ap);

	rc = ata_eh_recover(ap, prereset, softreset, hardreset, postreset,
			    NULL);
	if (rc) {
		ata_link_for_each_dev(dev, &ap->link)
			ata_dev_disable(dev);
	}

	ata_eh_finish(ap);
}