diff options
-rw-r--r-- | drivers/scsi/hosts.c | 10 | ||||
-rw-r--r-- | drivers/scsi/scsi_error.c | 16 | ||||
-rw-r--r-- | drivers/scsi/scsi_pm.c | 110 | ||||
-rw-r--r-- | drivers/scsi/scsi_priv.h | 14 | ||||
-rw-r--r-- | drivers/scsi/scsi_scan.c | 24 | ||||
-rw-r--r-- | drivers/scsi/scsi_sysfs.c | 20 | ||||
-rw-r--r-- | drivers/scsi/sg.c | 10 | ||||
-rw-r--r-- | include/scsi/scsi_device.h | 8 |
8 files changed, 201 insertions, 11 deletions
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index a2b1414da288..8a8f803439e1 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/completion.h> | 32 | #include <linux/completion.h> |
33 | #include <linux/transport_class.h> | 33 | #include <linux/transport_class.h> |
34 | #include <linux/platform_device.h> | 34 | #include <linux/platform_device.h> |
35 | #include <linux/pm_runtime.h> | ||
35 | 36 | ||
36 | #include <scsi/scsi_device.h> | 37 | #include <scsi/scsi_device.h> |
37 | #include <scsi/scsi_host.h> | 38 | #include <scsi/scsi_host.h> |
@@ -156,6 +157,7 @@ EXPORT_SYMBOL(scsi_host_set_state); | |||
156 | void scsi_remove_host(struct Scsi_Host *shost) | 157 | void scsi_remove_host(struct Scsi_Host *shost) |
157 | { | 158 | { |
158 | unsigned long flags; | 159 | unsigned long flags; |
160 | |||
159 | mutex_lock(&shost->scan_mutex); | 161 | mutex_lock(&shost->scan_mutex); |
160 | spin_lock_irqsave(shost->host_lock, flags); | 162 | spin_lock_irqsave(shost->host_lock, flags); |
161 | if (scsi_host_set_state(shost, SHOST_CANCEL)) | 163 | if (scsi_host_set_state(shost, SHOST_CANCEL)) |
@@ -165,6 +167,8 @@ void scsi_remove_host(struct Scsi_Host *shost) | |||
165 | return; | 167 | return; |
166 | } | 168 | } |
167 | spin_unlock_irqrestore(shost->host_lock, flags); | 169 | spin_unlock_irqrestore(shost->host_lock, flags); |
170 | |||
171 | scsi_autopm_get_host(shost); | ||
168 | scsi_forget_host(shost); | 172 | scsi_forget_host(shost); |
169 | mutex_unlock(&shost->scan_mutex); | 173 | mutex_unlock(&shost->scan_mutex); |
170 | scsi_proc_host_rm(shost); | 174 | scsi_proc_host_rm(shost); |
@@ -216,12 +220,14 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, | |||
216 | shost->shost_gendev.parent = dev ? dev : &platform_bus; | 220 | shost->shost_gendev.parent = dev ? dev : &platform_bus; |
217 | shost->dma_dev = dma_dev; | 221 | shost->dma_dev = dma_dev; |
218 | 222 | ||
219 | device_enable_async_suspend(&shost->shost_gendev); | ||
220 | |||
221 | error = device_add(&shost->shost_gendev); | 223 | error = device_add(&shost->shost_gendev); |
222 | if (error) | 224 | if (error) |
223 | goto out; | 225 | goto out; |
224 | 226 | ||
227 | pm_runtime_set_active(&shost->shost_gendev); | ||
228 | pm_runtime_enable(&shost->shost_gendev); | ||
229 | device_enable_async_suspend(&shost->shost_gendev); | ||
230 | |||
225 | scsi_host_set_state(shost, SHOST_RUNNING); | 231 | scsi_host_set_state(shost, SHOST_RUNNING); |
226 | get_device(shost->shost_gendev.parent); | 232 | get_device(shost->shost_gendev.parent); |
227 | 233 | ||
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index c60cffbefa3c..2bf98469dc4c 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c | |||
@@ -1775,6 +1775,14 @@ int scsi_error_handler(void *data) | |||
1775 | * what we need to do to get it up and online again (if we can). | 1775 | * what we need to do to get it up and online again (if we can). |
1776 | * If we fail, we end up taking the thing offline. | 1776 | * If we fail, we end up taking the thing offline. |
1777 | */ | 1777 | */ |
1778 | if (scsi_autopm_get_host(shost) != 0) { | ||
1779 | SCSI_LOG_ERROR_RECOVERY(1, | ||
1780 | printk(KERN_ERR "Error handler scsi_eh_%d " | ||
1781 | "unable to autoresume\n", | ||
1782 | shost->host_no)); | ||
1783 | continue; | ||
1784 | } | ||
1785 | |||
1778 | if (shost->transportt->eh_strategy_handler) | 1786 | if (shost->transportt->eh_strategy_handler) |
1779 | shost->transportt->eh_strategy_handler(shost); | 1787 | shost->transportt->eh_strategy_handler(shost); |
1780 | else | 1788 | else |
@@ -1788,6 +1796,7 @@ int scsi_error_handler(void *data) | |||
1788 | * which are still online. | 1796 | * which are still online. |
1789 | */ | 1797 | */ |
1790 | scsi_restart_operations(shost); | 1798 | scsi_restart_operations(shost); |
1799 | scsi_autopm_put_host(shost); | ||
1791 | set_current_state(TASK_INTERRUPTIBLE); | 1800 | set_current_state(TASK_INTERRUPTIBLE); |
1792 | } | 1801 | } |
1793 | __set_current_state(TASK_RUNNING); | 1802 | __set_current_state(TASK_RUNNING); |
@@ -1885,12 +1894,16 @@ scsi_reset_provider_done_command(struct scsi_cmnd *scmd) | |||
1885 | int | 1894 | int |
1886 | scsi_reset_provider(struct scsi_device *dev, int flag) | 1895 | scsi_reset_provider(struct scsi_device *dev, int flag) |
1887 | { | 1896 | { |
1888 | struct scsi_cmnd *scmd = scsi_get_command(dev, GFP_KERNEL); | 1897 | struct scsi_cmnd *scmd; |
1889 | struct Scsi_Host *shost = dev->host; | 1898 | struct Scsi_Host *shost = dev->host; |
1890 | struct request req; | 1899 | struct request req; |
1891 | unsigned long flags; | 1900 | unsigned long flags; |
1892 | int rtn; | 1901 | int rtn; |
1893 | 1902 | ||
1903 | if (scsi_autopm_get_host(shost) < 0) | ||
1904 | return FAILED; | ||
1905 | |||
1906 | scmd = scsi_get_command(dev, GFP_KERNEL); | ||
1894 | blk_rq_init(NULL, &req); | 1907 | blk_rq_init(NULL, &req); |
1895 | scmd->request = &req; | 1908 | scmd->request = &req; |
1896 | 1909 | ||
@@ -1947,6 +1960,7 @@ scsi_reset_provider(struct scsi_device *dev, int flag) | |||
1947 | scsi_run_host_queues(shost); | 1960 | scsi_run_host_queues(shost); |
1948 | 1961 | ||
1949 | scsi_next_command(scmd); | 1962 | scsi_next_command(scmd); |
1963 | scsi_autopm_put_host(shost); | ||
1950 | return rtn; | 1964 | return rtn; |
1951 | } | 1965 | } |
1952 | EXPORT_SYMBOL(scsi_reset_provider); | 1966 | EXPORT_SYMBOL(scsi_reset_provider); |
diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c index cd83758ce0a2..d70e91ae60af 100644 --- a/drivers/scsi/scsi_pm.c +++ b/drivers/scsi/scsi_pm.c | |||
@@ -59,6 +59,12 @@ static int scsi_bus_resume_common(struct device *dev) | |||
59 | 59 | ||
60 | if (scsi_is_sdev_device(dev)) | 60 | if (scsi_is_sdev_device(dev)) |
61 | err = scsi_dev_type_resume(dev); | 61 | err = scsi_dev_type_resume(dev); |
62 | |||
63 | if (err == 0) { | ||
64 | pm_runtime_disable(dev); | ||
65 | pm_runtime_set_active(dev); | ||
66 | pm_runtime_enable(dev); | ||
67 | } | ||
62 | return err; | 68 | return err; |
63 | } | 69 | } |
64 | 70 | ||
@@ -86,6 +92,107 @@ static int scsi_bus_poweroff(struct device *dev) | |||
86 | 92 | ||
87 | #endif /* CONFIG_PM_SLEEP */ | 93 | #endif /* CONFIG_PM_SLEEP */ |
88 | 94 | ||
95 | #ifdef CONFIG_PM_RUNTIME | ||
96 | |||
97 | static int scsi_runtime_suspend(struct device *dev) | ||
98 | { | ||
99 | int err = 0; | ||
100 | |||
101 | dev_dbg(dev, "scsi_runtime_suspend\n"); | ||
102 | if (scsi_is_sdev_device(dev)) { | ||
103 | err = scsi_dev_type_suspend(dev, PMSG_AUTO_SUSPEND); | ||
104 | if (err == -EAGAIN) | ||
105 | pm_schedule_suspend(dev, jiffies_to_msecs( | ||
106 | round_jiffies_up_relative(HZ/10))); | ||
107 | } | ||
108 | |||
109 | /* Insert hooks here for targets, hosts, and transport classes */ | ||
110 | |||
111 | return err; | ||
112 | } | ||
113 | |||
114 | static int scsi_runtime_resume(struct device *dev) | ||
115 | { | ||
116 | int err = 0; | ||
117 | |||
118 | dev_dbg(dev, "scsi_runtime_resume\n"); | ||
119 | if (scsi_is_sdev_device(dev)) | ||
120 | err = scsi_dev_type_resume(dev); | ||
121 | |||
122 | /* Insert hooks here for targets, hosts, and transport classes */ | ||
123 | |||
124 | return err; | ||
125 | } | ||
126 | |||
127 | static int scsi_runtime_idle(struct device *dev) | ||
128 | { | ||
129 | int err; | ||
130 | |||
131 | dev_dbg(dev, "scsi_runtime_idle\n"); | ||
132 | |||
133 | /* Insert hooks here for targets, hosts, and transport classes */ | ||
134 | |||
135 | if (scsi_is_sdev_device(dev)) | ||
136 | err = pm_schedule_suspend(dev, 100); | ||
137 | else | ||
138 | err = pm_runtime_suspend(dev); | ||
139 | return err; | ||
140 | } | ||
141 | |||
142 | int scsi_autopm_get_device(struct scsi_device *sdev) | ||
143 | { | ||
144 | int err; | ||
145 | |||
146 | err = pm_runtime_get_sync(&sdev->sdev_gendev); | ||
147 | if (err < 0) | ||
148 | pm_runtime_put_sync(&sdev->sdev_gendev); | ||
149 | else if (err > 0) | ||
150 | err = 0; | ||
151 | return err; | ||
152 | } | ||
153 | EXPORT_SYMBOL_GPL(scsi_autopm_get_device); | ||
154 | |||
155 | void scsi_autopm_put_device(struct scsi_device *sdev) | ||
156 | { | ||
157 | pm_runtime_put_sync(&sdev->sdev_gendev); | ||
158 | } | ||
159 | EXPORT_SYMBOL_GPL(scsi_autopm_put_device); | ||
160 | |||
161 | void scsi_autopm_get_target(struct scsi_target *starget) | ||
162 | { | ||
163 | pm_runtime_get_sync(&starget->dev); | ||
164 | } | ||
165 | |||
166 | void scsi_autopm_put_target(struct scsi_target *starget) | ||
167 | { | ||
168 | pm_runtime_put_sync(&starget->dev); | ||
169 | } | ||
170 | |||
171 | int scsi_autopm_get_host(struct Scsi_Host *shost) | ||
172 | { | ||
173 | int err; | ||
174 | |||
175 | err = pm_runtime_get_sync(&shost->shost_gendev); | ||
176 | if (err < 0) | ||
177 | pm_runtime_put_sync(&shost->shost_gendev); | ||
178 | else if (err > 0) | ||
179 | err = 0; | ||
180 | return err; | ||
181 | } | ||
182 | |||
183 | void scsi_autopm_put_host(struct Scsi_Host *shost) | ||
184 | { | ||
185 | pm_runtime_put_sync(&shost->shost_gendev); | ||
186 | } | ||
187 | |||
188 | #else | ||
189 | |||
190 | #define scsi_runtime_suspend NULL | ||
191 | #define scsi_runtime_resume NULL | ||
192 | #define scsi_runtime_idle NULL | ||
193 | |||
194 | #endif /* CONFIG_PM_RUNTIME */ | ||
195 | |||
89 | const struct dev_pm_ops scsi_bus_pm_ops = { | 196 | const struct dev_pm_ops scsi_bus_pm_ops = { |
90 | .suspend = scsi_bus_suspend, | 197 | .suspend = scsi_bus_suspend, |
91 | .resume = scsi_bus_resume_common, | 198 | .resume = scsi_bus_resume_common, |
@@ -93,4 +200,7 @@ const struct dev_pm_ops scsi_bus_pm_ops = { | |||
93 | .thaw = scsi_bus_resume_common, | 200 | .thaw = scsi_bus_resume_common, |
94 | .poweroff = scsi_bus_poweroff, | 201 | .poweroff = scsi_bus_poweroff, |
95 | .restore = scsi_bus_resume_common, | 202 | .restore = scsi_bus_resume_common, |
203 | .runtime_suspend = scsi_runtime_suspend, | ||
204 | .runtime_resume = scsi_runtime_resume, | ||
205 | .runtime_idle = scsi_runtime_idle, | ||
96 | }; | 206 | }; |
diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index dddacc732550..026295e2c539 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h | |||
@@ -7,6 +7,7 @@ struct request_queue; | |||
7 | struct request; | 7 | struct request; |
8 | struct scsi_cmnd; | 8 | struct scsi_cmnd; |
9 | struct scsi_device; | 9 | struct scsi_device; |
10 | struct scsi_target; | ||
10 | struct scsi_host_template; | 11 | struct scsi_host_template; |
11 | struct Scsi_Host; | 12 | struct Scsi_Host; |
12 | struct scsi_nl_hdr; | 13 | struct scsi_nl_hdr; |
@@ -147,9 +148,20 @@ static inline void scsi_netlink_exit(void) {} | |||
147 | /* scsi_pm.c */ | 148 | /* scsi_pm.c */ |
148 | #ifdef CONFIG_PM_OPS | 149 | #ifdef CONFIG_PM_OPS |
149 | extern const struct dev_pm_ops scsi_bus_pm_ops; | 150 | extern const struct dev_pm_ops scsi_bus_pm_ops; |
150 | #else | 151 | #else /* CONFIG_PM_OPS */ |
151 | #define scsi_bus_pm_ops (*NULL) | 152 | #define scsi_bus_pm_ops (*NULL) |
152 | #endif | 153 | #endif |
154 | #ifdef CONFIG_PM_RUNTIME | ||
155 | extern void scsi_autopm_get_target(struct scsi_target *); | ||
156 | extern void scsi_autopm_put_target(struct scsi_target *); | ||
157 | extern int scsi_autopm_get_host(struct Scsi_Host *); | ||
158 | extern void scsi_autopm_put_host(struct Scsi_Host *); | ||
159 | #else | ||
160 | static inline void scsi_autopm_get_target(struct scsi_target *t) {} | ||
161 | static inline void scsi_autopm_put_target(struct scsi_target *t) {} | ||
162 | static inline int scsi_autopm_get_host(struct Scsi_Host *h) { return 0; } | ||
163 | static inline void scsi_autopm_put_host(struct Scsi_Host *h) {} | ||
164 | #endif /* CONFIG_PM_RUNTIME */ | ||
153 | 165 | ||
154 | /* | 166 | /* |
155 | * internal scsi timeout functions: for use by mid-layer and transport | 167 | * internal scsi timeout functions: for use by mid-layer and transport |
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 | ||
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 5f85f8e831f3..562fb3bce261 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/init.h> | 11 | #include <linux/init.h> |
12 | #include <linux/blkdev.h> | 12 | #include <linux/blkdev.h> |
13 | #include <linux/device.h> | 13 | #include <linux/device.h> |
14 | #include <linux/pm_runtime.h> | ||
14 | 15 | ||
15 | #include <scsi/scsi.h> | 16 | #include <scsi/scsi.h> |
16 | #include <scsi/scsi_device.h> | 17 | #include <scsi/scsi_device.h> |
@@ -802,8 +803,6 @@ static int scsi_target_add(struct scsi_target *starget) | |||
802 | if (starget->state != STARGET_CREATED) | 803 | if (starget->state != STARGET_CREATED) |
803 | return 0; | 804 | return 0; |
804 | 805 | ||
805 | device_enable_async_suspend(&starget->dev); | ||
806 | |||
807 | error = device_add(&starget->dev); | 806 | error = device_add(&starget->dev); |
808 | if (error) { | 807 | if (error) { |
809 | dev_err(&starget->dev, "target device_add failed, error %d\n", error); | 808 | dev_err(&starget->dev, "target device_add failed, error %d\n", error); |
@@ -812,6 +811,10 @@ static int scsi_target_add(struct scsi_target *starget) | |||
812 | transport_add_device(&starget->dev); | 811 | transport_add_device(&starget->dev); |
813 | starget->state = STARGET_RUNNING; | 812 | starget->state = STARGET_RUNNING; |
814 | 813 | ||
814 | pm_runtime_set_active(&starget->dev); | ||
815 | pm_runtime_enable(&starget->dev); | ||
816 | device_enable_async_suspend(&starget->dev); | ||
817 | |||
815 | return 0; | 818 | return 0; |
816 | } | 819 | } |
817 | 820 | ||
@@ -841,7 +844,20 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) | |||
841 | return error; | 844 | return error; |
842 | 845 | ||
843 | transport_configure_device(&starget->dev); | 846 | transport_configure_device(&starget->dev); |
847 | |||
844 | device_enable_async_suspend(&sdev->sdev_gendev); | 848 | device_enable_async_suspend(&sdev->sdev_gendev); |
849 | scsi_autopm_get_target(starget); | ||
850 | pm_runtime_set_active(&sdev->sdev_gendev); | ||
851 | pm_runtime_forbid(&sdev->sdev_gendev); | ||
852 | pm_runtime_enable(&sdev->sdev_gendev); | ||
853 | scsi_autopm_put_target(starget); | ||
854 | |||
855 | /* The following call will keep sdev active indefinitely, until | ||
856 | * its driver does a corresponding scsi_autopm_pm_device(). Only | ||
857 | * drivers supporting autosuspend will do this. | ||
858 | */ | ||
859 | scsi_autopm_get_device(sdev); | ||
860 | |||
845 | error = device_add(&sdev->sdev_gendev); | 861 | error = device_add(&sdev->sdev_gendev); |
846 | if (error) { | 862 | if (error) { |
847 | printk(KERN_INFO "error 1\n"); | 863 | printk(KERN_INFO "error 1\n"); |
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index d4549092400c..2968c6b83ddb 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c | |||
@@ -245,6 +245,10 @@ sg_open(struct inode *inode, struct file *filp) | |||
245 | if (retval) | 245 | if (retval) |
246 | goto sg_put; | 246 | goto sg_put; |
247 | 247 | ||
248 | retval = scsi_autopm_get_device(sdp->device); | ||
249 | if (retval) | ||
250 | goto sdp_put; | ||
251 | |||
248 | if (!((flags & O_NONBLOCK) || | 252 | if (!((flags & O_NONBLOCK) || |
249 | scsi_block_when_processing_errors(sdp->device))) { | 253 | scsi_block_when_processing_errors(sdp->device))) { |
250 | retval = -ENXIO; | 254 | retval = -ENXIO; |
@@ -302,8 +306,11 @@ sg_open(struct inode *inode, struct file *filp) | |||
302 | } | 306 | } |
303 | retval = 0; | 307 | retval = 0; |
304 | error_out: | 308 | error_out: |
305 | if (retval) | 309 | if (retval) { |
310 | scsi_autopm_put_device(sdp->device); | ||
311 | sdp_put: | ||
306 | scsi_device_put(sdp->device); | 312 | scsi_device_put(sdp->device); |
313 | } | ||
307 | sg_put: | 314 | sg_put: |
308 | if (sdp) | 315 | if (sdp) |
309 | sg_put_dev(sdp); | 316 | sg_put_dev(sdp); |
@@ -327,6 +334,7 @@ sg_release(struct inode *inode, struct file *filp) | |||
327 | sdp->exclude = 0; | 334 | sdp->exclude = 0; |
328 | wake_up_interruptible(&sdp->o_excl_wait); | 335 | wake_up_interruptible(&sdp->o_excl_wait); |
329 | 336 | ||
337 | scsi_autopm_put_device(sdp->device); | ||
330 | kref_put(&sfp->f_ref, sg_remove_sfp); | 338 | kref_put(&sfp->f_ref, sg_remove_sfp); |
331 | return 0; | 339 | return 0; |
332 | } | 340 | } |
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index d80b6dbed1ca..50cb34ffef11 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h | |||
@@ -381,6 +381,14 @@ extern int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd, | |||
381 | struct scsi_sense_hdr *, int timeout, int retries, | 381 | struct scsi_sense_hdr *, int timeout, int retries, |
382 | int *resid); | 382 | int *resid); |
383 | 383 | ||
384 | #ifdef CONFIG_PM_RUNTIME | ||
385 | extern int scsi_autopm_get_device(struct scsi_device *); | ||
386 | extern void scsi_autopm_put_device(struct scsi_device *); | ||
387 | #else | ||
388 | static inline int scsi_autopm_get_device(struct scsi_device *d) { return 0; } | ||
389 | static inline void scsi_autopm_put_device(struct scsi_device *d) {} | ||
390 | #endif /* CONFIG_PM_RUNTIME */ | ||
391 | |||
384 | static inline int __must_check scsi_device_reprobe(struct scsi_device *sdev) | 392 | static inline int __must_check scsi_device_reprobe(struct scsi_device *sdev) |
385 | { | 393 | { |
386 | return device_reprobe(&sdev->sdev_gendev); | 394 | return device_reprobe(&sdev->sdev_gendev); |