diff options
author | Peter Oberparleiter <peter.oberparleiter@de.ibm.com> | 2008-01-26 08:10:52 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-01-26 08:11:06 -0500 |
commit | 90ab133603d066e850fc9ed297b6eb52f888dd25 (patch) | |
tree | ab26ae8c0733a979c8c215fa1d491be3e500cf95 /drivers/s390/cio/device.c | |
parent | 808e48882316dd4a325cd1cc382516945edad77d (diff) |
[S390] cio: introduce timed recovery procedure
Add a timed recovery procedure to reactivate ccw devices in cases
where HW/VM events are not sufficient to allow for proper recovery
of reappearing channel paths.
Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio/device.c')
-rw-r--r-- | drivers/s390/cio/device.c | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index f80d7f5418d3..d35dc3f25d06 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/list.h> | 17 | #include <linux/list.h> |
18 | #include <linux/device.h> | 18 | #include <linux/device.h> |
19 | #include <linux/workqueue.h> | 19 | #include <linux/workqueue.h> |
20 | #include <linux/timer.h> | ||
20 | 21 | ||
21 | #include <asm/ccwdev.h> | 22 | #include <asm/ccwdev.h> |
22 | #include <asm/cio.h> | 23 | #include <asm/cio.h> |
@@ -30,6 +31,11 @@ | |||
30 | #include "ioasm.h" | 31 | #include "ioasm.h" |
31 | #include "io_sch.h" | 32 | #include "io_sch.h" |
32 | 33 | ||
34 | static struct timer_list recovery_timer; | ||
35 | static spinlock_t recovery_lock; | ||
36 | static int recovery_phase; | ||
37 | static const unsigned long recovery_delay[] = { 3, 30, 300 }; | ||
38 | |||
33 | /******************* bus type handling ***********************/ | 39 | /******************* bus type handling ***********************/ |
34 | 40 | ||
35 | /* The Linux driver model distinguishes between a bus type and | 41 | /* The Linux driver model distinguishes between a bus type and |
@@ -142,6 +148,8 @@ struct workqueue_struct *ccw_device_notify_work; | |||
142 | wait_queue_head_t ccw_device_init_wq; | 148 | wait_queue_head_t ccw_device_init_wq; |
143 | atomic_t ccw_device_init_count; | 149 | atomic_t ccw_device_init_count; |
144 | 150 | ||
151 | static void recovery_func(unsigned long data); | ||
152 | |||
145 | static int __init | 153 | static int __init |
146 | init_ccw_bus_type (void) | 154 | init_ccw_bus_type (void) |
147 | { | 155 | { |
@@ -149,6 +157,7 @@ init_ccw_bus_type (void) | |||
149 | 157 | ||
150 | init_waitqueue_head(&ccw_device_init_wq); | 158 | init_waitqueue_head(&ccw_device_init_wq); |
151 | atomic_set(&ccw_device_init_count, 0); | 159 | atomic_set(&ccw_device_init_count, 0); |
160 | setup_timer(&recovery_timer, recovery_func, 0); | ||
152 | 161 | ||
153 | ccw_device_work = create_singlethread_workqueue("cio"); | 162 | ccw_device_work = create_singlethread_workqueue("cio"); |
154 | if (!ccw_device_work) | 163 | if (!ccw_device_work) |
@@ -1503,6 +1512,60 @@ ccw_device_get_subchannel_id(struct ccw_device *cdev) | |||
1503 | return sch->schid; | 1512 | return sch->schid; |
1504 | } | 1513 | } |
1505 | 1514 | ||
1515 | static int recovery_check(struct device *dev, void *data) | ||
1516 | { | ||
1517 | struct ccw_device *cdev = to_ccwdev(dev); | ||
1518 | int *redo = data; | ||
1519 | |||
1520 | spin_lock_irq(cdev->ccwlock); | ||
1521 | switch (cdev->private->state) { | ||
1522 | case DEV_STATE_DISCONNECTED: | ||
1523 | CIO_MSG_EVENT(3, "recovery: trigger 0.%x.%04x\n", | ||
1524 | cdev->private->dev_id.ssid, | ||
1525 | cdev->private->dev_id.devno); | ||
1526 | dev_fsm_event(cdev, DEV_EVENT_VERIFY); | ||
1527 | *redo = 1; | ||
1528 | break; | ||
1529 | case DEV_STATE_DISCONNECTED_SENSE_ID: | ||
1530 | *redo = 1; | ||
1531 | break; | ||
1532 | } | ||
1533 | spin_unlock_irq(cdev->ccwlock); | ||
1534 | |||
1535 | return 0; | ||
1536 | } | ||
1537 | |||
1538 | static void recovery_func(unsigned long data) | ||
1539 | { | ||
1540 | int redo = 0; | ||
1541 | |||
1542 | bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check); | ||
1543 | if (redo) { | ||
1544 | spin_lock_irq(&recovery_lock); | ||
1545 | if (!timer_pending(&recovery_timer)) { | ||
1546 | if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1) | ||
1547 | recovery_phase++; | ||
1548 | mod_timer(&recovery_timer, jiffies + | ||
1549 | recovery_delay[recovery_phase] * HZ); | ||
1550 | } | ||
1551 | spin_unlock_irq(&recovery_lock); | ||
1552 | } else | ||
1553 | CIO_MSG_EVENT(2, "recovery: end\n"); | ||
1554 | } | ||
1555 | |||
1556 | void ccw_device_schedule_recovery(void) | ||
1557 | { | ||
1558 | unsigned long flags; | ||
1559 | |||
1560 | CIO_MSG_EVENT(2, "recovery: schedule\n"); | ||
1561 | spin_lock_irqsave(&recovery_lock, flags); | ||
1562 | if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) { | ||
1563 | recovery_phase = 0; | ||
1564 | mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ); | ||
1565 | } | ||
1566 | spin_unlock_irqrestore(&recovery_lock, flags); | ||
1567 | } | ||
1568 | |||
1506 | MODULE_LICENSE("GPL"); | 1569 | MODULE_LICENSE("GPL"); |
1507 | EXPORT_SYMBOL(ccw_device_set_online); | 1570 | EXPORT_SYMBOL(ccw_device_set_online); |
1508 | EXPORT_SYMBOL(ccw_device_set_offline); | 1571 | EXPORT_SYMBOL(ccw_device_set_offline); |