diff options
Diffstat (limited to 'drivers/s390/cio/device.c')
-rw-r--r-- | drivers/s390/cio/device.c | 88 |
1 files changed, 64 insertions, 24 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 51bd3687d163..8e04c00cf0ad 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -127,7 +127,7 @@ static int ccw_uevent(struct device *dev, struct kobj_uevent_env *env) | |||
127 | return ret; | 127 | return ret; |
128 | } | 128 | } |
129 | 129 | ||
130 | struct bus_type ccw_bus_type; | 130 | static struct bus_type ccw_bus_type; |
131 | 131 | ||
132 | static void io_subchannel_irq(struct subchannel *); | 132 | static void io_subchannel_irq(struct subchannel *); |
133 | static int io_subchannel_probe(struct subchannel *); | 133 | static int io_subchannel_probe(struct subchannel *); |
@@ -172,9 +172,11 @@ static int io_subchannel_settle(void) | |||
172 | } | 172 | } |
173 | 173 | ||
174 | static struct css_driver io_subchannel_driver = { | 174 | static struct css_driver io_subchannel_driver = { |
175 | .owner = THIS_MODULE, | 175 | .drv = { |
176 | .owner = THIS_MODULE, | ||
177 | .name = "io_subchannel", | ||
178 | }, | ||
176 | .subchannel_type = io_subchannel_ids, | 179 | .subchannel_type = io_subchannel_ids, |
177 | .name = "io_subchannel", | ||
178 | .irq = io_subchannel_irq, | 180 | .irq = io_subchannel_irq, |
179 | .sch_event = io_subchannel_sch_event, | 181 | .sch_event = io_subchannel_sch_event, |
180 | .chp_event = io_subchannel_chp_event, | 182 | .chp_event = io_subchannel_chp_event, |
@@ -539,15 +541,24 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr, | |||
539 | int force, ret; | 541 | int force, ret; |
540 | unsigned long i; | 542 | unsigned long i; |
541 | 543 | ||
542 | if (!dev_fsm_final_state(cdev) && | 544 | /* Prevent conflict between multiple on-/offline processing requests. */ |
543 | cdev->private->state != DEV_STATE_DISCONNECTED) | ||
544 | return -EAGAIN; | ||
545 | if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) | 545 | if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) |
546 | return -EAGAIN; | 546 | return -EAGAIN; |
547 | /* Prevent conflict between internal I/Os and on-/offline processing. */ | ||
548 | if (!dev_fsm_final_state(cdev) && | ||
549 | cdev->private->state != DEV_STATE_DISCONNECTED) { | ||
550 | ret = -EAGAIN; | ||
551 | goto out_onoff; | ||
552 | } | ||
553 | /* Prevent conflict between pending work and on-/offline processing.*/ | ||
554 | if (work_pending(&cdev->private->todo_work)) { | ||
555 | ret = -EAGAIN; | ||
556 | goto out_onoff; | ||
557 | } | ||
547 | 558 | ||
548 | if (cdev->drv && !try_module_get(cdev->drv->owner)) { | 559 | if (cdev->drv && !try_module_get(cdev->drv->driver.owner)) { |
549 | atomic_set(&cdev->private->onoff, 0); | 560 | ret = -EINVAL; |
550 | return -EINVAL; | 561 | goto out_onoff; |
551 | } | 562 | } |
552 | if (!strncmp(buf, "force\n", count)) { | 563 | if (!strncmp(buf, "force\n", count)) { |
553 | force = 1; | 564 | force = 1; |
@@ -571,7 +582,8 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr, | |||
571 | } | 582 | } |
572 | out: | 583 | out: |
573 | if (cdev->drv) | 584 | if (cdev->drv) |
574 | module_put(cdev->drv->owner); | 585 | module_put(cdev->drv->driver.owner); |
586 | out_onoff: | ||
575 | atomic_set(&cdev->private->onoff, 0); | 587 | atomic_set(&cdev->private->onoff, 0); |
576 | return (ret < 0) ? ret : count; | 588 | return (ret < 0) ? ret : count; |
577 | } | 589 | } |
@@ -1030,6 +1042,7 @@ static void io_subchannel_init_fields(struct subchannel *sch) | |||
1030 | */ | 1042 | */ |
1031 | static int io_subchannel_probe(struct subchannel *sch) | 1043 | static int io_subchannel_probe(struct subchannel *sch) |
1032 | { | 1044 | { |
1045 | struct io_subchannel_private *io_priv; | ||
1033 | struct ccw_device *cdev; | 1046 | struct ccw_device *cdev; |
1034 | int rc; | 1047 | int rc; |
1035 | 1048 | ||
@@ -1073,10 +1086,11 @@ static int io_subchannel_probe(struct subchannel *sch) | |||
1073 | if (rc) | 1086 | if (rc) |
1074 | goto out_schedule; | 1087 | goto out_schedule; |
1075 | /* Allocate I/O subchannel private data. */ | 1088 | /* Allocate I/O subchannel private data. */ |
1076 | sch->private = kzalloc(sizeof(struct io_subchannel_private), | 1089 | io_priv = kzalloc(sizeof(*io_priv), GFP_KERNEL | GFP_DMA); |
1077 | GFP_KERNEL | GFP_DMA); | 1090 | if (!io_priv) |
1078 | if (!sch->private) | ||
1079 | goto out_schedule; | 1091 | goto out_schedule; |
1092 | |||
1093 | set_io_private(sch, io_priv); | ||
1080 | css_schedule_eval(sch->schid); | 1094 | css_schedule_eval(sch->schid); |
1081 | return 0; | 1095 | return 0; |
1082 | 1096 | ||
@@ -1090,6 +1104,7 @@ out_schedule: | |||
1090 | static int | 1104 | static int |
1091 | io_subchannel_remove (struct subchannel *sch) | 1105 | io_subchannel_remove (struct subchannel *sch) |
1092 | { | 1106 | { |
1107 | struct io_subchannel_private *io_priv = to_io_private(sch); | ||
1093 | struct ccw_device *cdev; | 1108 | struct ccw_device *cdev; |
1094 | 1109 | ||
1095 | cdev = sch_get_cdev(sch); | 1110 | cdev = sch_get_cdev(sch); |
@@ -1099,11 +1114,12 @@ io_subchannel_remove (struct subchannel *sch) | |||
1099 | /* Set ccw device to not operational and drop reference. */ | 1114 | /* Set ccw device to not operational and drop reference. */ |
1100 | spin_lock_irq(cdev->ccwlock); | 1115 | spin_lock_irq(cdev->ccwlock); |
1101 | sch_set_cdev(sch, NULL); | 1116 | sch_set_cdev(sch, NULL); |
1117 | set_io_private(sch, NULL); | ||
1102 | cdev->private->state = DEV_STATE_NOT_OPER; | 1118 | cdev->private->state = DEV_STATE_NOT_OPER; |
1103 | spin_unlock_irq(cdev->ccwlock); | 1119 | spin_unlock_irq(cdev->ccwlock); |
1104 | ccw_device_unregister(cdev); | 1120 | ccw_device_unregister(cdev); |
1105 | out_free: | 1121 | out_free: |
1106 | kfree(sch->private); | 1122 | kfree(io_priv); |
1107 | sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group); | 1123 | sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group); |
1108 | return 0; | 1124 | return 0; |
1109 | } | 1125 | } |
@@ -1147,6 +1163,7 @@ err: | |||
1147 | static int io_subchannel_chp_event(struct subchannel *sch, | 1163 | static int io_subchannel_chp_event(struct subchannel *sch, |
1148 | struct chp_link *link, int event) | 1164 | struct chp_link *link, int event) |
1149 | { | 1165 | { |
1166 | struct ccw_device *cdev = sch_get_cdev(sch); | ||
1150 | int mask; | 1167 | int mask; |
1151 | 1168 | ||
1152 | mask = chp_ssd_get_mask(&sch->ssd_info, link); | 1169 | mask = chp_ssd_get_mask(&sch->ssd_info, link); |
@@ -1156,22 +1173,30 @@ static int io_subchannel_chp_event(struct subchannel *sch, | |||
1156 | case CHP_VARY_OFF: | 1173 | case CHP_VARY_OFF: |
1157 | sch->opm &= ~mask; | 1174 | sch->opm &= ~mask; |
1158 | sch->lpm &= ~mask; | 1175 | sch->lpm &= ~mask; |
1176 | if (cdev) | ||
1177 | cdev->private->path_gone_mask |= mask; | ||
1159 | io_subchannel_terminate_path(sch, mask); | 1178 | io_subchannel_terminate_path(sch, mask); |
1160 | break; | 1179 | break; |
1161 | case CHP_VARY_ON: | 1180 | case CHP_VARY_ON: |
1162 | sch->opm |= mask; | 1181 | sch->opm |= mask; |
1163 | sch->lpm |= mask; | 1182 | sch->lpm |= mask; |
1183 | if (cdev) | ||
1184 | cdev->private->path_new_mask |= mask; | ||
1164 | io_subchannel_verify(sch); | 1185 | io_subchannel_verify(sch); |
1165 | break; | 1186 | break; |
1166 | case CHP_OFFLINE: | 1187 | case CHP_OFFLINE: |
1167 | if (cio_update_schib(sch)) | 1188 | if (cio_update_schib(sch)) |
1168 | return -ENODEV; | 1189 | return -ENODEV; |
1190 | if (cdev) | ||
1191 | cdev->private->path_gone_mask |= mask; | ||
1169 | io_subchannel_terminate_path(sch, mask); | 1192 | io_subchannel_terminate_path(sch, mask); |
1170 | break; | 1193 | break; |
1171 | case CHP_ONLINE: | 1194 | case CHP_ONLINE: |
1172 | if (cio_update_schib(sch)) | 1195 | if (cio_update_schib(sch)) |
1173 | return -ENODEV; | 1196 | return -ENODEV; |
1174 | sch->lpm |= mask & sch->opm; | 1197 | sch->lpm |= mask & sch->opm; |
1198 | if (cdev) | ||
1199 | cdev->private->path_new_mask |= mask; | ||
1175 | io_subchannel_verify(sch); | 1200 | io_subchannel_verify(sch); |
1176 | break; | 1201 | break; |
1177 | } | 1202 | } |
@@ -1196,6 +1221,7 @@ static void io_subchannel_quiesce(struct subchannel *sch) | |||
1196 | cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO)); | 1221 | cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO)); |
1197 | while (ret == -EBUSY) { | 1222 | while (ret == -EBUSY) { |
1198 | cdev->private->state = DEV_STATE_QUIESCE; | 1223 | cdev->private->state = DEV_STATE_QUIESCE; |
1224 | cdev->private->iretry = 255; | ||
1199 | ret = ccw_device_cancel_halt_clear(cdev); | 1225 | ret = ccw_device_cancel_halt_clear(cdev); |
1200 | if (ret == -EBUSY) { | 1226 | if (ret == -EBUSY) { |
1201 | ccw_device_set_timeout(cdev, HZ/10); | 1227 | ccw_device_set_timeout(cdev, HZ/10); |
@@ -1295,10 +1321,12 @@ static int purge_fn(struct device *dev, void *data) | |||
1295 | 1321 | ||
1296 | spin_lock_irq(cdev->ccwlock); | 1322 | spin_lock_irq(cdev->ccwlock); |
1297 | if (is_blacklisted(id->ssid, id->devno) && | 1323 | if (is_blacklisted(id->ssid, id->devno) && |
1298 | (cdev->private->state == DEV_STATE_OFFLINE)) { | 1324 | (cdev->private->state == DEV_STATE_OFFLINE) && |
1325 | (atomic_cmpxchg(&cdev->private->onoff, 0, 1) == 0)) { | ||
1299 | CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", id->ssid, | 1326 | CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", id->ssid, |
1300 | id->devno); | 1327 | id->devno); |
1301 | ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); | 1328 | ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); |
1329 | atomic_set(&cdev->private->onoff, 0); | ||
1302 | } | 1330 | } |
1303 | spin_unlock_irq(cdev->ccwlock); | 1331 | spin_unlock_irq(cdev->ccwlock); |
1304 | /* Abort loop in case of pending signal. */ | 1332 | /* Abort loop in case of pending signal. */ |
@@ -1445,7 +1473,16 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) | |||
1445 | break; | 1473 | break; |
1446 | case IO_SCH_UNREG_ATTACH: | 1474 | case IO_SCH_UNREG_ATTACH: |
1447 | case IO_SCH_UNREG: | 1475 | case IO_SCH_UNREG: |
1448 | if (cdev) | 1476 | if (!cdev) |
1477 | break; | ||
1478 | if (cdev->private->state == DEV_STATE_SENSE_ID) { | ||
1479 | /* | ||
1480 | * Note: delayed work triggered by this event | ||
1481 | * and repeated calls to sch_event are synchronized | ||
1482 | * by the above check for work_pending(cdev). | ||
1483 | */ | ||
1484 | dev_fsm_event(cdev, DEV_EVENT_NOTOPER); | ||
1485 | } else | ||
1449 | ccw_device_set_notoper(cdev); | 1486 | ccw_device_set_notoper(cdev); |
1450 | break; | 1487 | break; |
1451 | case IO_SCH_NOP: | 1488 | case IO_SCH_NOP: |
@@ -1468,9 +1505,13 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) | |||
1468 | goto out; | 1505 | goto out; |
1469 | break; | 1506 | break; |
1470 | case IO_SCH_UNREG_ATTACH: | 1507 | case IO_SCH_UNREG_ATTACH: |
1508 | if (cdev->private->flags.resuming) { | ||
1509 | /* Device will be handled later. */ | ||
1510 | rc = 0; | ||
1511 | goto out; | ||
1512 | } | ||
1471 | /* Unregister ccw device. */ | 1513 | /* Unregister ccw device. */ |
1472 | if (!cdev->private->flags.resuming) | 1514 | ccw_device_unregister(cdev); |
1473 | ccw_device_unregister(cdev); | ||
1474 | break; | 1515 | break; |
1475 | default: | 1516 | default: |
1476 | break; | 1517 | break; |
@@ -1530,11 +1571,12 @@ spinlock_t * cio_get_console_lock(void) | |||
1530 | static int ccw_device_console_enable(struct ccw_device *cdev, | 1571 | static int ccw_device_console_enable(struct ccw_device *cdev, |
1531 | struct subchannel *sch) | 1572 | struct subchannel *sch) |
1532 | { | 1573 | { |
1574 | struct io_subchannel_private *io_priv = cio_get_console_priv(); | ||
1533 | int rc; | 1575 | int rc; |
1534 | 1576 | ||
1535 | /* Attach subchannel private data. */ | 1577 | /* Attach subchannel private data. */ |
1536 | sch->private = cio_get_console_priv(); | 1578 | memset(io_priv, 0, sizeof(*io_priv)); |
1537 | memset(sch->private, 0, sizeof(struct io_subchannel_private)); | 1579 | set_io_private(sch, io_priv); |
1538 | io_subchannel_init_fields(sch); | 1580 | io_subchannel_init_fields(sch); |
1539 | rc = cio_commit_config(sch); | 1581 | rc = cio_commit_config(sch); |
1540 | if (rc) | 1582 | if (rc) |
@@ -1812,6 +1854,7 @@ static void __ccw_device_pm_restore(struct ccw_device *cdev) | |||
1812 | * available again. Kick re-detection. | 1854 | * available again. Kick re-detection. |
1813 | */ | 1855 | */ |
1814 | cdev->private->flags.resuming = 1; | 1856 | cdev->private->flags.resuming = 1; |
1857 | cdev->private->path_new_mask = LPM_ANYPATH; | ||
1815 | css_schedule_eval(sch->schid); | 1858 | css_schedule_eval(sch->schid); |
1816 | spin_unlock_irq(sch->lock); | 1859 | spin_unlock_irq(sch->lock); |
1817 | css_complete_work(); | 1860 | css_complete_work(); |
@@ -1939,7 +1982,7 @@ static const struct dev_pm_ops ccw_pm_ops = { | |||
1939 | .restore = ccw_device_pm_restore, | 1982 | .restore = ccw_device_pm_restore, |
1940 | }; | 1983 | }; |
1941 | 1984 | ||
1942 | struct bus_type ccw_bus_type = { | 1985 | static struct bus_type ccw_bus_type = { |
1943 | .name = "ccw", | 1986 | .name = "ccw", |
1944 | .match = ccw_bus_match, | 1987 | .match = ccw_bus_match, |
1945 | .uevent = ccw_uevent, | 1988 | .uevent = ccw_uevent, |
@@ -1962,8 +2005,6 @@ int ccw_driver_register(struct ccw_driver *cdriver) | |||
1962 | struct device_driver *drv = &cdriver->driver; | 2005 | struct device_driver *drv = &cdriver->driver; |
1963 | 2006 | ||
1964 | drv->bus = &ccw_bus_type; | 2007 | drv->bus = &ccw_bus_type; |
1965 | drv->name = cdriver->name; | ||
1966 | drv->owner = cdriver->owner; | ||
1967 | 2008 | ||
1968 | return driver_register(drv); | 2009 | return driver_register(drv); |
1969 | } | 2010 | } |
@@ -2081,5 +2122,4 @@ EXPORT_SYMBOL(ccw_device_set_offline); | |||
2081 | EXPORT_SYMBOL(ccw_driver_register); | 2122 | EXPORT_SYMBOL(ccw_driver_register); |
2082 | EXPORT_SYMBOL(ccw_driver_unregister); | 2123 | EXPORT_SYMBOL(ccw_driver_unregister); |
2083 | EXPORT_SYMBOL(get_ccwdev_by_busid); | 2124 | EXPORT_SYMBOL(get_ccwdev_by_busid); |
2084 | EXPORT_SYMBOL(ccw_bus_type); | ||
2085 | EXPORT_SYMBOL_GPL(ccw_device_get_subchannel_id); | 2125 | EXPORT_SYMBOL_GPL(ccw_device_get_subchannel_id); |