diff options
Diffstat (limited to 'drivers/s390/cio/device.c')
-rw-r--r-- | drivers/s390/cio/device.c | 118 |
1 files changed, 82 insertions, 36 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 28221030b886..4e78c82194b4 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include "device.h" | 31 | #include "device.h" |
32 | #include "ioasm.h" | 32 | #include "ioasm.h" |
33 | #include "io_sch.h" | 33 | #include "io_sch.h" |
34 | #include "blacklist.h" | ||
34 | 35 | ||
35 | static struct timer_list recovery_timer; | 36 | static struct timer_list recovery_timer; |
36 | static DEFINE_SPINLOCK(recovery_lock); | 37 | static DEFINE_SPINLOCK(recovery_lock); |
@@ -296,36 +297,33 @@ static void ccw_device_unregister(struct ccw_device *cdev) | |||
296 | device_del(&cdev->dev); | 297 | device_del(&cdev->dev); |
297 | } | 298 | } |
298 | 299 | ||
299 | static void ccw_device_remove_orphan_cb(struct device *dev) | 300 | static void ccw_device_remove_orphan_cb(struct work_struct *work) |
300 | { | 301 | { |
301 | struct ccw_device *cdev = to_ccwdev(dev); | 302 | struct ccw_device_private *priv; |
303 | struct ccw_device *cdev; | ||
302 | 304 | ||
305 | priv = container_of(work, struct ccw_device_private, kick_work); | ||
306 | cdev = priv->cdev; | ||
303 | ccw_device_unregister(cdev); | 307 | ccw_device_unregister(cdev); |
304 | put_device(&cdev->dev); | 308 | put_device(&cdev->dev); |
309 | /* Release cdev reference for workqueue processing. */ | ||
310 | put_device(&cdev->dev); | ||
305 | } | 311 | } |
306 | 312 | ||
307 | static void ccw_device_remove_sch_cb(struct device *dev) | 313 | static void ccw_device_call_sch_unregister(struct work_struct *work); |
308 | { | ||
309 | struct subchannel *sch; | ||
310 | |||
311 | sch = to_subchannel(dev); | ||
312 | css_sch_device_unregister(sch); | ||
313 | /* Reset intparm to zeroes. */ | ||
314 | sch->schib.pmcw.intparm = 0; | ||
315 | cio_modify(sch); | ||
316 | put_device(&sch->dev); | ||
317 | } | ||
318 | 314 | ||
319 | static void | 315 | static void |
320 | ccw_device_remove_disconnected(struct ccw_device *cdev) | 316 | ccw_device_remove_disconnected(struct ccw_device *cdev) |
321 | { | 317 | { |
322 | unsigned long flags; | 318 | unsigned long flags; |
323 | int rc; | ||
324 | 319 | ||
325 | /* | 320 | /* |
326 | * Forced offline in disconnected state means | 321 | * Forced offline in disconnected state means |
327 | * 'throw away device'. | 322 | * 'throw away device'. |
328 | */ | 323 | */ |
324 | /* Get cdev reference for workqueue processing. */ | ||
325 | if (!get_device(&cdev->dev)) | ||
326 | return; | ||
329 | if (ccw_device_is_orphan(cdev)) { | 327 | if (ccw_device_is_orphan(cdev)) { |
330 | /* | 328 | /* |
331 | * Deregister ccw device. | 329 | * Deregister ccw device. |
@@ -335,23 +333,13 @@ ccw_device_remove_disconnected(struct ccw_device *cdev) | |||
335 | spin_lock_irqsave(cdev->ccwlock, flags); | 333 | spin_lock_irqsave(cdev->ccwlock, flags); |
336 | cdev->private->state = DEV_STATE_NOT_OPER; | 334 | cdev->private->state = DEV_STATE_NOT_OPER; |
337 | spin_unlock_irqrestore(cdev->ccwlock, flags); | 335 | spin_unlock_irqrestore(cdev->ccwlock, flags); |
338 | rc = device_schedule_callback(&cdev->dev, | 336 | PREPARE_WORK(&cdev->private->kick_work, |
339 | ccw_device_remove_orphan_cb); | 337 | ccw_device_remove_orphan_cb); |
340 | if (rc) | 338 | } else |
341 | CIO_MSG_EVENT(0, "Couldn't unregister orphan " | 339 | /* Deregister subchannel, which will kill the ccw device. */ |
342 | "0.%x.%04x\n", | 340 | PREPARE_WORK(&cdev->private->kick_work, |
343 | cdev->private->dev_id.ssid, | 341 | ccw_device_call_sch_unregister); |
344 | cdev->private->dev_id.devno); | 342 | queue_work(slow_path_wq, &cdev->private->kick_work); |
345 | return; | ||
346 | } | ||
347 | /* Deregister subchannel, which will kill the ccw device. */ | ||
348 | rc = device_schedule_callback(cdev->dev.parent, | ||
349 | ccw_device_remove_sch_cb); | ||
350 | if (rc) | ||
351 | CIO_MSG_EVENT(0, "Couldn't unregister disconnected device " | ||
352 | "0.%x.%04x\n", | ||
353 | cdev->private->dev_id.ssid, | ||
354 | cdev->private->dev_id.devno); | ||
355 | } | 343 | } |
356 | 344 | ||
357 | /** | 345 | /** |
@@ -970,12 +958,17 @@ static void ccw_device_call_sch_unregister(struct work_struct *work) | |||
970 | 958 | ||
971 | priv = container_of(work, struct ccw_device_private, kick_work); | 959 | priv = container_of(work, struct ccw_device_private, kick_work); |
972 | cdev = priv->cdev; | 960 | cdev = priv->cdev; |
961 | /* Get subchannel reference for local processing. */ | ||
962 | if (!get_device(cdev->dev.parent)) | ||
963 | return; | ||
973 | sch = to_subchannel(cdev->dev.parent); | 964 | sch = to_subchannel(cdev->dev.parent); |
974 | css_sch_device_unregister(sch); | 965 | css_sch_device_unregister(sch); |
975 | /* Reset intparm to zeroes. */ | 966 | /* Reset intparm to zeroes. */ |
976 | sch->schib.pmcw.intparm = 0; | 967 | sch->schib.pmcw.intparm = 0; |
977 | cio_modify(sch); | 968 | cio_modify(sch); |
969 | /* Release cdev reference for workqueue processing.*/ | ||
978 | put_device(&cdev->dev); | 970 | put_device(&cdev->dev); |
971 | /* Release subchannel reference for local processing. */ | ||
979 | put_device(&sch->dev); | 972 | put_device(&sch->dev); |
980 | } | 973 | } |
981 | 974 | ||
@@ -1001,6 +994,8 @@ io_subchannel_recog_done(struct ccw_device *cdev) | |||
1001 | PREPARE_WORK(&cdev->private->kick_work, | 994 | PREPARE_WORK(&cdev->private->kick_work, |
1002 | ccw_device_call_sch_unregister); | 995 | ccw_device_call_sch_unregister); |
1003 | queue_work(slow_path_wq, &cdev->private->kick_work); | 996 | queue_work(slow_path_wq, &cdev->private->kick_work); |
997 | /* Release subchannel reference for asynchronous recognition. */ | ||
998 | put_device(&sch->dev); | ||
1004 | if (atomic_dec_and_test(&ccw_device_init_count)) | 999 | if (atomic_dec_and_test(&ccw_device_init_count)) |
1005 | wake_up(&ccw_device_init_wq); | 1000 | wake_up(&ccw_device_init_wq); |
1006 | break; | 1001 | break; |
@@ -1040,8 +1035,11 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) | |||
1040 | init_timer(&priv->timer); | 1035 | init_timer(&priv->timer); |
1041 | 1036 | ||
1042 | /* Set an initial name for the device. */ | 1037 | /* Set an initial name for the device. */ |
1043 | snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", | 1038 | if (cio_is_console(sch->schid)) |
1044 | sch->schid.ssid, sch->schib.pmcw.dev); | 1039 | cdev->dev.init_name = cio_get_console_cdev_name(sch); |
1040 | else | ||
1041 | dev_set_name(&cdev->dev, "0.%x.%04x", | ||
1042 | sch->schid.ssid, sch->schib.pmcw.dev); | ||
1045 | 1043 | ||
1046 | /* Increase counter of devices currently in recognition. */ | 1044 | /* Increase counter of devices currently in recognition. */ |
1047 | atomic_inc(&ccw_device_init_count); | 1045 | atomic_inc(&ccw_device_init_count); |
@@ -1106,7 +1104,7 @@ static void io_subchannel_irq(struct subchannel *sch) | |||
1106 | cdev = sch_get_cdev(sch); | 1104 | cdev = sch_get_cdev(sch); |
1107 | 1105 | ||
1108 | CIO_TRACE_EVENT(3, "IRQ"); | 1106 | CIO_TRACE_EVENT(3, "IRQ"); |
1109 | CIO_TRACE_EVENT(3, sch->dev.bus_id); | 1107 | CIO_TRACE_EVENT(3, dev_name(&sch->dev)); |
1110 | if (cdev) | 1108 | if (cdev) |
1111 | dev_fsm_event(cdev, DEV_EVENT_INTERRUPT); | 1109 | dev_fsm_event(cdev, DEV_EVENT_INTERRUPT); |
1112 | } | 1110 | } |
@@ -1476,6 +1474,45 @@ static void ccw_device_schedule_recovery(void) | |||
1476 | spin_unlock_irqrestore(&recovery_lock, flags); | 1474 | spin_unlock_irqrestore(&recovery_lock, flags); |
1477 | } | 1475 | } |
1478 | 1476 | ||
1477 | static int purge_fn(struct device *dev, void *data) | ||
1478 | { | ||
1479 | struct ccw_device *cdev = to_ccwdev(dev); | ||
1480 | struct ccw_device_private *priv = cdev->private; | ||
1481 | int unreg; | ||
1482 | |||
1483 | spin_lock_irq(cdev->ccwlock); | ||
1484 | unreg = is_blacklisted(priv->dev_id.ssid, priv->dev_id.devno) && | ||
1485 | (priv->state == DEV_STATE_OFFLINE); | ||
1486 | spin_unlock_irq(cdev->ccwlock); | ||
1487 | if (!unreg) | ||
1488 | goto out; | ||
1489 | if (!get_device(&cdev->dev)) | ||
1490 | goto out; | ||
1491 | CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", priv->dev_id.ssid, | ||
1492 | priv->dev_id.devno); | ||
1493 | PREPARE_WORK(&cdev->private->kick_work, ccw_device_call_sch_unregister); | ||
1494 | queue_work(slow_path_wq, &cdev->private->kick_work); | ||
1495 | |||
1496 | out: | ||
1497 | /* Abort loop in case of pending signal. */ | ||
1498 | if (signal_pending(current)) | ||
1499 | return -EINTR; | ||
1500 | |||
1501 | return 0; | ||
1502 | } | ||
1503 | |||
1504 | /** | ||
1505 | * ccw_purge_blacklisted - purge unused, blacklisted devices | ||
1506 | * | ||
1507 | * Unregister all ccw devices that are offline and on the blacklist. | ||
1508 | */ | ||
1509 | int ccw_purge_blacklisted(void) | ||
1510 | { | ||
1511 | CIO_MSG_EVENT(2, "ccw: purging blacklisted devices\n"); | ||
1512 | bus_for_each_dev(&ccw_bus_type, NULL, NULL, purge_fn); | ||
1513 | return 0; | ||
1514 | } | ||
1515 | |||
1479 | static void device_set_disconnected(struct ccw_device *cdev) | 1516 | static void device_set_disconnected(struct ccw_device *cdev) |
1480 | { | 1517 | { |
1481 | if (!cdev) | 1518 | if (!cdev) |
@@ -1492,7 +1529,7 @@ void ccw_device_set_notoper(struct ccw_device *cdev) | |||
1492 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | 1529 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
1493 | 1530 | ||
1494 | CIO_TRACE_EVENT(2, "notoper"); | 1531 | CIO_TRACE_EVENT(2, "notoper"); |
1495 | CIO_TRACE_EVENT(2, sch->dev.bus_id); | 1532 | CIO_TRACE_EVENT(2, dev_name(&sch->dev)); |
1496 | ccw_device_set_timeout(cdev, 0); | 1533 | ccw_device_set_timeout(cdev, 0); |
1497 | cio_disable_subchannel(sch); | 1534 | cio_disable_subchannel(sch); |
1498 | cdev->private->state = DEV_STATE_NOT_OPER; | 1535 | cdev->private->state = DEV_STATE_NOT_OPER; |
@@ -1591,6 +1628,7 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow) | |||
1591 | 1628 | ||
1592 | #ifdef CONFIG_CCW_CONSOLE | 1629 | #ifdef CONFIG_CCW_CONSOLE |
1593 | static struct ccw_device console_cdev; | 1630 | static struct ccw_device console_cdev; |
1631 | static char console_cdev_name[10] = "0.x.xxxx"; | ||
1594 | static struct ccw_device_private console_private; | 1632 | static struct ccw_device_private console_private; |
1595 | static int console_cdev_in_use; | 1633 | static int console_cdev_in_use; |
1596 | 1634 | ||
@@ -1661,6 +1699,14 @@ ccw_device_probe_console(void) | |||
1661 | console_cdev.online = 1; | 1699 | console_cdev.online = 1; |
1662 | return &console_cdev; | 1700 | return &console_cdev; |
1663 | } | 1701 | } |
1702 | |||
1703 | |||
1704 | const char *cio_get_console_cdev_name(struct subchannel *sch) | ||
1705 | { | ||
1706 | snprintf(console_cdev_name, 10, "0.%x.%04x", | ||
1707 | sch->schid.ssid, sch->schib.pmcw.dev); | ||
1708 | return (const char *)console_cdev_name; | ||
1709 | } | ||
1664 | #endif | 1710 | #endif |
1665 | 1711 | ||
1666 | /* | 1712 | /* |
@@ -1673,7 +1719,7 @@ __ccwdev_check_busid(struct device *dev, void *id) | |||
1673 | 1719 | ||
1674 | bus_id = id; | 1720 | bus_id = id; |
1675 | 1721 | ||
1676 | return (strncmp(bus_id, dev->bus_id, BUS_ID_SIZE) == 0); | 1722 | return (strncmp(bus_id, dev_name(dev), BUS_ID_SIZE) == 0); |
1677 | } | 1723 | } |
1678 | 1724 | ||
1679 | 1725 | ||