int ata_eh_recover()

in libata-eh.c [3545:3763]


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,
		   struct ata_link **r_failed_link)
{
	struct ata_link *link;
	struct ata_device *dev;
	int rc, nr_fails;
	unsigned long flags, deadline;

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

		/* re-enable link? */
		if (ehc->i.action & ATA_EH_ENABLE_LINK) {
			ata_eh_about_to_do(link, NULL, ATA_EH_ENABLE_LINK);
			spin_lock_irqsave(ap->lock, flags);
			link->flags &= ~ATA_LFLAG_DISABLED;
			spin_unlock_irqrestore(ap->lock, flags);
			ata_eh_done(link, NULL, ATA_EH_ENABLE_LINK);
		}

		ata_for_each_dev(dev, link, ALL) {
			if (link->flags & ATA_LFLAG_NO_RETRY)
				ehc->tries[dev->devno] = 1;
			else
				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.dev_action[dev->devno] &= ATA_EH_PERDEV_MASK;

			/* process hotplug request */
			if (dev->flags & ATA_DFLAG_DETACH)
				ata_eh_detach_dev(dev);

			/* schedule probe if necessary */
			if (!ata_dev_enabled(dev))
				ata_eh_schedule_probe(dev);
		}
	}

 retry:
	rc = 0;

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

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

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

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

	/* reset */
	ata_for_each_link(link, ap, EDGE) {
		struct ata_eh_context *ehc = &link->eh_context;

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

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

	do {
		unsigned long now;

		/*
		 * clears ATA_EH_PARK in eh_info and resets
		 * ap->park_req_pending
		 */
		ata_eh_pull_park_action(ap);

		deadline = jiffies;
		ata_for_each_link(link, ap, EDGE) {
			ata_for_each_dev(dev, link, ALL) {
				struct ata_eh_context *ehc = &link->eh_context;
				unsigned long tmp;

				if (dev->class != ATA_DEV_ATA &&
				    dev->class != ATA_DEV_ZAC)
					continue;
				if (!(ehc->i.dev_action[dev->devno] &
				      ATA_EH_PARK))
					continue;
				tmp = dev->unpark_deadline;
				if (time_before(deadline, tmp))
					deadline = tmp;
				else if (time_before_eq(tmp, jiffies))
					continue;
				if (ehc->unloaded_mask & (1 << dev->devno))
					continue;

				ata_eh_park_issue_cmd(dev, 1);
			}
		}

		now = jiffies;
		if (time_before_eq(deadline, now))
			break;

		ata_eh_release(ap);
		deadline = wait_for_completion_timeout(&ap->park_req_pending,
						       deadline - now);
		ata_eh_acquire(ap);
	} while (deadline);
	ata_for_each_link(link, ap, EDGE) {
		ata_for_each_dev(dev, link, ALL) {
			if (!(link->eh_context.unloaded_mask &
			      (1 << dev->devno)))
				continue;

			ata_eh_park_issue_cmd(dev, 0);
			ata_eh_done(link, dev, ATA_EH_PARK);
		}
	}

	/* the rest */
	nr_fails = 0;
	ata_for_each_link(link, ap, PMP_FIRST) {
		struct ata_eh_context *ehc = &link->eh_context;

		if (sata_pmp_attached(ap) && ata_is_host_link(link))
			goto config_lpm;

		/* revalidate existing devices and attach new ones */
		rc = ata_eh_revalidate_and_attach(link, &dev);
		if (rc)
			goto rest_fail;

		/* if PMP got attached, return, pmp EH will take care of it */
		if (link->device->class == ATA_DEV_PMP) {
			ehc->i.action = 0;
			return 0;
		}

		/* configure transfer mode if necessary */
		if (ehc->i.flags & ATA_EHI_SETMODE) {
			rc = ata_set_mode(link, &dev);
			if (rc)
				goto rest_fail;
			ehc->i.flags &= ~ATA_EHI_SETMODE;
		}

		/* If reset has been issued, clear UA to avoid
		 * disrupting the current users of the device.
		 */
		if (ehc->i.flags & ATA_EHI_DID_RESET) {
			ata_for_each_dev(dev, link, ALL) {
				if (dev->class != ATA_DEV_ATAPI)
					continue;
				rc = atapi_eh_clear_ua(dev);
				if (rc)
					goto rest_fail;
				if (zpodd_dev_enabled(dev))
					zpodd_post_poweron(dev);
			}
		}

		/* retry flush if necessary */
		ata_for_each_dev(dev, link, ALL) {
			if (dev->class != ATA_DEV_ATA &&
			    dev->class != ATA_DEV_ZAC)
				continue;
			rc = ata_eh_maybe_retry_flush(dev);
			if (rc)
				goto rest_fail;
		}

	config_lpm:
		/* configure link power saving */
		if (link->lpm_policy != ap->target_lpm_policy) {
			rc = ata_eh_set_lpm(link, ap->target_lpm_policy, &dev);
			if (rc)
				goto rest_fail;
		}

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

	rest_fail:
		nr_fails++;
		if (dev)
			ata_eh_handle_dev_fail(dev, rc);

		if (ap->pflags & ATA_PFLAG_FROZEN) {
			/* PMP reset requires working host port.
			 * Can't retry if it's frozen.
			 */
			if (sata_pmp_attached(ap))
				goto out;
			break;
		}
	}

	if (nr_fails)
		goto retry;

 out:
	if (rc && r_failed_link)
		*r_failed_link = link;

	return rc;
}