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

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

libata: reimplement suspend/resume support using sdev->manage_start_stop



Reimplement suspend/resume support using sdev->manage_start_stop.

* Device suspend/resume is now SCSI layer's responsibility and the
  code is simplified a lot.

* DPM is dropped.  This also simplifies code a lot.  Suspend/resume
  status is port-wide now.

* ata_scsi_device_suspend/resume() and ata_dev_ready() removed.

* Resume now has to wait for disk to spin up before proceeding.  I
  couldn't find easy way out as libata is in EH waiting for the
  disk to be ready and sd is waiting for EH to complete to issue
  START_STOP.

* sdev->manage_start_stop is set to 1 in ata_scsi_slave_config().
  This fixes spindown on shutdown and suspend-to-disk.

Signed-off-by: default avatarTejun Heo <htejun@gmail.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent 0a3fd051
Loading
Loading
Loading
Loading
+0 −4
Original line number Original line Diff line number Diff line
@@ -250,10 +250,6 @@ static struct scsi_host_template ahci_sht = {
	.slave_configure	= ata_scsi_slave_config,
	.slave_configure	= ata_scsi_slave_config,
	.slave_destroy		= ata_scsi_slave_destroy,
	.slave_destroy		= ata_scsi_slave_destroy,
	.bios_param		= ata_std_bios_param,
	.bios_param		= ata_std_bios_param,
#ifdef CONFIG_PM
	.suspend		= ata_scsi_device_suspend,
	.resume			= ata_scsi_device_resume,
#endif
};
};


