diff options
Diffstat (limited to 'drivers/s390/cio/device.c')
-rw-r--r-- | drivers/s390/cio/device.c | 160 |
1 files changed, 80 insertions, 80 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index a6c7d5426fb2..c6abb75c4615 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -136,7 +136,6 @@ static int io_subchannel_sch_event(struct subchannel *, int); | |||
136 | static int io_subchannel_chp_event(struct subchannel *, struct chp_link *, | 136 | static int io_subchannel_chp_event(struct subchannel *, struct chp_link *, |
137 | int); | 137 | int); |
138 | static void recovery_func(unsigned long data); | 138 | static void recovery_func(unsigned long data); |
139 | struct workqueue_struct *ccw_device_work; | ||
140 | wait_queue_head_t ccw_device_init_wq; | 139 | wait_queue_head_t ccw_device_init_wq; |
141 | atomic_t ccw_device_init_count; | 140 | atomic_t ccw_device_init_count; |
142 | 141 | ||
@@ -159,11 +158,16 @@ static int io_subchannel_prepare(struct subchannel *sch) | |||
159 | return 0; | 158 | return 0; |
160 | } | 159 | } |
161 | 160 | ||
162 | static void io_subchannel_settle(void) | 161 | static int io_subchannel_settle(void) |
163 | { | 162 | { |
164 | wait_event(ccw_device_init_wq, | 163 | int ret; |
165 | atomic_read(&ccw_device_init_count) == 0); | 164 | |
166 | flush_workqueue(ccw_device_work); | 165 | ret = wait_event_interruptible(ccw_device_init_wq, |
166 | atomic_read(&ccw_device_init_count) == 0); | ||
167 | if (ret) | ||
168 | return -EINTR; | ||
169 | flush_workqueue(cio_work_q); | ||
170 | return 0; | ||
167 | } | 171 | } |
168 | 172 | ||
169 | static struct css_driver io_subchannel_driver = { | 173 | static struct css_driver io_subchannel_driver = { |
@@ -188,27 +192,13 @@ int __init io_subchannel_init(void) | |||
188 | atomic_set(&ccw_device_init_count, 0); | 192 | atomic_set(&ccw_device_init_count, 0); |
189 | setup_timer(&recovery_timer, recovery_func, 0); | 193 | setup_timer(&recovery_timer, recovery_func, 0); |
190 | 194 | ||
191 | ccw_device_work = create_singlethread_workqueue("cio"); | 195 | ret = bus_register(&ccw_bus_type); |
192 | if (!ccw_device_work) | 196 | if (ret) |
193 | return -ENOMEM; | 197 | return ret; |
194 | slow_path_wq = create_singlethread_workqueue("kslowcrw"); | ||
195 | if (!slow_path_wq) { | ||
196 | ret = -ENOMEM; | ||
197 | goto out_err; | ||
198 | } | ||
199 | if ((ret = bus_register (&ccw_bus_type))) | ||
200 | goto out_err; | ||
201 | |||
202 | ret = css_driver_register(&io_subchannel_driver); | 198 | ret = css_driver_register(&io_subchannel_driver); |
203 | if (ret) | 199 | if (ret) |
204 | goto out_err; | 200 | bus_unregister(&ccw_bus_type); |
205 | 201 | ||
206 | return 0; | ||
207 | out_err: | ||
208 | if (ccw_device_work) | ||
209 | destroy_workqueue(ccw_device_work); | ||
210 | if (slow_path_wq) | ||
211 | destroy_workqueue(slow_path_wq); | ||
212 | return ret; | 202 | return ret; |
213 | } | 203 | } |
214 | 204 | ||
@@ -1348,7 +1338,7 @@ static enum io_sch_action sch_get_action(struct subchannel *sch) | |||
1348 | /* Not operational. */ | 1338 | /* Not operational. */ |
1349 | if (!cdev) | 1339 | if (!cdev) |
1350 | return IO_SCH_UNREG; | 1340 | return IO_SCH_UNREG; |
1351 | if (!ccw_device_notify(cdev, CIO_GONE)) | 1341 | if (ccw_device_notify(cdev, CIO_GONE) != NOTIFY_OK) |
1352 | return IO_SCH_UNREG; | 1342 | return IO_SCH_UNREG; |
1353 | return IO_SCH_ORPH_UNREG; | 1343 | return IO_SCH_ORPH_UNREG; |
1354 | } | 1344 | } |
@@ -1356,12 +1346,12 @@ static enum io_sch_action sch_get_action(struct subchannel *sch) | |||
1356 | if (!cdev) | 1346 | if (!cdev) |
1357 | return IO_SCH_ATTACH; | 1347 | return IO_SCH_ATTACH; |
1358 | if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { | 1348 | if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { |
1359 | if (!ccw_device_notify(cdev, CIO_GONE)) | 1349 | if (ccw_device_notify(cdev, CIO_GONE) != NOTIFY_OK) |
1360 | return IO_SCH_UNREG_ATTACH; | 1350 | return IO_SCH_UNREG_ATTACH; |
1361 | return IO_SCH_ORPH_ATTACH; | 1351 | return IO_SCH_ORPH_ATTACH; |
1362 | } | 1352 | } |
1363 | if ((sch->schib.pmcw.pam & sch->opm) == 0) { | 1353 | if ((sch->schib.pmcw.pam & sch->opm) == 0) { |
1364 | if (!ccw_device_notify(cdev, CIO_NO_PATH)) | 1354 | if (ccw_device_notify(cdev, CIO_NO_PATH) != NOTIFY_OK) |
1365 | return IO_SCH_UNREG; | 1355 | return IO_SCH_UNREG; |
1366 | return IO_SCH_DISC; | 1356 | return IO_SCH_DISC; |
1367 | } | 1357 | } |
@@ -1410,6 +1400,12 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) | |||
1410 | rc = 0; | 1400 | rc = 0; |
1411 | goto out_unlock; | 1401 | goto out_unlock; |
1412 | case IO_SCH_VERIFY: | 1402 | case IO_SCH_VERIFY: |
1403 | if (cdev->private->flags.resuming == 1) { | ||
1404 | if (cio_enable_subchannel(sch, (u32)(addr_t)sch)) { | ||
1405 | ccw_device_set_notoper(cdev); | ||
1406 | break; | ||
1407 | } | ||
1408 | } | ||
1413 | /* Trigger path verification. */ | 1409 | /* Trigger path verification. */ |
1414 | io_subchannel_verify(sch); | 1410 | io_subchannel_verify(sch); |
1415 | rc = 0; | 1411 | rc = 0; |
@@ -1448,7 +1444,8 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) | |||
1448 | break; | 1444 | break; |
1449 | case IO_SCH_UNREG_ATTACH: | 1445 | case IO_SCH_UNREG_ATTACH: |
1450 | /* Unregister ccw device. */ | 1446 | /* Unregister ccw device. */ |
1451 | ccw_device_unregister(cdev); | 1447 | if (!cdev->private->flags.resuming) |
1448 | ccw_device_unregister(cdev); | ||
1452 | break; | 1449 | break; |
1453 | default: | 1450 | default: |
1454 | break; | 1451 | break; |
@@ -1457,7 +1454,8 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) | |||
1457 | switch (action) { | 1454 | switch (action) { |
1458 | case IO_SCH_ORPH_UNREG: | 1455 | case IO_SCH_ORPH_UNREG: |
1459 | case IO_SCH_UNREG: | 1456 | case IO_SCH_UNREG: |
1460 | css_sch_device_unregister(sch); | 1457 | if (!cdev || !cdev->private->flags.resuming) |
1458 | css_sch_device_unregister(sch); | ||
1461 | break; | 1459 | break; |
1462 | case IO_SCH_ORPH_ATTACH: | 1460 | case IO_SCH_ORPH_ATTACH: |
1463 | case IO_SCH_UNREG_ATTACH: | 1461 | case IO_SCH_UNREG_ATTACH: |
@@ -1779,26 +1777,42 @@ static void __ccw_device_pm_restore(struct ccw_device *cdev) | |||
1779 | { | 1777 | { |
1780 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | 1778 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
1781 | 1779 | ||
1782 | if (cio_is_console(sch->schid)) | 1780 | spin_lock_irq(sch->lock); |
1783 | goto out; | 1781 | if (cio_is_console(sch->schid)) { |
1782 | cio_enable_subchannel(sch, (u32)(addr_t)sch); | ||
1783 | goto out_unlock; | ||
1784 | } | ||
1784 | /* | 1785 | /* |
1785 | * While we were sleeping, devices may have gone or become | 1786 | * While we were sleeping, devices may have gone or become |
1786 | * available again. Kick re-detection. | 1787 | * available again. Kick re-detection. |
1787 | */ | 1788 | */ |
1788 | spin_lock_irq(sch->lock); | ||
1789 | cdev->private->flags.resuming = 1; | 1789 | cdev->private->flags.resuming = 1; |
1790 | css_schedule_eval(sch->schid); | ||
1791 | spin_unlock_irq(sch->lock); | ||
1792 | css_complete_work(); | ||
1793 | |||
1794 | /* cdev may have been moved to a different subchannel. */ | ||
1795 | sch = to_subchannel(cdev->dev.parent); | ||
1796 | spin_lock_irq(sch->lock); | ||
1797 | if (cdev->private->state != DEV_STATE_ONLINE && | ||
1798 | cdev->private->state != DEV_STATE_OFFLINE) | ||
1799 | goto out_unlock; | ||
1800 | |||
1790 | ccw_device_recognition(cdev); | 1801 | ccw_device_recognition(cdev); |
1791 | spin_unlock_irq(sch->lock); | 1802 | spin_unlock_irq(sch->lock); |
1792 | wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev) || | 1803 | wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev) || |
1793 | cdev->private->state == DEV_STATE_DISCONNECTED); | 1804 | cdev->private->state == DEV_STATE_DISCONNECTED); |
1794 | out: | 1805 | spin_lock_irq(sch->lock); |
1806 | |||
1807 | out_unlock: | ||
1795 | cdev->private->flags.resuming = 0; | 1808 | cdev->private->flags.resuming = 0; |
1809 | spin_unlock_irq(sch->lock); | ||
1796 | } | 1810 | } |
1797 | 1811 | ||
1798 | static int resume_handle_boxed(struct ccw_device *cdev) | 1812 | static int resume_handle_boxed(struct ccw_device *cdev) |
1799 | { | 1813 | { |
1800 | cdev->private->state = DEV_STATE_BOXED; | 1814 | cdev->private->state = DEV_STATE_BOXED; |
1801 | if (ccw_device_notify(cdev, CIO_BOXED)) | 1815 | if (ccw_device_notify(cdev, CIO_BOXED) == NOTIFY_OK) |
1802 | return 0; | 1816 | return 0; |
1803 | ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); | 1817 | ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); |
1804 | return -ENODEV; | 1818 | return -ENODEV; |
@@ -1807,7 +1821,7 @@ static int resume_handle_boxed(struct ccw_device *cdev) | |||
1807 | static int resume_handle_disc(struct ccw_device *cdev) | 1821 | static int resume_handle_disc(struct ccw_device *cdev) |
1808 | { | 1822 | { |
1809 | cdev->private->state = DEV_STATE_DISCONNECTED; | 1823 | cdev->private->state = DEV_STATE_DISCONNECTED; |
1810 | if (ccw_device_notify(cdev, CIO_GONE)) | 1824 | if (ccw_device_notify(cdev, CIO_GONE) == NOTIFY_OK) |
1811 | return 0; | 1825 | return 0; |
1812 | ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); | 1826 | ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); |
1813 | return -ENODEV; | 1827 | return -ENODEV; |
@@ -1816,40 +1830,31 @@ static int resume_handle_disc(struct ccw_device *cdev) | |||
1816 | static int ccw_device_pm_restore(struct device *dev) | 1830 | static int ccw_device_pm_restore(struct device *dev) |
1817 | { | 1831 | { |
1818 | struct ccw_device *cdev = to_ccwdev(dev); | 1832 | struct ccw_device *cdev = to_ccwdev(dev); |
1819 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | 1833 | struct subchannel *sch; |
1820 | int ret = 0, cm_enabled; | 1834 | int ret = 0; |
1821 | 1835 | ||
1822 | __ccw_device_pm_restore(cdev); | 1836 | __ccw_device_pm_restore(cdev); |
1837 | sch = to_subchannel(cdev->dev.parent); | ||
1823 | spin_lock_irq(sch->lock); | 1838 | spin_lock_irq(sch->lock); |
1824 | if (cio_is_console(sch->schid)) { | 1839 | if (cio_is_console(sch->schid)) |
1825 | cio_enable_subchannel(sch, (u32)(addr_t)sch); | ||
1826 | spin_unlock_irq(sch->lock); | ||
1827 | goto out_restore; | 1840 | goto out_restore; |
1828 | } | 1841 | |
1829 | cdev->private->flags.donotify = 0; | ||
1830 | /* check recognition results */ | 1842 | /* check recognition results */ |
1831 | switch (cdev->private->state) { | 1843 | switch (cdev->private->state) { |
1832 | case DEV_STATE_OFFLINE: | 1844 | case DEV_STATE_OFFLINE: |
1845 | case DEV_STATE_ONLINE: | ||
1846 | cdev->private->flags.donotify = 0; | ||
1833 | break; | 1847 | break; |
1834 | case DEV_STATE_BOXED: | 1848 | case DEV_STATE_BOXED: |
1835 | ret = resume_handle_boxed(cdev); | 1849 | ret = resume_handle_boxed(cdev); |
1836 | spin_unlock_irq(sch->lock); | ||
1837 | if (ret) | 1850 | if (ret) |
1838 | goto out; | 1851 | goto out_unlock; |
1839 | goto out_restore; | 1852 | goto out_restore; |
1840 | case DEV_STATE_DISCONNECTED: | ||
1841 | goto out_disc_unlock; | ||
1842 | default: | 1853 | default: |
1843 | goto out_unreg_unlock; | 1854 | ret = resume_handle_disc(cdev); |
1844 | } | 1855 | if (ret) |
1845 | /* check if the device id has changed */ | 1856 | goto out_unlock; |
1846 | if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { | 1857 | goto out_restore; |
1847 | CIO_MSG_EVENT(0, "resume: sch 0.%x.%04x: failed (devno " | ||
1848 | "changed from %04x to %04x)\n", | ||
1849 | sch->schid.ssid, sch->schid.sch_no, | ||
1850 | cdev->private->dev_id.devno, | ||
1851 | sch->schib.pmcw.dev); | ||
1852 | goto out_unreg_unlock; | ||
1853 | } | 1858 | } |
1854 | /* check if the device type has changed */ | 1859 | /* check if the device type has changed */ |
1855 | if (!ccw_device_test_sense_data(cdev)) { | 1860 | if (!ccw_device_test_sense_data(cdev)) { |
@@ -1858,24 +1863,30 @@ static int ccw_device_pm_restore(struct device *dev) | |||
1858 | ret = -ENODEV; | 1863 | ret = -ENODEV; |
1859 | goto out_unlock; | 1864 | goto out_unlock; |
1860 | } | 1865 | } |
1861 | if (!cdev->online) { | 1866 | if (!cdev->online) |
1862 | ret = 0; | ||
1863 | goto out_unlock; | 1867 | goto out_unlock; |
1864 | } | ||
1865 | ret = ccw_device_online(cdev); | ||
1866 | if (ret) | ||
1867 | goto out_disc_unlock; | ||
1868 | 1868 | ||
1869 | cm_enabled = cdev->private->cmb != NULL; | 1869 | if (ccw_device_online(cdev)) { |
1870 | ret = resume_handle_disc(cdev); | ||
1871 | if (ret) | ||
1872 | goto out_unlock; | ||
1873 | goto out_restore; | ||
1874 | } | ||
1870 | spin_unlock_irq(sch->lock); | 1875 | spin_unlock_irq(sch->lock); |
1871 | |||
1872 | wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); | 1876 | wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); |
1873 | if (cdev->private->state != DEV_STATE_ONLINE) { | 1877 | spin_lock_irq(sch->lock); |
1874 | spin_lock_irq(sch->lock); | 1878 | |
1875 | goto out_disc_unlock; | 1879 | if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_BAD) { |
1880 | ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); | ||
1881 | ret = -ENODEV; | ||
1882 | goto out_unlock; | ||
1876 | } | 1883 | } |
1877 | if (cm_enabled) { | 1884 | |
1885 | /* reenable cmf, if needed */ | ||
1886 | if (cdev->private->cmb) { | ||
1887 | spin_unlock_irq(sch->lock); | ||
1878 | ret = ccw_set_cmf(cdev, 1); | 1888 | ret = ccw_set_cmf(cdev, 1); |
1889 | spin_lock_irq(sch->lock); | ||
1879 | if (ret) { | 1890 | if (ret) { |
1880 | CIO_MSG_EVENT(2, "resume: cdev 0.%x.%04x: cmf failed " | 1891 | CIO_MSG_EVENT(2, "resume: cdev 0.%x.%04x: cmf failed " |
1881 | "(rc=%d)\n", cdev->private->dev_id.ssid, | 1892 | "(rc=%d)\n", cdev->private->dev_id.ssid, |
@@ -1885,21 +1896,11 @@ static int ccw_device_pm_restore(struct device *dev) | |||
1885 | } | 1896 | } |
1886 | 1897 | ||
1887 | out_restore: | 1898 | out_restore: |
1899 | spin_unlock_irq(sch->lock); | ||
1888 | if (cdev->online && cdev->drv && cdev->drv->restore) | 1900 | if (cdev->online && cdev->drv && cdev->drv->restore) |
1889 | ret = cdev->drv->restore(cdev); | 1901 | ret = cdev->drv->restore(cdev); |
1890 | out: | ||
1891 | return ret; | 1902 | return ret; |
1892 | 1903 | ||
1893 | out_disc_unlock: | ||
1894 | ret = resume_handle_disc(cdev); | ||
1895 | spin_unlock_irq(sch->lock); | ||
1896 | if (ret) | ||
1897 | return ret; | ||
1898 | goto out_restore; | ||
1899 | |||
1900 | out_unreg_unlock: | ||
1901 | ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL); | ||
1902 | ret = -ENODEV; | ||
1903 | out_unlock: | 1904 | out_unlock: |
1904 | spin_unlock_irq(sch->lock); | 1905 | spin_unlock_irq(sch->lock); |
1905 | return ret; | 1906 | return ret; |
@@ -2028,7 +2029,7 @@ void ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo) | |||
2028 | /* Get workqueue ref. */ | 2029 | /* Get workqueue ref. */ |
2029 | if (!get_device(&cdev->dev)) | 2030 | if (!get_device(&cdev->dev)) |
2030 | return; | 2031 | return; |
2031 | if (!queue_work(slow_path_wq, &cdev->private->todo_work)) { | 2032 | if (!queue_work(cio_work_q, &cdev->private->todo_work)) { |
2032 | /* Already queued, release workqueue ref. */ | 2033 | /* Already queued, release workqueue ref. */ |
2033 | put_device(&cdev->dev); | 2034 | put_device(&cdev->dev); |
2034 | } | 2035 | } |
@@ -2041,5 +2042,4 @@ EXPORT_SYMBOL(ccw_driver_register); | |||
2041 | EXPORT_SYMBOL(ccw_driver_unregister); | 2042 | EXPORT_SYMBOL(ccw_driver_unregister); |
2042 | EXPORT_SYMBOL(get_ccwdev_by_busid); | 2043 | EXPORT_SYMBOL(get_ccwdev_by_busid); |
2043 | EXPORT_SYMBOL(ccw_bus_type); | 2044 | EXPORT_SYMBOL(ccw_bus_type); |
2044 | EXPORT_SYMBOL(ccw_device_work); | ||
2045 | EXPORT_SYMBOL_GPL(ccw_device_get_subchannel_id); | 2045 | EXPORT_SYMBOL_GPL(ccw_device_get_subchannel_id); |