diff options
Diffstat (limited to 'drivers/s390/cio/device.c')
-rw-r--r-- | drivers/s390/cio/device.c | 111 |
1 files changed, 61 insertions, 50 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index c7b2b7b26b8b..c6abb75c4615 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -1400,6 +1400,12 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) | |||
1400 | rc = 0; | 1400 | rc = 0; |
1401 | goto out_unlock; | 1401 | goto out_unlock; |
1402 | 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 | } | ||
1403 | /* Trigger path verification. */ | 1409 | /* Trigger path verification. */ |
1404 | io_subchannel_verify(sch); | 1410 | io_subchannel_verify(sch); |
1405 | rc = 0; | 1411 | rc = 0; |
@@ -1438,7 +1444,8 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) | |||
1438 | break; | 1444 | break; |
1439 | case IO_SCH_UNREG_ATTACH: | 1445 | case IO_SCH_UNREG_ATTACH: |
1440 | /* Unregister ccw device. */ | 1446 | /* Unregister ccw device. */ |
1441 | ccw_device_unregister(cdev); | 1447 | if (!cdev->private->flags.resuming) |
1448 | ccw_device_unregister(cdev); | ||
1442 | break; | 1449 | break; |
1443 | default: | 1450 | default: |
1444 | break; | 1451 | break; |
@@ -1447,7 +1454,8 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) | |||
1447 | switch (action) { | 1454 | switch (action) { |
1448 | case IO_SCH_ORPH_UNREG: | 1455 | case IO_SCH_ORPH_UNREG: |
1449 | case IO_SCH_UNREG: | 1456 | case IO_SCH_UNREG: |
1450 | css_sch_device_unregister(sch); | 1457 | if (!cdev || !cdev->private->flags.resuming) |
1458 | css_sch_device_unregister(sch); | ||
1451 | break; | 1459 | break; |
1452 | case IO_SCH_ORPH_ATTACH: | 1460 | case IO_SCH_ORPH_ATTACH: |
1453 | case IO_SCH_UNREG_ATTACH: | 1461 | case IO_SCH_UNREG_ATTACH: |
@@ -1769,20 +1777,36 @@ static void __ccw_device_pm_restore(struct ccw_device *cdev) | |||
1769 | { | 1777 | { |
1770 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | 1778 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
1771 | 1779 | ||
1772 | if (cio_is_console(sch->schid)) | 1780 | spin_lock_irq(sch->lock); |
1773 | goto out; | 1781 | if (cio_is_console(sch->schid)) { |
1782 | cio_enable_subchannel(sch, (u32)(addr_t)sch); | ||
1783 | goto out_unlock; | ||
1784 | } | ||
1774 | /* | 1785 | /* |
1775 | * While we were sleeping, devices may have gone or become | 1786 | * While we were sleeping, devices may have gone or become |
1776 | * available again. Kick re-detection. | 1787 | * available again. Kick re-detection. |
1777 | */ | 1788 | */ |
1778 | spin_lock_irq(sch->lock); | ||
1779 | 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 | |||
1780 | ccw_device_recognition(cdev); | 1801 | ccw_device_recognition(cdev); |
1781 | spin_unlock_irq(sch->lock); | 1802 | spin_unlock_irq(sch->lock); |
1782 | wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev) || | 1803 | wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev) || |
1783 | cdev->private->state == DEV_STATE_DISCONNECTED); | 1804 | cdev->private->state == DEV_STATE_DISCONNECTED); |
1784 | out: | 1805 | spin_lock_irq(sch->lock); |
1806 | |||
1807 | out_unlock: | ||
1785 | cdev->private->flags.resuming = 0; | 1808 | cdev->private->flags.resuming = 0; |
1809 | spin_unlock_irq(sch->lock); | ||
1786 | } | 1810 | } |
1787 | 1811 | ||
1788 | static int resume_handle_boxed(struct ccw_device *cdev) | 1812 | static int resume_handle_boxed(struct ccw_device *cdev) |
@@ -1806,40 +1830,31 @@ static int resume_handle_disc(struct ccw_device *cdev) | |||
1806 | static int ccw_device_pm_restore(struct device *dev) | 1830 | static int ccw_device_pm_restore(struct device *dev) |
1807 | { | 1831 | { |
1808 | struct ccw_device *cdev = to_ccwdev(dev); | 1832 | struct ccw_device *cdev = to_ccwdev(dev); |
1809 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | 1833 | struct subchannel *sch; |
1810 | int ret = 0, cm_enabled; | 1834 | int ret = 0; |
1811 | 1835 | ||
1812 | __ccw_device_pm_restore(cdev); | 1836 | __ccw_device_pm_restore(cdev); |
1837 | sch = to_subchannel(cdev->dev.parent); | ||
1813 | spin_lock_irq(sch->lock); | 1838 | spin_lock_irq(sch->lock); |
1814 | if (cio_is_console(sch->schid)) { | 1839 | if (cio_is_console(sch->schid)) |
1815 | cio_enable_subchannel(sch, (u32)(addr_t)sch); | ||
1816 | spin_unlock_irq(sch->lock); | ||
1817 | goto out_restore; | 1840 | goto out_restore; |
1818 | } | 1841 | |
1819 | cdev->private->flags.donotify = 0; | ||
1820 | /* check recognition results */ | 1842 | /* check recognition results */ |
1821 | switch (cdev->private->state) { | 1843 | switch (cdev->private->state) { |
1822 | case DEV_STATE_OFFLINE: | 1844 | case DEV_STATE_OFFLINE: |
1845 | case DEV_STATE_ONLINE: | ||
1846 | cdev->private->flags.donotify = 0; | ||
1823 | break; | 1847 | break; |
1824 | case DEV_STATE_BOXED: | 1848 | case DEV_STATE_BOXED: |
1825 | ret = resume_handle_boxed(cdev); | 1849 | ret = resume_handle_boxed(cdev); |
1826 | spin_unlock_irq(sch->lock); | ||
1827 | if (ret) | 1850 | if (ret) |
1828 | goto out; | 1851 | goto out_unlock; |
1829 | goto out_restore; | 1852 | goto out_restore; |
1830 | case DEV_STATE_DISCONNECTED: | ||
1831 | goto out_disc_unlock; | ||
1832 | default: | 1853 | default: |
1833 | goto out_unreg_unlock; | 1854 | ret = resume_handle_disc(cdev); |
1834 | } | 1855 | if (ret) |
1835 | /* check if the device id has changed */ | 1856 | goto out_unlock; |
1836 | if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { | 1857 | goto out_restore; |
1837 | CIO_MSG_EVENT(0, "resume: sch 0.%x.%04x: failed (devno " | ||
1838 | "changed from %04x to %04x)\n", | ||
1839 | sch->schid.ssid, sch->schid.sch_no, | ||
1840 | cdev->private->dev_id.devno, | ||
1841 | sch->schib.pmcw.dev); | ||
1842 | goto out_unreg_unlock; | ||
1843 | } | 1858 | } |
1844 | /* check if the device type has changed */ | 1859 | /* check if the device type has changed */ |
1845 | if (!ccw_device_test_sense_data(cdev)) { | 1860 | if (!ccw_device_test_sense_data(cdev)) { |
@@ -1848,24 +1863,30 @@ static int ccw_device_pm_restore(struct device *dev) | |||
1848 | ret = -ENODEV; | 1863 | ret = -ENODEV; |
1849 | goto out_unlock; | 1864 | goto out_unlock; |
1850 | } | 1865 | } |
1851 | if (!cdev->online) { | 1866 | if (!cdev->online) |
1852 | ret = 0; | ||
1853 | goto out_unlock; | 1867 | goto out_unlock; |
1854 | } | ||
1855 | ret = ccw_device_online(cdev); | ||
1856 | if (ret) | ||
1857 | goto out_disc_unlock; | ||
1858 | 1868 | ||
1859 | 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 | } | ||
1860 | spin_unlock_irq(sch->lock); | 1875 | spin_unlock_irq(sch->lock); |
1861 | |||
1862 | wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); | 1876 | wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); |
1863 | if (cdev->private->state != DEV_STATE_ONLINE) { | 1877 | spin_lock_irq(sch->lock); |
1864 | spin_lock_irq(sch->lock); | 1878 | |
1865 | 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; | ||
1866 | } | 1883 | } |
1867 | if (cm_enabled) { | 1884 | |
1885 | /* reenable cmf, if needed */ | ||
1886 | if (cdev->private->cmb) { | ||
1887 | spin_unlock_irq(sch->lock); | ||
1868 | ret = ccw_set_cmf(cdev, 1); | 1888 | ret = ccw_set_cmf(cdev, 1); |
1889 | spin_lock_irq(sch->lock); | ||
1869 | if (ret) { | 1890 | if (ret) { |
1870 | CIO_MSG_EVENT(2, "resume: cdev 0.%x.%04x: cmf failed " | 1891 | CIO_MSG_EVENT(2, "resume: cdev 0.%x.%04x: cmf failed " |
1871 | "(rc=%d)\n", cdev->private->dev_id.ssid, | 1892 | "(rc=%d)\n", cdev->private->dev_id.ssid, |
@@ -1875,21 +1896,11 @@ static int ccw_device_pm_restore(struct device *dev) | |||
1875 | } | 1896 | } |
1876 | 1897 | ||
1877 | out_restore: | 1898 | out_restore: |
1899 | spin_unlock_irq(sch->lock); | ||
1878 | if (cdev->online && cdev->drv && cdev->drv->restore) | 1900 | if (cdev->online && cdev->drv && cdev->drv->restore) |
1879 | ret = cdev->drv->restore(cdev); | 1901 | ret = cdev->drv->restore(cdev); |
1880 | out: | ||
1881 | return ret; | 1902 | return ret; |
1882 | 1903 | ||
1883 | out_disc_unlock: | ||
1884 | ret = resume_handle_disc(cdev); | ||
1885 | spin_unlock_irq(sch->lock); | ||
1886 | if (ret) | ||
1887 | return ret; | ||
1888 | goto out_restore; | ||
1889 | |||
1890 | out_unreg_unlock: | ||
1891 | ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL); | ||
1892 | ret = -ENODEV; | ||
1893 | out_unlock: | 1904 | out_unlock: |
1894 | spin_unlock_irq(sch->lock); | 1905 | spin_unlock_irq(sch->lock); |
1895 | return ret; | 1906 | return ret; |