static const struct ata_port_operations ahci_ops = {
static const struct ata_port_operations ahci_ops = {
+1 −5
Original line number Original line Diff line number Diff line
@@ -54,7 +54,7 @@ static int generic_set_mode(struct ata_port *ap, struct ata_device **unused)


	for (i = 0; i < ATA_MAX_DEVICES; i++) {
	for (i = 0; i < ATA_MAX_DEVICES; i++) {
		struct ata_device *dev = &ap->device[i];
		struct ata_device *dev = &ap->device[i];
		if (ata_dev_ready(dev)) {
		if (ata_dev_enabled(dev)) {
			/* We don't really care */
			/* We don't really care */
			dev->pio_mode = XFER_PIO_0;
			dev->pio_mode = XFER_PIO_0;
			dev->dma_mode = XFER_MW_DMA_0;
			dev->dma_mode = XFER_MW_DMA_0;
@@ -90,10 +90,6 @@ static struct scsi_host_template generic_sht = {
	.slave_configure	= ata_scsi_slave_config,
	.slave_configure	= ata_scsi_slave_config,
	.slave_destroy		= ata_scsi_slave_destroy,
	.slave_destroy		= ata_scsi_slave_destroy,
	.bios_param		= ata_std_bios_param,
	.bios_param		= ata_std_bios_param,
#ifdef CONFIG_PM
	.resume			= ata_scsi_device_resume,
	.suspend		= ata_scsi_device_suspend,
#endif
};
};


static struct ata_port_operations generic_port_ops = {
static struct ata_port_operations generic_port_ops = {
+0 −4
Original line number Original line Diff line number Diff line
@@ -275,10 +275,6 @@ static struct scsi_host_template piix_sht = {
	.slave_configure	= ata_scsi_slave_config,
	.slave_configure	= ata_scsi_slave_config,
	.slave_destroy		= ata_scsi_slave_destroy,
	.slave_destroy		= ata_scsi_slave_destroy,
	.bios_param		= ata_std_bios_param,
	.bios_param		= ata_std_bios_param,
#ifdef CONFIG_PM
	.resume			= ata_scsi_device_resume,
	.suspend		= ata_scsi_device_suspend,
#endif
};
};


static const struct ata_port_operations piix_pata_ops = {
static const struct ata_port_operations piix_pata_ops = {
+4 −35
Original line number Original line Diff line number Diff line
@@ -2860,7 +2860,7 @@ int ata_do_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev)
		dev = &ap->device[i];
		dev = &ap->device[i];


		/* don't update suspended devices' xfer mode */
		/* don't update suspended devices' xfer mode */
		if (!ata_dev_ready(dev))
		if (!ata_dev_enabled(dev))
			continue;
			continue;


		rc = ata_dev_set_mode(dev);
		rc = ata_dev_set_mode(dev);
@@ -5845,37 +5845,11 @@ static int ata_host_request_pm(struct ata_host *host, pm_message_t mesg,
 */
 */
int ata_host_suspend(struct ata_host *host, pm_message_t mesg)
int ata_host_suspend(struct ata_host *host, pm_message_t mesg)
{
{
	int i, j, rc;
	int rc;


	rc = ata_host_request_pm(host, mesg, 0, ATA_EHI_QUIET, 1);
	rc = ata_host_request_pm(host, mesg, 0, ATA_EHI_QUIET, 1);
	if (rc)
	if (rc == 0)
		goto fail;

	/* EH is quiescent now.  Fail if we have any ready device.
	 * This happens if hotplug occurs between completion of device
	 * suspension and here.
	 */
	for (i = 0; i < host->n_ports; i++) {
		struct ata_port *ap = host->ports[i];

		for (j = 0; j < ATA_MAX_DEVICES; j++) {
			struct ata_device *dev = &ap->device[j];

			if (ata_dev_ready(dev)) {
				ata_port_printk(ap, KERN_WARNING,
						"suspend failed, device %d "
						"still active\n", dev->devno);
				rc = -EBUSY;
				goto fail;
			}
		}
	}

		host->dev->power.power_state = mesg;
		host->dev->power.power_state = mesg;
	return 0;

 fail:
	ata_host_resume(host);
	return rc;
	return rc;
}
}


@@ -6889,11 +6863,6 @@ EXPORT_SYMBOL_GPL(ata_pci_default_filter);
EXPORT_SYMBOL_GPL(ata_pci_clear_simplex);
EXPORT_SYMBOL_GPL(ata_pci_clear_simplex);
#endif /* CONFIG_PCI */
#endif /* CONFIG_PCI */


#ifdef CONFIG_PM
EXPORT_SYMBOL_GPL(ata_scsi_device_suspend);
EXPORT_SYMBOL_GPL(ata_scsi_device_resume);
#endif /* CONFIG_PM */

EXPORT_SYMBOL_GPL(ata_eng_timeout);
EXPORT_SYMBOL_GPL(ata_eng_timeout);
EXPORT_SYMBOL_GPL(ata_port_schedule_eh);
EXPORT_SYMBOL_GPL(ata_port_schedule_eh);
EXPORT_SYMBOL_GPL(ata_port_abort);
EXPORT_SYMBOL_GPL(ata_port_abort);
+4 −233
Original line number Original line Diff line number Diff line
@@ -77,29 +77,12 @@ static void ata_eh_finish(struct ata_port *ap);
#ifdef CONFIG_PM
#ifdef CONFIG_PM
static void ata_eh_handle_port_suspend(struct ata_port *ap);
static void ata_eh_handle_port_suspend(struct ata_port *ap);
static void ata_eh_handle_port_resume(struct ata_port *ap);
static void ata_eh_handle_port_resume(struct ata_port *ap);
static int ata_eh_suspend(struct ata_port *ap,
			  struct ata_device **r_failed_dev);
static void ata_eh_prep_resume(struct ata_port *ap);
static int ata_eh_resume(struct ata_port *ap, struct ata_device **r_failed_dev);
#else /* CONFIG_PM */
#else /* CONFIG_PM */
static void ata_eh_handle_port_suspend(struct ata_port *ap)
static void ata_eh_handle_port_suspend(struct ata_port *ap)
{ }
{ }


static void ata_eh_handle_port_resume(struct ata_port *ap)
static void ata_eh_handle_port_resume(struct ata_port *ap)
{ }
{ }

static int ata_eh_suspend(struct ata_port *ap, struct ata_device **r_failed_dev)
{
	return 0;
}

static void ata_eh_prep_resume(struct ata_port *ap)
{ }

static int ata_eh_resume(struct ata_port *ap, struct ata_device **r_failed_dev)
{
	return 0;
}
#endif /* CONFIG_PM */
#endif /* CONFIG_PM */


static void ata_ering_record(struct ata_ering *ering, int is_io,
static void ata_ering_record(struct ata_ering *ering, int is_io,
@@ -1791,7 +1774,7 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap,
		if (ehc->i.flags & ATA_EHI_DID_RESET)
		if (ehc->i.flags & ATA_EHI_DID_RESET)
			readid_flags |= ATA_READID_POSTRESET;
			readid_flags |= ATA_READID_POSTRESET;


		if (action & ATA_EH_REVALIDATE && ata_dev_ready(dev)) {
		if ((action & ATA_EH_REVALIDATE) && ata_dev_enabled(dev)) {
			if (ata_port_offline(ap)) {
			if (ata_port_offline(ap)) {
				rc = -EIO;
				rc = -EIO;
				goto err;
				goto err;
@@ -1872,166 +1855,6 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap,
	return rc;
	return rc;
}
}


#ifdef CONFIG_PM
/**
 *	ata_eh_suspend - handle suspend EH action
 *	@ap: target host port
 *	@r_failed_dev: result parameter to indicate failing device
 *
 *	Handle suspend EH action.  Disk devices are spinned down and
 *	other types of devices are just marked suspended.  Once
 *	suspended, no EH action to the device is allowed until it is
 *	resumed.
 *
 *	LOCKING:
 *	Kernel thread context (may sleep).
 *
 *	RETURNS:
 *	0 on success, -errno otherwise
 */
static int ata_eh_suspend(struct ata_port *ap, struct ata_device **r_failed_dev)
{
	struct ata_device *dev;
	int i, rc = 0;

	DPRINTK("ENTER\n");

	for (i = 0; i < ATA_MAX_DEVICES; i++) {
		unsigned long flags;
		unsigned int action, err_mask;

		dev = &ap->device[i];
		action = ata_eh_dev_action(dev);

		if (!ata_dev_enabled(dev) || !(action & ATA_EH_SUSPEND))
			continue;

		WARN_ON(dev->flags & ATA_DFLAG_SUSPENDED);

		ata_eh_about_to_do(ap, dev, ATA_EH_SUSPEND);

		if (dev->class == ATA_DEV_ATA && !(action & ATA_EH_PM_FREEZE)) {
			/* flush cache */
			rc = ata_flush_cache(dev);
			if (rc)
				break;

			/* spin down */
			err_mask = ata_do_simple_cmd(dev, ATA_CMD_STANDBYNOW1);
			if (err_mask) {
				ata_dev_printk(dev, KERN_ERR, "failed to "
					       "spin down (err_mask=0x%x)\n",
					       err_mask);
				rc = -EIO;
				break;
			}
		}

		spin_lock_irqsave(ap->lock, flags);
		dev->flags |= ATA_DFLAG_SUSPENDED;
		spin_unlock_irqrestore(ap->lock, flags);

		ata_eh_done(ap, dev, ATA_EH_SUSPEND);
	}

	if (rc)
		*r_failed_dev = dev;

	DPRINTK("EXIT\n");
	return rc;
}

/**
 *	ata_eh_prep_resume - prep for resume EH action
 *	@ap: target host port
 *
 *	Clear SUSPENDED in preparation for scheduled resume actions.
 *	This allows other parts of EH to access the devices being
 *	resumed.
 *
 *	LOCKING:
 *	Kernel thread context (may sleep).
 */
static void ata_eh_prep_resume(struct ata_port *ap)
{
	struct ata_device *dev;
	unsigned long flags;
	int i;

	DPRINTK("ENTER\n");

	for (i = 0; i < ATA_MAX_DEVICES; i++) {
		unsigned int action;

		dev = &ap->device[i];
		action = ata_eh_dev_action(dev);

		if (!ata_dev_enabled(dev) || !(action & ATA_EH_RESUME))
			continue;

		spin_lock_irqsave(ap->lock, flags);
		dev->flags &= ~ATA_DFLAG_SUSPENDED;
		spin_unlock_irqrestore(ap->lock, flags);
	}

	DPRINTK("EXIT\n");
}

/**
 *	ata_eh_resume - handle resume EH action
 *	@ap: target host port
 *	@r_failed_dev: result parameter to indicate failing device
 *
 *	Handle resume EH action.  Target devices are already reset and
 *	revalidated.  Spinning up is the only operation left.
 *
 *	LOCKING:
 *	Kernel thread context (may sleep).
 *
 *	RETURNS:
 *	0 on success, -errno otherwise
 */
static int ata_eh_resume(struct ata_port *ap, struct ata_device **r_failed_dev)
{
	struct ata_device *dev;
	int i, rc = 0;

	DPRINTK("ENTER\n");

	for (i = 0; i < ATA_MAX_DEVICES; i++) {
		unsigned int action, err_mask;

		dev = &ap->device[i];
		action = ata_eh_dev_action(dev);

		if (!ata_dev_enabled(dev) || !(action & ATA_EH_RESUME))
			continue;

		ata_eh_about_to_do(ap, dev, ATA_EH_RESUME);

		if (dev->class == ATA_DEV_ATA && !(action & ATA_EH_PM_FREEZE)) {
			err_mask = ata_do_simple_cmd(dev,
						     ATA_CMD_IDLEIMMEDIATE);
			if (err_mask) {
				ata_dev_printk(dev, KERN_ERR, "failed to "
					       "spin up (err_mask=0x%x)\n",
					       err_mask);
				rc = -EIO;
				break;
			}
		}

		ata_eh_done(ap, dev, ATA_EH_RESUME);
	}

	if (rc)
		*r_failed_dev = dev;

	DPRINTK("EXIT\n");
	return 0;
}
#endif /* CONFIG_PM */

static int ata_port_nr_enabled(struct ata_port *ap)
static int ata_port_nr_enabled(struct ata_port *ap)
{
{
	int i, cnt = 0;
	int i, cnt = 0;
@@ -2057,17 +1880,6 @@ static int ata_eh_skip_recovery(struct ata_port *ap)
	struct ata_eh_context *ehc = &ap->eh_context;
	struct ata_eh_context *ehc = &ap->eh_context;
	int i;
	int i;


	/* skip if all possible devices are suspended */
	for (i = 0; i < ata_port_max_devices(ap); i++) {
		struct ata_device *dev = &ap->device[i];

		if (!(dev->flags & ATA_DFLAG_SUSPENDED))
			break;
	}

	if (i == ata_port_max_devices(ap))
		return 1;

	/* thaw frozen port, resume link and recover failed devices */
	/* thaw frozen port, resume link and recover failed devices */
	if ((ap->pflags & ATA_PFLAG_FROZEN) ||
	if ((ap->pflags & ATA_PFLAG_FROZEN) ||
	    (ehc->i.flags & ATA_EHI_RESUME_LINK) || ata_port_nr_enabled(ap))
	    (ehc->i.flags & ATA_EHI_RESUME_LINK) || ata_port_nr_enabled(ap))
@@ -2147,9 +1959,6 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
	if (ap->pflags & ATA_PFLAG_UNLOADING)
	if (ap->pflags & ATA_PFLAG_UNLOADING)
		goto out;
		goto out;


	/* prep for resume */
	ata_eh_prep_resume(ap);

	/* skip EH if possible. */
	/* skip EH if possible. */
	if (ata_eh_skip_recovery(ap))
	if (ata_eh_skip_recovery(ap))
		ehc->i.action = 0;
		ehc->i.action = 0;
@@ -2177,11 +1986,6 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
	if (rc)
	if (rc)
		goto dev_fail;
		goto dev_fail;


	/* resume devices */
	rc = ata_eh_resume(ap, &dev);
	if (rc)
		goto dev_fail;

	/* configure transfer mode if necessary */
	/* configure transfer mode if necessary */
	if (ehc->i.flags & ATA_EHI_SETMODE) {
	if (ehc->i.flags & ATA_EHI_SETMODE) {
		rc = ata_set_mode(ap, &dev);
		rc = ata_set_mode(ap, &dev);
@@ -2190,11 +1994,6 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
		ehc->i.flags &= ~ATA_EHI_SETMODE;
		ehc->i.flags &= ~ATA_EHI_SETMODE;
	}
	}


	/* suspend devices */
	rc = ata_eh_suspend(ap, &dev);
	if (rc)
		goto dev_fail;

	goto out;
	goto out;


 dev_fail:
 dev_fail:
@@ -2390,22 +2189,13 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap)
 *
 *
 *	Resume @ap.
 *	Resume @ap.
 *
 *
 *	This function also waits upto one second until all devices
 *	hanging off this port requests resume EH action.  This is to
 *	prevent invoking EH and thus reset multiple times on resume.
 *
 *	On DPM resume, where some of devices might not be resumed
 *	together, this may delay port resume upto one second, but such
 *	DPM resumes are rare and 1 sec delay isn't too bad.
 *
 *	LOCKING:
 *	LOCKING:
 *	Kernel thread context (may sleep).
 *	Kernel thread context (may sleep).
 */
 */
static void ata_eh_handle_port_resume(struct ata_port *ap)
static void ata_eh_handle_port_resume(struct ata_port *ap)
{
{
	unsigned long timeout;
	unsigned long flags;
	unsigned long flags;
	int i, rc = 0;
	int rc = 0;


	/* are we resuming? */
	/* are we resuming? */
	spin_lock_irqsave(ap->lock, flags);
	spin_lock_irqsave(ap->lock, flags);
@@ -2416,31 +2206,12 @@ static void ata_eh_handle_port_resume(struct ata_port *ap)
	}
	}
	spin_unlock_irqrestore(ap->lock, flags);
	spin_unlock_irqrestore(ap->lock, flags);


	/* spurious? */
	WARN_ON(!(ap->pflags & ATA_PFLAG_SUSPENDED));
	if (!(ap->pflags & ATA_PFLAG_SUSPENDED))
		goto done;


	if (ap->ops->port_resume)
	if (ap->ops->port_resume)
		rc = ap->ops->port_resume(ap);
		rc = ap->ops->port_resume(ap);


	/* give devices time to request EH */
	/* report result */
	timeout = jiffies + HZ; /* 1s max */
	while (1) {
		for (i = 0; i < ATA_MAX_DEVICES; i++) {
			struct ata_device *dev = &ap->device[i];
			unsigned int action = ata_eh_dev_action(dev);

			if ((dev->flags & ATA_DFLAG_SUSPENDED) &&
			    !(action & ATA_EH_RESUME))
				break;
		}

		if (i == ATA_MAX_DEVICES || time_after(jiffies, timeout))
			break;
		msleep(10);
	}

 done:
	spin_lock_irqsave(ap->lock, flags);
	spin_lock_irqsave(ap->lock, flags);
	ap->pflags &= ~(ATA_PFLAG_PM_PENDING | ATA_PFLAG_SUSPENDED);
	ap->pflags &= ~(ATA_PFLAG_PM_PENDING | ATA_PFLAG_SUSPENDED);
	if (ap->pm_result) {
	if (ap->pm_result) {
Loading