[S390] cio: introduce timed recovery procedure
authorPeter Oberparleiter <peter.oberparleiter@de.ibm.com>
Sat, 26 Jan 2008 13:10:52 +0000 (14:10 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Sat, 26 Jan 2008 13:11:06 +0000 (14:11 +0100)
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>
drivers/s390/cio/device.c
drivers/s390/cio/device.h
drivers/s390/cio/device_fsm.c

index f80d7f5418d3e4dc62f07eb721b9952763334fca..d35dc3f25d06ec3a392a35da8617ca85d4da70f7 100644 (file)
@@ -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>
 #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);
index 87576fe2c7c4acd4aa061e97ecf6d557bbe94ec2..d40a2ffaa0006234b57dbed8b69105de6fc1afe2 100644 (file)
@@ -88,6 +88,8 @@ int ccw_device_recognition(struct ccw_device *);
 int ccw_device_online(struct ccw_device *);
 int ccw_device_offline(struct ccw_device *);
 
+void ccw_device_schedule_recovery(void);
+
 /* Function prototypes for device status and basic sense stuff. */
 void ccw_device_accumulate_irb(struct ccw_device *, struct irb *);
 void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *);
index 485741a12125ceab9d86c6332d610ae07f4c1265..4b92c84fb438440874eeb9ea4cb42b5f72965739 100644 (file)
@@ -61,6 +61,8 @@ device_set_disconnected(struct subchannel *sch)
        ccw_device_set_timeout(cdev, 0);
        cdev->private->flags.fake_irb = 0;
        cdev->private->state = DEV_STATE_DISCONNECTED;
+       if (cdev->online)
+               ccw_device_schedule_recovery();
 }
 
 void device_set_intretry(struct subchannel *sch)