aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/s390/cio/device.c63
-rw-r--r--drivers/s390/cio/device.h2
-rw-r--r--drivers/s390/cio/device_fsm.c2
3 files changed, 67 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
34static struct timer_list recovery_timer;
35static spinlock_t recovery_lock;
36static int recovery_phase;
37static 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;
142wait_queue_head_t ccw_device_init_wq; 148wait_queue_head_t ccw_device_init_wq;
143atomic_t ccw_device_init_count; 149atomic_t ccw_device_init_count;
144 150
151static void recovery_func(unsigned long data);
152
145static int __init 153static int __init
146init_ccw_bus_type (void) 154init_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
1515static 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
1538static 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
1556void 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
1506MODULE_LICENSE("GPL"); 1569MODULE_LICENSE("GPL");
1507EXPORT_SYMBOL(ccw_device_set_online); 1570EXPORT_SYMBOL(ccw_device_set_online);
1508EXPORT_SYMBOL(ccw_device_set_offline); 1571EXPORT_SYMBOL(ccw_device_set_offline);
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index 87576fe2c7c4..d40a2ffaa000 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -88,6 +88,8 @@ int ccw_device_recognition(struct ccw_device *);
88int ccw_device_online(struct ccw_device *); 88int ccw_device_online(struct ccw_device *);
89int ccw_device_offline(struct ccw_device *); 89int ccw_device_offline(struct ccw_device *);
90 90
91void ccw_device_schedule_recovery(void);
92
91/* Function prototypes for device status and basic sense stuff. */ 93/* Function prototypes for device status and basic sense stuff. */
92void ccw_device_accumulate_irb(struct ccw_device *, struct irb *); 94void ccw_device_accumulate_irb(struct ccw_device *, struct irb *);
93void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *); 95void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *);
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 485741a12125..4b92c84fb438 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -61,6 +61,8 @@ device_set_disconnected(struct subchannel *sch)
61 ccw_device_set_timeout(cdev, 0); 61 ccw_device_set_timeout(cdev, 0);
62 cdev->private->flags.fake_irb = 0; 62 cdev->private->flags.fake_irb = 0;
63 cdev->private->state = DEV_STATE_DISCONNECTED; 63 cdev->private->state = DEV_STATE_DISCONNECTED;
64 if (cdev->online)
65 ccw_device_schedule_recovery();
64} 66}
65 67
66void device_set_intretry(struct subchannel *sch) 68void device_set_intretry(struct subchannel *sch)