int ata_eh_reset()

in libata-eh.c [2438:2842]


int ata_eh_reset(struct ata_link *link, int classify,
		 ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
		 ata_reset_fn_t hardreset, ata_postreset_fn_t postreset)
{
	struct ata_port *ap = link->ap;
	struct ata_link *slave = ap->slave_link;
	struct ata_eh_context *ehc = &link->eh_context;
	struct ata_eh_context *sehc = slave ? &slave->eh_context : NULL;
	unsigned int *classes = ehc->classes;
	unsigned int lflags = link->flags;
	int verbose = !(ehc->i.flags & ATA_EHI_QUIET);
	int max_tries = 0, try = 0;
	struct ata_link *failed_link;
	struct ata_device *dev;
	unsigned long deadline, now;
	ata_reset_fn_t reset;
	unsigned long flags;
	u32 sstatus;
	int nr_unknown, rc;

	/*
	 * Prepare to reset
	 */
	while (ata_eh_reset_timeouts[max_tries] != ULONG_MAX)
		max_tries++;
	if (link->flags & ATA_LFLAG_RST_ONCE)
		max_tries = 1;
	if (link->flags & ATA_LFLAG_NO_HRST)
		hardreset = NULL;
	if (link->flags & ATA_LFLAG_NO_SRST)
		softreset = NULL;

	/* make sure each reset attempt is at least COOL_DOWN apart */
	if (ehc->i.flags & ATA_EHI_DID_RESET) {
		now = jiffies;
		WARN_ON(time_after(ehc->last_reset, now));
		deadline = ata_deadline(ehc->last_reset,
					ATA_EH_RESET_COOL_DOWN);
		if (time_before(now, deadline))
			schedule_timeout_uninterruptible(deadline - now);
	}

	spin_lock_irqsave(ap->lock, flags);
	ap->pflags |= ATA_PFLAG_RESETTING;
	spin_unlock_irqrestore(ap->lock, flags);

	ata_eh_about_to_do(link, NULL, ATA_EH_RESET);

	ata_for_each_dev(dev, link, ALL) {
		/* If we issue an SRST then an ATA drive (not ATAPI)
		 * may change configuration and be in PIO0 timing. If
		 * we do a hard reset (or are coming from power on)
		 * this is true for ATA or ATAPI. Until we've set a
		 * suitable controller mode we should not touch the
		 * bus as we may be talking too fast.
		 */
		dev->pio_mode = XFER_PIO_0;
		dev->dma_mode = 0xff;

		/* If the controller has a pio mode setup function
		 * then use it to set the chipset to rights. Don't
		 * touch the DMA setup as that will be dealt with when
		 * configuring devices.
		 */
		if (ap->ops->set_piomode)
			ap->ops->set_piomode(ap, dev);
	}

	/* prefer hardreset */
	reset = NULL;
	ehc->i.action &= ~ATA_EH_RESET;
	if (hardreset) {
		reset = hardreset;
		ehc->i.action |= ATA_EH_HARDRESET;
	} else if (softreset) {
		reset = softreset;
		ehc->i.action |= ATA_EH_SOFTRESET;
	}

	if (prereset) {
		unsigned long deadline = ata_deadline(jiffies,
						      ATA_EH_PRERESET_TIMEOUT);

		if (slave) {
			sehc->i.action &= ~ATA_EH_RESET;
			sehc->i.action |= ehc->i.action;
		}

		rc = prereset(link, deadline);

		/* If present, do prereset on slave link too.  Reset
		 * is skipped iff both master and slave links report
		 * -ENOENT or clear ATA_EH_RESET.
		 */
		if (slave && (rc == 0 || rc == -ENOENT)) {
			int tmp;

			tmp = prereset(slave, deadline);
			if (tmp != -ENOENT)
				rc = tmp;

			ehc->i.action |= sehc->i.action;
		}

		if (rc) {
			if (rc == -ENOENT) {
				ata_link_dbg(link, "port disabled--ignoring\n");
				ehc->i.action &= ~ATA_EH_RESET;

				ata_for_each_dev(dev, link, ALL)
					classes[dev->devno] = ATA_DEV_NONE;

				rc = 0;
			} else
				ata_link_err(link,
					     "prereset failed (errno=%d)\n",
					     rc);
			goto out;
		}

		/* prereset() might have cleared ATA_EH_RESET.  If so,
		 * bang classes, thaw and return.
		 */
		if (reset && !(ehc->i.action & ATA_EH_RESET)) {
			ata_for_each_dev(dev, link, ALL)
				classes[dev->devno] = ATA_DEV_NONE;
			if ((ap->pflags & ATA_PFLAG_FROZEN) &&
			    ata_is_host_link(link))
				ata_eh_thaw_port(ap);
			rc = 0;
			goto out;
		}
	}

 retry:
	/*
	 * Perform reset
	 */
	if (ata_is_host_link(link))
		ata_eh_freeze_port(ap);

	deadline = ata_deadline(jiffies, ata_eh_reset_timeouts[try++]);

	if (reset) {
		if (verbose)
			ata_link_info(link, "%s resetting link\n",
				      reset == softreset ? "soft" : "hard");

		/* mark that this EH session started with reset */
		ehc->last_reset = jiffies;
		if (reset == hardreset) {
			ehc->i.flags |= ATA_EHI_DID_HARDRESET;
			trace_ata_link_hardreset_begin(link, classes, deadline);
		} else {
			ehc->i.flags |= ATA_EHI_DID_SOFTRESET;
			trace_ata_link_softreset_begin(link, classes, deadline);
		}

		rc = ata_do_reset(link, reset, classes, deadline, true);
		if (reset == hardreset)
			trace_ata_link_hardreset_end(link, classes, rc);
		else
			trace_ata_link_softreset_end(link, classes, rc);
		if (rc && rc != -EAGAIN) {
			failed_link = link;
			goto fail;
		}

		/* hardreset slave link if existent */
		if (slave && reset == hardreset) {
			int tmp;

			if (verbose)
				ata_link_info(slave, "hard resetting link\n");

			ata_eh_about_to_do(slave, NULL, ATA_EH_RESET);
			trace_ata_slave_hardreset_begin(slave, classes,
							deadline);
			tmp = ata_do_reset(slave, reset, classes, deadline,
					   false);
			trace_ata_slave_hardreset_end(slave, classes, tmp);
			switch (tmp) {
			case -EAGAIN:
				rc = -EAGAIN;
				break;
			case 0:
				break;
			default:
				failed_link = slave;
				rc = tmp;
				goto fail;
			}
		}

		/* perform follow-up SRST if necessary */
		if (reset == hardreset &&
		    ata_eh_followup_srst_needed(link, rc)) {
			reset = softreset;

			if (!reset) {
				ata_link_err(link,
	     "follow-up softreset required but no softreset available\n");
				failed_link = link;
				rc = -EINVAL;
				goto fail;
			}

			ata_eh_about_to_do(link, NULL, ATA_EH_RESET);
			trace_ata_link_softreset_begin(link, classes, deadline);
			rc = ata_do_reset(link, reset, classes, deadline, true);
			trace_ata_link_softreset_end(link, classes, rc);
			if (rc) {
				failed_link = link;
				goto fail;
			}
		}
	} else {
		if (verbose)
			ata_link_info(link,
	"no reset method available, skipping reset\n");
		if (!(lflags & ATA_LFLAG_ASSUME_CLASS))
			lflags |= ATA_LFLAG_ASSUME_ATA;
	}

	/*
	 * Post-reset processing
	 */
	ata_for_each_dev(dev, link, ALL) {
		/* After the reset, the device state is PIO 0 and the
		 * controller state is undefined.  Reset also wakes up
		 * drives from sleeping mode.
		 */
		dev->pio_mode = XFER_PIO_0;
		dev->flags &= ~ATA_DFLAG_SLEEPING;

		if (ata_phys_link_offline(ata_dev_phys_link(dev)))
			continue;

		/* apply class override */
		if (lflags & ATA_LFLAG_ASSUME_ATA)
			classes[dev->devno] = ATA_DEV_ATA;
		else if (lflags & ATA_LFLAG_ASSUME_SEMB)
			classes[dev->devno] = ATA_DEV_SEMB_UNSUP;
	}

	/* record current link speed */
	if (sata_scr_read(link, SCR_STATUS, &sstatus) == 0)
		link->sata_spd = (sstatus >> 4) & 0xf;
	if (slave && sata_scr_read(slave, SCR_STATUS, &sstatus) == 0)
		slave->sata_spd = (sstatus >> 4) & 0xf;

	/* thaw the port */
	if (ata_is_host_link(link))
		ata_eh_thaw_port(ap);

	/* postreset() should clear hardware SError.  Although SError
	 * is cleared during link resume, clearing SError here is
	 * necessary as some PHYs raise hotplug events after SRST.
	 * This introduces race condition where hotplug occurs between
	 * reset and here.  This race is mediated by cross checking
	 * link onlineness and classification result later.
	 */
	if (postreset) {
		postreset(link, classes);
		trace_ata_link_postreset(link, classes, rc);
		if (slave) {
			postreset(slave, classes);
			trace_ata_slave_postreset(slave, classes, rc);
		}
	}

	/*
	 * Some controllers can't be frozen very well and may set spurious
	 * error conditions during reset.  Clear accumulated error
	 * information and re-thaw the port if frozen.  As reset is the
	 * final recovery action and we cross check link onlineness against
	 * device classification later, no hotplug event is lost by this.
	 */
	spin_lock_irqsave(link->ap->lock, flags);
	memset(&link->eh_info, 0, sizeof(link->eh_info));
	if (slave)
		memset(&slave->eh_info, 0, sizeof(link->eh_info));
	ap->pflags &= ~ATA_PFLAG_EH_PENDING;
	spin_unlock_irqrestore(link->ap->lock, flags);

	if (ap->pflags & ATA_PFLAG_FROZEN)
		ata_eh_thaw_port(ap);

	/*
	 * Make sure onlineness and classification result correspond.
	 * Hotplug could have happened during reset and some
	 * controllers fail to wait while a drive is spinning up after
	 * being hotplugged causing misdetection.  By cross checking
	 * link on/offlineness and classification result, those
	 * conditions can be reliably detected and retried.
	 */
	nr_unknown = 0;
	ata_for_each_dev(dev, link, ALL) {
		if (ata_phys_link_online(ata_dev_phys_link(dev))) {
			if (classes[dev->devno] == ATA_DEV_UNKNOWN) {
				ata_dev_dbg(dev, "link online but device misclassified\n");
				classes[dev->devno] = ATA_DEV_NONE;
				nr_unknown++;
			}
		} else if (ata_phys_link_offline(ata_dev_phys_link(dev))) {
			if (ata_class_enabled(classes[dev->devno]))
				ata_dev_dbg(dev,
					    "link offline, clearing class %d to NONE\n",
					    classes[dev->devno]);
			classes[dev->devno] = ATA_DEV_NONE;
		} else if (classes[dev->devno] == ATA_DEV_UNKNOWN) {
			ata_dev_dbg(dev,
				    "link status unknown, clearing UNKNOWN to NONE\n");
			classes[dev->devno] = ATA_DEV_NONE;
		}
	}

	if (classify && nr_unknown) {
		if (try < max_tries) {
			ata_link_warn(link,
				      "link online but %d devices misclassified, retrying\n",
				      nr_unknown);
			failed_link = link;
			rc = -EAGAIN;
			goto fail;
		}
		ata_link_warn(link,
			      "link online but %d devices misclassified, "
			      "device detection might fail\n", nr_unknown);
	}

	/* reset successful, schedule revalidation */
	ata_eh_done(link, NULL, ATA_EH_RESET);
	if (slave)
		ata_eh_done(slave, NULL, ATA_EH_RESET);
	ehc->last_reset = jiffies;		/* update to completion time */
	ehc->i.action |= ATA_EH_REVALIDATE;
	link->lpm_policy = ATA_LPM_UNKNOWN;	/* reset LPM state */

	rc = 0;
 out:
	/* clear hotplug flag */
	ehc->i.flags &= ~ATA_EHI_HOTPLUGGED;
	if (slave)
		sehc->i.flags &= ~ATA_EHI_HOTPLUGGED;

	spin_lock_irqsave(ap->lock, flags);
	ap->pflags &= ~ATA_PFLAG_RESETTING;
	spin_unlock_irqrestore(ap->lock, flags);

	return rc;

 fail:
	/* if SCR isn't accessible on a fan-out port, PMP needs to be reset */
	if (!ata_is_host_link(link) &&
	    sata_scr_read(link, SCR_STATUS, &sstatus))
		rc = -ERESTART;

	if (try >= max_tries) {
		/*
		 * Thaw host port even if reset failed, so that the port
		 * can be retried on the next phy event.  This risks
		 * repeated EH runs but seems to be a better tradeoff than
		 * shutting down a port after a botched hotplug attempt.
		 */
		if (ata_is_host_link(link))
			ata_eh_thaw_port(ap);
		goto out;
	}

	now = jiffies;
	if (time_before(now, deadline)) {
		unsigned long delta = deadline - now;

		ata_link_warn(failed_link,
			"reset failed (errno=%d), retrying in %u secs\n",
			rc, DIV_ROUND_UP(jiffies_to_msecs(delta), 1000));

		ata_eh_release(ap);
		while (delta)
			delta = schedule_timeout_uninterruptible(delta);
		ata_eh_acquire(ap);
	}

	/*
	 * While disks spinup behind PMP, some controllers fail sending SRST.
	 * They need to be reset - as well as the PMP - before retrying.
	 */
	if (rc == -ERESTART) {
		if (ata_is_host_link(link))
			ata_eh_thaw_port(ap);
		goto out;
	}

	if (try == max_tries - 1) {
		sata_down_spd_limit(link, 0);
		if (slave)
			sata_down_spd_limit(slave, 0);
	} else if (rc == -EPIPE)
		sata_down_spd_limit(failed_link, 0);

	if (hardreset)
		reset = hardreset;
	goto retry;
}