diff options
author | Peter Oberparleiter <peter.oberparleiter@de.ibm.com> | 2008-01-26 13:10:52 (GMT) |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-01-26 13:11:06 (GMT) |
commit | 90ab133603d066e850fc9ed297b6eb52f888dd25 (patch) | |
tree | ab26ae8c0733a979c8c215fa1d491be3e500cf95 /drivers/s390/cio/device.c | |
parent | 808e48882316dd4a325cd1cc382516945edad77d (diff) | |
download | linux-fsl-qoriq-90ab133603d066e850fc9ed297b6eb52f888dd25.tar.xz |
[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 f80d7f5..d35dc3f 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -17,6 +17,7 @@ #include <linux/list.h> #include <linux/device.h> #include <linux/workqueue.h> +#include <linux/timer.h> #include <asm/ccwdev.h> #include <asm/cio.h> @@ -30,6 +31,11 @@ #include "ioasm.h" #include "io_sch.h" +static struct timer_list recovery_timer; +static spinlock_t recovery_lock; +static int recovery_phase; +static const unsigned long recovery_delay[] = { 3, 30, 300 }; + /******************* bus type handling ***********************/ /* The Linux driver model distinguishes between a bus type and @@ -142,6 +148,8 @@ struct workqueue_struct *ccw_device_notify_work; wait_queue_head_t ccw_device_init_wq; atomic_t ccw_device_init_count; +static void recovery_func(unsigned long data); + static int __init init_ccw_bus_type (void) { @@ -149,6 +157,7 @@ init_ccw_bus_type (void) init_waitqueue_head(&ccw_device_init_wq); atomic_set(&ccw_device_init_count, 0); + setup_timer(&recovery_timer, recovery_func, 0); ccw_device_work = create_singlethread_workqueue("cio"); if (!ccw_device_work) @@ -1503,6 +1512,60 @@ ccw_device_get_subchannel_id(struct ccw_device *cdev) return sch->schid; } +static int recovery_check(struct device *dev, void *data) +{ + struct ccw_device *cdev = to_ccwdev(dev); + int *redo = data; + + spin_lock_irq(cdev->ccwlock); + switch (cdev->private->state) { + case DEV_STATE_DISCONNECTED: + CIO_MSG_EVENT(3, "recovery: trigger 0.%x.%04x\n", + cdev->private->dev_id.ssid, + cdev->private->dev_id.devno); + dev_fsm_event(cdev, DEV_EVENT_VERIFY); + *redo = 1; + break; + case DEV_STATE_DISCONNECTED_SENSE_ID: + *redo = 1; + break; + } + spin_unlock_irq(cdev->ccwlock); + + return 0; +} + +static void recovery_func(unsigned long data) +{ + int redo = 0; + + bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check); + if (redo) { + spin_lock_irq(&recovery_lock); + if (!timer_pending(&recovery_timer)) { + if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1) + recovery_phase++; + mod_timer(&recovery_timer, jiffies + + recovery_delay[recovery_phase] * HZ); + } + spin_unlock_irq(&recovery_lock); + } else + CIO_MSG_EVENT(2, "recovery: end\n"); +} + +void ccw_device_schedule_recovery(void) +{ + unsigned long flags; + + CIO_MSG_EVENT(2, "recovery: schedule\n"); + spin_lock_irqsave(&recovery_lock, flags); + if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) { + recovery_phase = 0; + mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ); + } + spin_unlock_irqrestore(&recovery_lock, flags); +} + MODULE_LICENSE("GPL"); EXPORT_SYMBOL(ccw_device_set_online); EXPORT_SYMBOL(ccw_device_set_offline); |