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: Tejun Heo <htejun@gmail.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
This commit is contained in:
@@ -77,29 +77,12 @@ static void ata_eh_finish(struct ata_port *ap);
|
||||
#ifdef CONFIG_PM
|
||||
static void ata_eh_handle_port_suspend(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 */
|
||||
static void ata_eh_handle_port_suspend(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 */
|
||||
|
||||
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)
|
||||
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)) {
|
||||
rc = -EIO;
|
||||
goto err;
|
||||
@@ -1872,166 +1855,6 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap,
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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 */
|
||||
if ((ap->pflags & ATA_PFLAG_FROZEN) ||
|
||||
(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)
|
||||
goto out;
|
||||
|
||||
/* prep for resume */
|
||||
ata_eh_prep_resume(ap);
|
||||
|
||||
/* skip EH if possible. */
|
||||
if (ata_eh_skip_recovery(ap))
|
||||
ehc->i.action = 0;
|
||||
@@ -2177,11 +1986,6 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
|
||||
if (rc)
|
||||
goto dev_fail;
|
||||
|
||||
/* resume devices */
|
||||
rc = ata_eh_resume(ap, &dev);
|
||||
if (rc)
|
||||
goto dev_fail;
|
||||
|
||||
/* configure transfer mode if necessary */
|
||||
if (ehc->i.flags & ATA_EHI_SETMODE) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* suspend devices */
|
||||
rc = ata_eh_suspend(ap, &dev);
|
||||
if (rc)
|
||||
goto dev_fail;
|
||||
|
||||
goto out;
|
||||
|
||||
dev_fail:
|
||||
@@ -2390,22 +2189,13 @@ static void ata_eh_handle_port_suspend(struct ata_port *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:
|
||||
* Kernel thread context (may sleep).
|
||||
*/
|
||||
static void ata_eh_handle_port_resume(struct ata_port *ap)
|
||||
{
|
||||
unsigned long timeout;
|
||||
unsigned long flags;
|
||||
int i, rc = 0;
|
||||
int rc = 0;
|
||||
|
||||
/* are we resuming? */
|
||||
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);
|
||||
|
||||
/* spurious? */
|
||||
if (!(ap->pflags & ATA_PFLAG_SUSPENDED))
|
||||
goto done;
|
||||
WARN_ON(!(ap->pflags & ATA_PFLAG_SUSPENDED));
|
||||
|
||||
if (ap->ops->port_resume)
|
||||
rc = ap->ops->port_resume(ap);
|
||||
|
||||
/* give devices time to request EH */
|
||||
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:
|
||||
/* report result */
|
||||
spin_lock_irqsave(ap->lock, flags);
|
||||
ap->pflags &= ~(ATA_PFLAG_PM_PENDING | ATA_PFLAG_SUSPENDED);
|
||||
if (ap->pm_result) {
|
||||
|
Reference in New Issue
Block a user