aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libata-core.c
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2006-07-03 03:07:27 -0400
committerJeff Garzik <jeff@garzik.org>2006-07-05 22:16:28 -0400
commit500530f652f9e5dabe7571b018dec47742ce0f16 (patch)
treecb9653c45a7e37d9bfe8dcc3923ed6b33ca134ea /drivers/scsi/libata-core.c
parentd6f26d1f1f1128a896f38a7f8426daed0a1205a2 (diff)
[PATCH] libata: reimplement controller-wide PM
Reimplement controller-wide PM. ata_host_set_suspend/resume() are defined to suspend and resume a host_set. While suspended, EHs for all ports in the host_set are pegged using ATA_FLAG_SUSPENDED and frozen. Because SCSI device hotplug is done asynchronously against the rest of libata EH and the same mutex is used when adding new device, suspend cannot wait for hotplug to complete. So, if SCSI device hotplug is in progress, suspend fails with -EBUSY. In most cases, host_set resume is followed by device resume. As each resume operation requires a reset, a single host_set-wide resume operation may result in multiple resets. To avoid this, resume waits upto 1 second giving PM to request resume for devices. Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/scsi/libata-core.c')
-rw-r--r--drivers/scsi/libata-core.c165
1 files changed, 160 insertions, 5 deletions
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 51dbc5221934..fa65b990f8b0 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -5009,6 +5009,122 @@ int ata_flush_cache(struct ata_device *dev)
5009 return 0; 5009 return 0;
5010} 5010}
5011 5011
5012static int ata_host_set_request_pm(struct ata_host_set *host_set,
5013 pm_message_t mesg, unsigned int action,
5014 unsigned int ehi_flags, int wait)
5015{
5016 unsigned long flags;
5017 int i, rc;
5018
5019 for (i = 0; i < host_set->n_ports; i++) {
5020 struct ata_port *ap = host_set->ports[i];
5021
5022 /* Previous resume operation might still be in
5023 * progress. Wait for PM_PENDING to clear.
5024 */
5025 if (ap->pflags & ATA_PFLAG_PM_PENDING) {
5026 ata_port_wait_eh(ap);
5027 WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
5028 }
5029
5030 /* request PM ops to EH */
5031 spin_lock_irqsave(ap->lock, flags);
5032
5033 ap->pm_mesg = mesg;
5034 if (wait) {
5035 rc = 0;
5036 ap->pm_result = &rc;
5037 }
5038
5039 ap->pflags |= ATA_PFLAG_PM_PENDING;
5040 ap->eh_info.action |= action;
5041 ap->eh_info.flags |= ehi_flags;
5042
5043 ata_port_schedule_eh(ap);
5044
5045 spin_unlock_irqrestore(ap->lock, flags);
5046
5047 /* wait and check result */
5048 if (wait) {
5049 ata_port_wait_eh(ap);
5050 WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
5051 if (rc)
5052 return rc;
5053 }
5054 }
5055
5056 return 0;
5057}
5058
5059/**
5060 * ata_host_set_suspend - suspend host_set
5061 * @host_set: host_set to suspend
5062 * @mesg: PM message
5063 *
5064 * Suspend @host_set. Actual operation is performed by EH. This
5065 * function requests EH to perform PM operations and waits for EH
5066 * to finish.
5067 *
5068 * LOCKING:
5069 * Kernel thread context (may sleep).
5070 *
5071 * RETURNS:
5072 * 0 on success, -errno on failure.
5073 */
5074int ata_host_set_suspend(struct ata_host_set *host_set, pm_message_t mesg)
5075{
5076 int i, j, rc;
5077
5078 rc = ata_host_set_request_pm(host_set, mesg, 0, ATA_EHI_QUIET, 1);
5079 if (rc)
5080 goto fail;
5081
5082 /* EH is quiescent now. Fail if we have any ready device.
5083 * This happens if hotplug occurs between completion of device
5084 * suspension and here.
5085 */
5086 for (i = 0; i < host_set->n_ports; i++) {
5087 struct ata_port *ap = host_set->ports[i];
5088
5089 for (j = 0; j < ATA_MAX_DEVICES; j++) {
5090 struct ata_device *dev = &ap->device[j];
5091
5092 if (ata_dev_ready(dev)) {
5093 ata_port_printk(ap, KERN_WARNING,
5094 "suspend failed, device %d "
5095 "still active\n", dev->devno);
5096 rc = -EBUSY;
5097 goto fail;
5098 }
5099 }
5100 }
5101
5102 host_set->dev->power.power_state = mesg;
5103 return 0;
5104
5105 fail:
5106 ata_host_set_resume(host_set);
5107 return rc;
5108}
5109
5110/**
5111 * ata_host_set_resume - resume host_set
5112 * @host_set: host_set to resume
5113 *
5114 * Resume @host_set. Actual operation is performed by EH. This
5115 * function requests EH to perform PM operations and returns.
5116 * Note that all resume operations are performed parallely.
5117 *
5118 * LOCKING:
5119 * Kernel thread context (may sleep).
5120 */
5121void ata_host_set_resume(struct ata_host_set *host_set)
5122{
5123 ata_host_set_request_pm(host_set, PMSG_ON, ATA_EH_SOFTRESET,
5124 ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0);
5125 host_set->dev->power.power_state = PMSG_ON;
5126}
5127
5012/** 5128/**
5013 * ata_port_start - Set port up for dma. 5129 * ata_port_start - Set port up for dma.
5014 * @ap: Port to initialize 5130 * @ap: Port to initialize
@@ -5651,20 +5767,55 @@ int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits)
5651 return (tmp == bits->val) ? 1 : 0; 5767 return (tmp == bits->val) ? 1 : 0;
5652} 5768}
5653 5769
5654int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state) 5770void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t state)
5655{ 5771{
5656 pci_save_state(pdev); 5772 pci_save_state(pdev);
5657 pci_disable_device(pdev); 5773
5658 pci_set_power_state(pdev, PCI_D3hot); 5774 if (state.event == PM_EVENT_SUSPEND) {
5659 return 0; 5775 pci_disable_device(pdev);
5776 pci_set_power_state(pdev, PCI_D3hot);
5777 }
5660} 5778}
5661 5779
5662int ata_pci_device_resume(struct pci_dev *pdev) 5780void ata_pci_device_do_resume(struct pci_dev *pdev)
5663{ 5781{
5664 pci_set_power_state(pdev, PCI_D0); 5782 pci_set_power_state(pdev, PCI_D0);
5665 pci_restore_state(pdev); 5783 pci_restore_state(pdev);
5666 pci_enable_device(pdev); 5784 pci_enable_device(pdev);
5667 pci_set_master(pdev); 5785 pci_set_master(pdev);
5786}
5787
5788int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state)
5789{
5790 struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
5791 int rc = 0;
5792
5793 rc = ata_host_set_suspend(host_set, state);
5794 if (rc)
5795 return rc;
5796
5797 if (host_set->next) {
5798 rc = ata_host_set_suspend(host_set->next, state);
5799 if (rc) {
5800 ata_host_set_resume(host_set);
5801 return rc;
5802 }
5803 }
5804
5805 ata_pci_device_do_suspend(pdev, state);
5806
5807 return 0;
5808}
5809
5810int ata_pci_device_resume(struct pci_dev *pdev)
5811{
5812 struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
5813
5814 ata_pci_device_do_resume(pdev);
5815 ata_host_set_resume(host_set);
5816 if (host_set->next)
5817 ata_host_set_resume(host_set->next);
5818
5668 return 0; 5819 return 0;
5669} 5820}
5670#endif /* CONFIG_PCI */ 5821#endif /* CONFIG_PCI */
@@ -5844,6 +5995,8 @@ EXPORT_SYMBOL_GPL(sata_scr_write);
5844EXPORT_SYMBOL_GPL(sata_scr_write_flush); 5995EXPORT_SYMBOL_GPL(sata_scr_write_flush);
5845EXPORT_SYMBOL_GPL(ata_port_online); 5996EXPORT_SYMBOL_GPL(ata_port_online);
5846EXPORT_SYMBOL_GPL(ata_port_offline); 5997EXPORT_SYMBOL_GPL(ata_port_offline);
5998EXPORT_SYMBOL_GPL(ata_host_set_suspend);
5999EXPORT_SYMBOL_GPL(ata_host_set_resume);
5847EXPORT_SYMBOL_GPL(ata_id_string); 6000EXPORT_SYMBOL_GPL(ata_id_string);
5848EXPORT_SYMBOL_GPL(ata_id_c_string); 6001EXPORT_SYMBOL_GPL(ata_id_c_string);
5849EXPORT_SYMBOL_GPL(ata_scsi_simulate); 6002EXPORT_SYMBOL_GPL(ata_scsi_simulate);
@@ -5858,6 +6011,8 @@ EXPORT_SYMBOL_GPL(ata_pci_host_stop);
5858EXPORT_SYMBOL_GPL(ata_pci_init_native_mode); 6011EXPORT_SYMBOL_GPL(ata_pci_init_native_mode);
5859EXPORT_SYMBOL_GPL(ata_pci_init_one); 6012EXPORT_SYMBOL_GPL(ata_pci_init_one);
5860EXPORT_SYMBOL_GPL(ata_pci_remove_one); 6013EXPORT_SYMBOL_GPL(ata_pci_remove_one);
6014EXPORT_SYMBOL_GPL(ata_pci_device_do_suspend);
6015EXPORT_SYMBOL_GPL(ata_pci_device_do_resume);
5861EXPORT_SYMBOL_GPL(ata_pci_device_suspend); 6016EXPORT_SYMBOL_GPL(ata_pci_device_suspend);
5862EXPORT_SYMBOL_GPL(ata_pci_device_resume); 6017EXPORT_SYMBOL_GPL(ata_pci_device_resume);
5863EXPORT_SYMBOL_GPL(ata_pci_default_filter); 6018EXPORT_SYMBOL_GPL(ata_pci_default_filter);