diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2010-06-17 10:41:42 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2010-07-28 10:07:50 -0400 |
commit | bc4f24014de58f045f169742701a6598884d93db (patch) | |
tree | 4e68ae6fa5fff179ce69b2d890b01a5fcc9c55d5 /drivers/scsi/scsi_scan.c | |
parent | db5bd1e0b505c54ff492172ce4abc245cf6cd639 (diff) |
[SCSI] implement runtime Power Management
This patch (as1398b) adds runtime PM support to the SCSI layer. Only
the machanism is provided; use of it is up to the various high-level
drivers, and the patch doesn't change any of them. Except for sg --
the patch expicitly prevents a device from being runtime-suspended
while its sg device file is open.
The implementation is simplistic. In general, hosts and targets are
automatically suspended when all their children are asleep, but for
them the runtime-suspend code doesn't actually do anything. (A host's
runtime PM status is propagated up the device tree, though, so a
runtime-PM-aware lower-level driver could power down the host adapter
hardware at the appropriate times.) There are comments indicating
where a transport class might be notified or some other hooks added.
LUNs are runtime-suspended by calling the drivers' existing suspend
handlers (and likewise for runtime-resume). Somewhat arbitrarily, the
implementation delays for 100 ms before suspending an eligible LUN.
This is because there typically are occasions during bootup when the
same device file is opened and closed several times in quick
succession.
The way this all works is that the SCSI core increments a device's
PM-usage count when it is registered. If a high-level driver does
nothing then the device will not be eligible for runtime-suspend
because of the elevated usage count. If a high-level driver wants to
use runtime PM then it can call scsi_autopm_put_device() in its probe
routine to decrement the usage count and scsi_autopm_get_device() in
its remove routine to restore the original count.
Hosts, targets, and LUNs are not suspended while they are being probed
or removed, or while the error handler is running. In fact, a fairly
large part of the patch consists of code to make sure that things
aren't suspended at such times.
[jejb: fix up compile issues in PM config variations]
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi/scsi_scan.c')
-rw-r--r-- | drivers/scsi/scsi_scan.c | 24 |
1 files changed, 20 insertions, 4 deletions
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 1c027a97d8b9..3d0a1e6e9c48 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c | |||
@@ -1513,14 +1513,18 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, | |||
1513 | starget = scsi_alloc_target(parent, channel, id); | 1513 | starget = scsi_alloc_target(parent, channel, id); |
1514 | if (!starget) | 1514 | if (!starget) |
1515 | return ERR_PTR(-ENOMEM); | 1515 | return ERR_PTR(-ENOMEM); |
1516 | scsi_autopm_get_target(starget); | ||
1516 | 1517 | ||
1517 | mutex_lock(&shost->scan_mutex); | 1518 | mutex_lock(&shost->scan_mutex); |
1518 | if (!shost->async_scan) | 1519 | if (!shost->async_scan) |
1519 | scsi_complete_async_scans(); | 1520 | scsi_complete_async_scans(); |
1520 | 1521 | ||
1521 | if (scsi_host_scan_allowed(shost)) | 1522 | if (scsi_host_scan_allowed(shost) && scsi_autopm_get_host(shost) == 0) { |
1522 | scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata); | 1523 | scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata); |
1524 | scsi_autopm_put_host(shost); | ||
1525 | } | ||
1523 | mutex_unlock(&shost->scan_mutex); | 1526 | mutex_unlock(&shost->scan_mutex); |
1527 | scsi_autopm_put_target(starget); | ||
1524 | scsi_target_reap(starget); | 1528 | scsi_target_reap(starget); |
1525 | put_device(&starget->dev); | 1529 | put_device(&starget->dev); |
1526 | 1530 | ||
@@ -1574,6 +1578,7 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel, | |||
1574 | starget = scsi_alloc_target(parent, channel, id); | 1578 | starget = scsi_alloc_target(parent, channel, id); |
1575 | if (!starget) | 1579 | if (!starget) |
1576 | return; | 1580 | return; |
1581 | scsi_autopm_get_target(starget); | ||
1577 | 1582 | ||
1578 | if (lun != SCAN_WILD_CARD) { | 1583 | if (lun != SCAN_WILD_CARD) { |
1579 | /* | 1584 | /* |
@@ -1599,6 +1604,7 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel, | |||
1599 | } | 1604 | } |
1600 | 1605 | ||
1601 | out_reap: | 1606 | out_reap: |
1607 | scsi_autopm_put_target(starget); | ||
1602 | /* now determine if the target has any children at all | 1608 | /* now determine if the target has any children at all |
1603 | * and if not, nuke it */ | 1609 | * and if not, nuke it */ |
1604 | scsi_target_reap(starget); | 1610 | scsi_target_reap(starget); |
@@ -1633,8 +1639,10 @@ void scsi_scan_target(struct device *parent, unsigned int channel, | |||
1633 | if (!shost->async_scan) | 1639 | if (!shost->async_scan) |
1634 | scsi_complete_async_scans(); | 1640 | scsi_complete_async_scans(); |
1635 | 1641 | ||
1636 | if (scsi_host_scan_allowed(shost)) | 1642 | if (scsi_host_scan_allowed(shost) && scsi_autopm_get_host(shost) == 0) { |
1637 | __scsi_scan_target(parent, channel, id, lun, rescan); | 1643 | __scsi_scan_target(parent, channel, id, lun, rescan); |
1644 | scsi_autopm_put_host(shost); | ||
1645 | } | ||
1638 | mutex_unlock(&shost->scan_mutex); | 1646 | mutex_unlock(&shost->scan_mutex); |
1639 | } | 1647 | } |
1640 | EXPORT_SYMBOL(scsi_scan_target); | 1648 | EXPORT_SYMBOL(scsi_scan_target); |
@@ -1686,7 +1694,7 @@ int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, | |||
1686 | if (!shost->async_scan) | 1694 | if (!shost->async_scan) |
1687 | scsi_complete_async_scans(); | 1695 | scsi_complete_async_scans(); |
1688 | 1696 | ||
1689 | if (scsi_host_scan_allowed(shost)) { | 1697 | if (scsi_host_scan_allowed(shost) && scsi_autopm_get_host(shost) == 0) { |
1690 | if (channel == SCAN_WILD_CARD) | 1698 | if (channel == SCAN_WILD_CARD) |
1691 | for (channel = 0; channel <= shost->max_channel; | 1699 | for (channel = 0; channel <= shost->max_channel; |
1692 | channel++) | 1700 | channel++) |
@@ -1694,6 +1702,7 @@ int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, | |||
1694 | rescan); | 1702 | rescan); |
1695 | else | 1703 | else |
1696 | scsi_scan_channel(shost, channel, id, lun, rescan); | 1704 | scsi_scan_channel(shost, channel, id, lun, rescan); |
1705 | scsi_autopm_put_host(shost); | ||
1697 | } | 1706 | } |
1698 | mutex_unlock(&shost->scan_mutex); | 1707 | mutex_unlock(&shost->scan_mutex); |
1699 | 1708 | ||
@@ -1831,8 +1840,11 @@ static void do_scsi_scan_host(struct Scsi_Host *shost) | |||
1831 | static int do_scan_async(void *_data) | 1840 | static int do_scan_async(void *_data) |
1832 | { | 1841 | { |
1833 | struct async_scan_data *data = _data; | 1842 | struct async_scan_data *data = _data; |
1834 | do_scsi_scan_host(data->shost); | 1843 | struct Scsi_Host *shost = data->shost; |
1844 | |||
1845 | do_scsi_scan_host(shost); | ||
1835 | scsi_finish_async_scan(data); | 1846 | scsi_finish_async_scan(data); |
1847 | scsi_autopm_put_host(shost); | ||
1836 | return 0; | 1848 | return 0; |
1837 | } | 1849 | } |
1838 | 1850 | ||
@@ -1847,16 +1859,20 @@ void scsi_scan_host(struct Scsi_Host *shost) | |||
1847 | 1859 | ||
1848 | if (strncmp(scsi_scan_type, "none", 4) == 0) | 1860 | if (strncmp(scsi_scan_type, "none", 4) == 0) |
1849 | return; | 1861 | return; |
1862 | if (scsi_autopm_get_host(shost) < 0) | ||
1863 | return; | ||
1850 | 1864 | ||
1851 | data = scsi_prep_async_scan(shost); | 1865 | data = scsi_prep_async_scan(shost); |
1852 | if (!data) { | 1866 | if (!data) { |
1853 | do_scsi_scan_host(shost); | 1867 | do_scsi_scan_host(shost); |
1868 | scsi_autopm_put_host(shost); | ||
1854 | return; | 1869 | return; |
1855 | } | 1870 | } |
1856 | 1871 | ||
1857 | p = kthread_run(do_scan_async, data, "scsi_scan_%d", shost->host_no); | 1872 | p = kthread_run(do_scan_async, data, "scsi_scan_%d", shost->host_no); |
1858 | if (IS_ERR(p)) | 1873 | if (IS_ERR(p)) |
1859 | do_scan_async(data); | 1874 | do_scan_async(data); |
1875 | /* scsi_autopm_put_host(shost) is called in do_scan_async() */ | ||
1860 | } | 1876 | } |
1861 | EXPORT_SYMBOL(scsi_scan_host); | 1877 | EXPORT_SYMBOL(scsi_scan_host); |
1862 | 1878 | ||