Commit 2fcbdcb4 authored by Dan Williams's avatar Dan Williams Committed by James Bottomley

[SCSI] libata: export ata_port suspend/resume infrastructure for sas

Reuse ata_port_{suspend|resume}_common for sas.  This path is chosen
over adding coordination between ata-tranport and sas-transport because
libsas wants to revalidate the domain at resume-time at the host level.
It can not validate links have resumed properly until libata has had a
chance to perform its revalidation, and any sane placing of an ata_port
in the sas-transport model would delay it's resumption until after the
host.

Export the common portion of port suspend/resume (bypass pm_runtime),
and allow sas to perform these operations asynchronously (similar to the
libsas async-ata probe implmentation).  Async operation is determined by
having an external, rather than stack based, location for storing the
result of the operation.
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
Reviewed-by: default avatarJacek Danecki <jacek.danecki@intel.com>
Acked-by: default avatarJeff Garzik <jgarzik@redhat.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent ca6d43b0
...@@ -5252,16 +5252,20 @@ bool ata_link_offline(struct ata_link *link) ...@@ -5252,16 +5252,20 @@ bool ata_link_offline(struct ata_link *link)
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg,
unsigned int action, unsigned int ehi_flags, unsigned int action, unsigned int ehi_flags,
int wait) int *async)
{ {
struct ata_link *link; struct ata_link *link;
unsigned long flags; unsigned long flags;
int rc; int rc = 0;
/* Previous resume operation might still be in /* Previous resume operation might still be in
* progress. Wait for PM_PENDING to clear. * progress. Wait for PM_PENDING to clear.
*/ */
if (ap->pflags & ATA_PFLAG_PM_PENDING) { if (ap->pflags & ATA_PFLAG_PM_PENDING) {
if (async) {
*async = -EAGAIN;
return 0;
}
ata_port_wait_eh(ap); ata_port_wait_eh(ap);
WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
} }
...@@ -5270,10 +5274,10 @@ static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, ...@@ -5270,10 +5274,10 @@ static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg,
spin_lock_irqsave(ap->lock, flags); spin_lock_irqsave(ap->lock, flags);
ap->pm_mesg = mesg; ap->pm_mesg = mesg;
if (wait) { if (async)
rc = 0; ap->pm_result = async;
else
ap->pm_result = &rc; ap->pm_result = &rc;
}
ap->pflags |= ATA_PFLAG_PM_PENDING; ap->pflags |= ATA_PFLAG_PM_PENDING;
ata_for_each_link(link, ap, HOST_FIRST) { ata_for_each_link(link, ap, HOST_FIRST) {
...@@ -5286,7 +5290,7 @@ static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, ...@@ -5286,7 +5290,7 @@ static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg,
spin_unlock_irqrestore(ap->lock, flags); spin_unlock_irqrestore(ap->lock, flags);
/* wait and check result */ /* wait and check result */
if (wait) { if (!async) {
ata_port_wait_eh(ap); ata_port_wait_eh(ap);
WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
} }
...@@ -5294,9 +5298,8 @@ static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, ...@@ -5294,9 +5298,8 @@ static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg,
return rc; return rc;
} }
static int ata_port_suspend_common(struct device *dev, pm_message_t mesg) static int __ata_port_suspend_common(struct ata_port *ap, pm_message_t mesg, int *async)
{ {
struct ata_port *ap = to_ata_port(dev);
unsigned int ehi_flags = ATA_EHI_QUIET; unsigned int ehi_flags = ATA_EHI_QUIET;
int rc; int rc;
...@@ -5311,10 +5314,17 @@ static int ata_port_suspend_common(struct device *dev, pm_message_t mesg) ...@@ -5311,10 +5314,17 @@ static int ata_port_suspend_common(struct device *dev, pm_message_t mesg)
if (mesg.event == PM_EVENT_SUSPEND) if (mesg.event == PM_EVENT_SUSPEND)
ehi_flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_NO_RECOVERY; ehi_flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_NO_RECOVERY;
rc = ata_port_request_pm(ap, mesg, 0, ehi_flags, 1); rc = ata_port_request_pm(ap, mesg, 0, ehi_flags, async);
return rc; return rc;
} }
static int ata_port_suspend_common(struct device *dev, pm_message_t mesg)
{
struct ata_port *ap = to_ata_port(dev);
return __ata_port_suspend_common(ap, mesg, NULL);
}
static int ata_port_suspend(struct device *dev) static int ata_port_suspend(struct device *dev)
{ {
if (pm_runtime_suspended(dev)) if (pm_runtime_suspended(dev))
...@@ -5339,16 +5349,22 @@ static int ata_port_poweroff(struct device *dev) ...@@ -5339,16 +5349,22 @@ static int ata_port_poweroff(struct device *dev)
return ata_port_suspend_common(dev, PMSG_HIBERNATE); return ata_port_suspend_common(dev, PMSG_HIBERNATE);
} }
static int ata_port_resume_common(struct device *dev) static int __ata_port_resume_common(struct ata_port *ap, int *async)
{ {
struct ata_port *ap = to_ata_port(dev);
int rc; int rc;
rc = ata_port_request_pm(ap, PMSG_ON, ATA_EH_RESET, rc = ata_port_request_pm(ap, PMSG_ON, ATA_EH_RESET,
ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 1); ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, async);
return rc; return rc;
} }
static int ata_port_resume_common(struct device *dev)
{
struct ata_port *ap = to_ata_port(dev);
return __ata_port_resume_common(ap, NULL);
}
static int ata_port_resume(struct device *dev) static int ata_port_resume(struct device *dev)
{ {
int rc; int rc;
...@@ -5381,6 +5397,24 @@ static const struct dev_pm_ops ata_port_pm_ops = { ...@@ -5381,6 +5397,24 @@ static const struct dev_pm_ops ata_port_pm_ops = {
.runtime_idle = ata_port_runtime_idle, .runtime_idle = ata_port_runtime_idle,
}; };
/* sas ports don't participate in pm runtime management of ata_ports,
* and need to resume ata devices at the domain level, not the per-port
* level. sas suspend/resume is async to allow parallel port recovery
* since sas has multiple ata_port instances per Scsi_Host.
*/
int ata_sas_port_async_suspend(struct ata_port *ap, int *async)
{
return __ata_port_suspend_common(ap, PMSG_SUSPEND, async);
}
EXPORT_SYMBOL_GPL(ata_sas_port_async_suspend);
int ata_sas_port_async_resume(struct ata_port *ap, int *async)
{
return __ata_port_resume_common(ap, async);
}
EXPORT_SYMBOL_GPL(ata_sas_port_async_resume);
/** /**
* ata_host_suspend - suspend host * ata_host_suspend - suspend host
* @host: host to suspend * @host: host to suspend
......
...@@ -1013,6 +1013,17 @@ extern bool ata_link_offline(struct ata_link *link); ...@@ -1013,6 +1013,17 @@ extern bool ata_link_offline(struct ata_link *link);
#ifdef CONFIG_PM #ifdef CONFIG_PM
extern int ata_host_suspend(struct ata_host *host, pm_message_t mesg); extern int ata_host_suspend(struct ata_host *host, pm_message_t mesg);
extern void ata_host_resume(struct ata_host *host); extern void ata_host_resume(struct ata_host *host);
extern int ata_sas_port_async_suspend(struct ata_port *ap, int *async);
extern int ata_sas_port_async_resume(struct ata_port *ap, int *async);
#else
static inline int ata_sas_port_async_suspend(struct ata_port *ap, int *async)
{
return 0;
}
static inline int ata_sas_port_async_resume(struct ata_port *ap, int *async)
{
return 0;
}
#endif #endif
extern int ata_ratelimit(void); extern int ata_ratelimit(void);
extern void ata_msleep(struct ata_port *ap, unsigned int msecs); extern void ata_msleep(struct ata_port *ap, unsigned int msecs);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment