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

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

libata: perform port detach in EH



ata_port_detach() first made sure EH saw ATA_PFLAG_UNLOADING and then
assumed EH context belongs to it and performed detach operation
itself.  However, UNLOADING doesn't disable all of EH and this could
lead to problems including triggering WARN_ON()'s in EH path.

This patch makes port detach behave more like other EH actions such
that ata_port_detach() requests EH to detach and waits for completion.

Signed-off-by: default avatarTejun Heo <tj@kernel.org>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent ad74e4c1
Loading
Loading
Loading
Loading
+4 −19
Original line number Diff line number Diff line
@@ -6107,8 +6107,6 @@ int ata_host_activate(struct ata_host *host, int irq,
static void ata_port_detach(struct ata_port *ap)
{
	unsigned long flags;
	struct ata_link *link;
	struct ata_device *dev;

	if (!ap->ops->error_handler)
		goto skip_eh;
@@ -6116,28 +6114,15 @@ static void ata_port_detach(struct ata_port *ap)
	/* tell EH we're leaving & flush EH */
	spin_lock_irqsave(ap->lock, flags);
	ap->pflags |= ATA_PFLAG_UNLOADING;
	ata_port_schedule_eh(ap);
	spin_unlock_irqrestore(ap->lock, flags);

	/* wait till EH commits suicide */
	ata_port_wait_eh(ap);

	/* EH is now guaranteed to see UNLOADING - EH context belongs
	 * to us.  Restore SControl and disable all existing devices.
	 */
	ata_for_each_link(link, ap, PMP_FIRST) {
		sata_scr_write(link, SCR_CONTROL, link->saved_scontrol & 0xff0);
		ata_for_each_dev(dev, link, ALL)
			ata_dev_disable(dev);
	}

	/* Final freeze & EH.  All in-flight commands are aborted.  EH
	 * will be skipped and retrials will be terminated with bad
	 * target.
	 */
	spin_lock_irqsave(ap->lock, flags);
	ata_port_freeze(ap);	/* won't be thawed */
	spin_unlock_irqrestore(ap->lock, flags);
	/* it better be dead now */
	WARN_ON(!(ap->pflags & ATA_PFLAG_UNLOADED));

	ata_port_wait_eh(ap);
	cancel_rearming_delayed_work(&ap->hotplug_task);

 skip_eh:
+31 −1
Original line number Diff line number Diff line
@@ -491,6 +491,31 @@ enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd)
	return ret;
}

static void ata_eh_unload(struct ata_port *ap)
{
	struct ata_link *link;
	struct ata_device *dev;
	unsigned long flags;

	/* Restore SControl IPM and SPD for the next driver and
	 * disable attached devices.
	 */
	ata_for_each_link(link, ap, PMP_FIRST) {
		sata_scr_write(link, SCR_CONTROL, link->saved_scontrol & 0xff0);
		ata_for_each_dev(dev, link, ALL)
			ata_dev_disable(dev);
	}

	/* freeze and set UNLOADED */
	spin_lock_irqsave(ap->lock, flags);

	ata_port_freeze(ap);			/* won't be thawed */
	ap->pflags &= ~ATA_PFLAG_EH_PENDING;	/* clear pending from freeze */
	ap->pflags |= ATA_PFLAG_UNLOADED;

	spin_unlock_irqrestore(ap->lock, flags);
}

/**
 *	ata_scsi_error - SCSI layer error handler callback
 *	@host: SCSI host on which error occurred
@@ -618,8 +643,13 @@ void ata_scsi_error(struct Scsi_Host *host)
		/* invoke EH, skip if unloading or suspended */
		if (!(ap->pflags & (ATA_PFLAG_UNLOADING | ATA_PFLAG_SUSPENDED)))
			ap->ops->error_handler(ap);
		else
		else {
			/* if unloading, commence suicide */
			if ((ap->pflags & ATA_PFLAG_UNLOADING) &&
			    !(ap->pflags & ATA_PFLAG_UNLOADED))
				ata_eh_unload(ap);
			ata_eh_finish(ap);
		}

		/* process port suspend request */
		ata_eh_handle_port_suspend(ap);
+2 −1
Original line number Diff line number Diff line
@@ -213,10 +213,11 @@ enum {
	ATA_PFLAG_FROZEN	= (1 << 2), /* port is frozen */
	ATA_PFLAG_RECOVERED	= (1 << 3), /* recovery action performed */
	ATA_PFLAG_LOADING	= (1 << 4), /* boot/loading probe */
	ATA_PFLAG_UNLOADING	= (1 << 5), /* module is unloading */
	ATA_PFLAG_SCSI_HOTPLUG	= (1 << 6), /* SCSI hotplug scheduled */
	ATA_PFLAG_INITIALIZING	= (1 << 7), /* being initialized, don't touch */
	ATA_PFLAG_RESETTING	= (1 << 8), /* reset in progress */
	ATA_PFLAG_UNLOADING	= (1 << 9), /* driver is being unloaded */
	ATA_PFLAG_UNLOADED	= (1 << 10), /* driver is unloaded */

	ATA_PFLAG_SUSPENDED	= (1 << 17), /* port is suspended (power) */
	ATA_PFLAG_PM_PENDING	= (1 << 18), /* PM operation pending */