diff options
| author | Dan Williams <dan.j.williams@intel.com> | 2012-06-22 02:41:46 -0400 |
|---|---|---|
| committer | James Bottomley <JBottomley@Parallels.com> | 2012-08-24 05:10:23 -0400 |
| commit | 2fcbdcb4c802fe40d6827dbc365dac90cfe8c0a3 (patch) | |
| tree | d4a583b2477afcf474a4ec226406748d9a327613 | |
| parent | ca6d43b051b5a061b33c43303b6b4c93b46a34b5 (diff) | |
[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: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Jacek Danecki <jacek.danecki@intel.com>
Acked-by: Jeff Garzik <jgarzik@redhat.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
| -rw-r--r-- | drivers/ata/libata-core.c | 58 | ||||
| -rw-r--r-- | include/linux/libata.h | 11 |
2 files changed, 57 insertions, 12 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 70964aabcb89..09657c372d3b 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c | |||
| @@ -5252,16 +5252,20 @@ bool ata_link_offline(struct ata_link *link) | |||
| 5252 | #ifdef CONFIG_PM | 5252 | #ifdef CONFIG_PM |
| 5253 | static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, | 5253 | static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, |
| 5254 | unsigned int action, unsigned int ehi_flags, | 5254 | unsigned int action, unsigned int ehi_flags, |
| 5255 | int wait) | 5255 | int *async) |
| 5256 | { | 5256 | { |
| 5257 | struct ata_link *link; | 5257 | struct ata_link *link; |
| 5258 | unsigned long flags; | 5258 | unsigned long flags; |
| 5259 | int rc; | 5259 | int rc = 0; |
| 5260 | 5260 | ||
| 5261 | /* Previous resume operation might still be in | 5261 | /* Previous resume operation might still be in |
| 5262 | * progress. Wait for PM_PENDING to clear. | 5262 | * progress. Wait for PM_PENDING to clear. |
| 5263 | */ | 5263 | */ |
| 5264 | if (ap->pflags & ATA_PFLAG_PM_PENDING) { | 5264 | if (ap->pflags & ATA_PFLAG_PM_PENDING) { |
| 5265 | if (async) { | ||
| 5266 | *async = -EAGAIN; | ||
| 5267 | return 0; | ||
| 5268 | } | ||
| 5265 | ata_port_wait_eh(ap); | 5269 | ata_port_wait_eh(ap); |
| 5266 | WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); | 5270 | WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); |
| 5267 | } | 5271 | } |
| @@ -5270,10 +5274,10 @@ static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, | |||
| 5270 | spin_lock_irqsave(ap->lock, flags); | 5274 | spin_lock_irqsave(ap->lock, flags); |
| 5271 | 5275 | ||
| 5272 | ap->pm_mesg = mesg; | 5276 | ap->pm_mesg = mesg; |
| 5273 | if (wait) { | 5277 | if (async) |
| 5274 | rc = 0; | 5278 | ap->pm_result = async; |
| 5279 | else | ||
| 5275 | ap->pm_result = &rc; | 5280 | ap->pm_result = &rc; |
| 5276 | } | ||
| 5277 | 5281 | ||
| 5278 | ap->pflags |= ATA_PFLAG_PM_PENDING; | 5282 | ap->pflags |= ATA_PFLAG_PM_PENDING; |
| 5279 | ata_for_each_link(link, ap, HOST_FIRST) { | 5283 | 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 | spin_unlock_irqrestore(ap->lock, flags); | 5290 | spin_unlock_irqrestore(ap->lock, flags); |
| 5287 | 5291 | ||
| 5288 | /* wait and check result */ | 5292 | /* wait and check result */ |
| 5289 | if (wait) { | 5293 | if (!async) { |
| 5290 | ata_port_wait_eh(ap); | 5294 | ata_port_wait_eh(ap); |
| 5291 | WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); | 5295 | WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); |
| 5292 | } | 5296 | } |
| @@ -5294,9 +5298,8 @@ static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, | |||
| 5294 | return rc; | 5298 | return rc; |
| 5295 | } | 5299 | } |
| 5296 | 5300 | ||
| 5297 | static int ata_port_suspend_common(struct device *dev, pm_message_t mesg) | 5301 | static int __ata_port_suspend_common(struct ata_port *ap, pm_message_t mesg, int *async) |
| 5298 | { | 5302 | { |
| 5299 | struct ata_port *ap = to_ata_port(dev); | ||
| 5300 | unsigned int ehi_flags = ATA_EHI_QUIET; | 5303 | unsigned int ehi_flags = ATA_EHI_QUIET; |
| 5301 | int rc; | 5304 | int rc; |
| 5302 | 5305 | ||
| @@ -5311,10 +5314,17 @@ static int ata_port_suspend_common(struct device *dev, pm_message_t mesg) | |||
| 5311 | if (mesg.event == PM_EVENT_SUSPEND) | 5314 | if (mesg.event == PM_EVENT_SUSPEND) |
| 5312 | ehi_flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_NO_RECOVERY; | 5315 | ehi_flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_NO_RECOVERY; |
| 5313 | 5316 | ||
| 5314 | rc = ata_port_request_pm(ap, mesg, 0, ehi_flags, 1); | 5317 | rc = ata_port_request_pm(ap, mesg, 0, ehi_flags, async); |
| 5315 | return rc; | 5318 | return rc; |
| 5316 | } | 5319 | } |
| 5317 | 5320 | ||
| 5321 | static int ata_port_suspend_common(struct device *dev, pm_message_t mesg) | ||
| 5322 | { | ||
| 5323 | struct ata_port *ap = to_ata_port(dev); | ||
| 5324 | |||
| 5325 | return __ata_port_suspend_common(ap, mesg, NULL); | ||
| 5326 | } | ||
| 5327 | |||
| 5318 | static int ata_port_suspend(struct device *dev) | 5328 | static int ata_port_suspend(struct device *dev) |
| 5319 | { | 5329 | { |
| 5320 | if (pm_runtime_suspended(dev)) | 5330 | if (pm_runtime_suspended(dev)) |
| @@ -5339,16 +5349,22 @@ static int ata_port_poweroff(struct device *dev) | |||
| 5339 | return ata_port_suspend_common(dev, PMSG_HIBERNATE); | 5349 | return ata_port_suspend_common(dev, PMSG_HIBERNATE); |
| 5340 | } | 5350 | } |
| 5341 | 5351 | ||
| 5342 | static int ata_port_resume_common(struct device *dev) | 5352 | static int __ata_port_resume_common(struct ata_port *ap, int *async) |
| 5343 | { | 5353 | { |
| 5344 | struct ata_port *ap = to_ata_port(dev); | ||
| 5345 | int rc; | 5354 | int rc; |
| 5346 | 5355 | ||
| 5347 | rc = ata_port_request_pm(ap, PMSG_ON, ATA_EH_RESET, | 5356 | rc = ata_port_request_pm(ap, PMSG_ON, ATA_EH_RESET, |
| 5348 | ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 1); | 5357 | ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, async); |
| 5349 | return rc; | 5358 | return rc; |
| 5350 | } | 5359 | } |
| 5351 | 5360 | ||
| 5361 | static int ata_port_resume_common(struct device *dev) | ||
| 5362 | { | ||
| 5363 | struct ata_port *ap = to_ata_port(dev); | ||
| 5364 | |||
| 5365 | return __ata_port_resume_common(ap, NULL); | ||
| 5366 | } | ||
| 5367 | |||
| 5352 | static int ata_port_resume(struct device *dev) | 5368 | static int ata_port_resume(struct device *dev) |
| 5353 | { | 5369 | { |
| 5354 | int rc; | 5370 | int rc; |
| @@ -5381,6 +5397,24 @@ static const struct dev_pm_ops ata_port_pm_ops = { | |||
| 5381 | .runtime_idle = ata_port_runtime_idle, | 5397 | .runtime_idle = ata_port_runtime_idle, |
| 5382 | }; | 5398 | }; |
| 5383 | 5399 | ||
| 5400 | /* sas ports don't participate in pm runtime management of ata_ports, | ||
| 5401 | * and need to resume ata devices at the domain level, not the per-port | ||
| 5402 | * level. sas suspend/resume is async to allow parallel port recovery | ||
| 5403 | * since sas has multiple ata_port instances per Scsi_Host. | ||
| 5404 | */ | ||
| 5405 | int ata_sas_port_async_suspend(struct ata_port *ap, int *async) | ||
| 5406 | { | ||
| 5407 | return __ata_port_suspend_common(ap, PMSG_SUSPEND, async); | ||
| 5408 | } | ||
| 5409 | EXPORT_SYMBOL_GPL(ata_sas_port_async_suspend); | ||
| 5410 | |||
| 5411 | int ata_sas_port_async_resume(struct ata_port *ap, int *async) | ||
| 5412 | { | ||
| 5413 | return __ata_port_resume_common(ap, async); | ||
| 5414 | } | ||
| 5415 | EXPORT_SYMBOL_GPL(ata_sas_port_async_resume); | ||
| 5416 | |||
| 5417 | |||
| 5384 | /** | 5418 | /** |
| 5385 | * ata_host_suspend - suspend host | 5419 | * ata_host_suspend - suspend host |
| 5386 | * @host: host to suspend | 5420 | * @host: host to suspend |
diff --git a/include/linux/libata.h b/include/linux/libata.h index 31a2853e9530..cc834e1136b2 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h | |||
| @@ -1013,6 +1013,17 @@ extern bool ata_link_offline(struct ata_link *link); | |||
| 1013 | #ifdef CONFIG_PM | 1013 | #ifdef CONFIG_PM |
| 1014 | extern int ata_host_suspend(struct ata_host *host, pm_message_t mesg); | 1014 | extern int ata_host_suspend(struct ata_host *host, pm_message_t mesg); |
| 1015 | extern void ata_host_resume(struct ata_host *host); | 1015 | extern void ata_host_resume(struct ata_host *host); |
| 1016 | extern int ata_sas_port_async_suspend(struct ata_port *ap, int *async); | ||
| 1017 | extern int ata_sas_port_async_resume(struct ata_port *ap, int *async); | ||
| 1018 | #else | ||
| 1019 | static inline int ata_sas_port_async_suspend(struct ata_port *ap, int *async) | ||
| 1020 | { | ||
| 1021 | return 0; | ||
| 1022 | } | ||
| 1023 | static inline int ata_sas_port_async_resume(struct ata_port *ap, int *async) | ||
| 1024 | { | ||
| 1025 | return 0; | ||
| 1026 | } | ||
| 1016 | #endif | 1027 | #endif |
| 1017 | extern int ata_ratelimit(void); | 1028 | extern int ata_ratelimit(void); |
| 1018 | extern void ata_msleep(struct ata_port *ap, unsigned int msecs); | 1029 | extern void ata_msleep(struct ata_port *ap, unsigned int msecs); |
