--- /dev/null
+From: Gerald Schaefer <geraldsc@de.ibm.com>
+Subject: dasd: fix race in dasd timer handling
+References: bnc#482818,LTC#52092
+
+Symptom: DASD driver runs into BUG statement in add_timer
+Problem: If the timer expires just after we check whether it is
+ still pending and before mod_timer is called, then
+ mod_timer will setup the timer, return zero and add_timer
+ will be called as well although the timer is already set.
+Solution: Initialize the timer variables at device creation time
+ and remove all timer_pending checks from dasd functions.
+ del_timer and mod_timer will do all the necessary
+ checking themselves and mod_timer will set a timer if it
+ is not already pending.
+
+Acked-by: John Jolly <jjolly@suse.de>
+---
+ drivers/s390/block/dasd.c | 46 ++++++++++++++++------------------------------
+ 1 file changed, 16 insertions(+), 30 deletions(-)
+
+Index: linux-sles11/drivers/s390/block/dasd.c
+===================================================================
+--- linux-sles11.orig/drivers/s390/block/dasd.c
++++ linux-sles11/drivers/s390/block/dasd.c
+@@ -57,6 +57,8 @@ static void dasd_device_tasklet(struct d
+ static void dasd_block_tasklet(struct dasd_block *);
+ static void do_kick_device(struct work_struct *);
+ static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *);
++static void dasd_device_timeout(unsigned long);
++static void dasd_block_timeout(unsigned long);
+
+ /*
+ * SECTION: Operations on the device structure.
+@@ -99,6 +101,8 @@ struct dasd_device *dasd_alloc_device(vo
+ (unsigned long) device);
+ INIT_LIST_HEAD(&device->ccw_queue);
+ init_timer(&device->timer);
++ device->timer.function = dasd_device_timeout;
++ device->timer.data = (unsigned long) device;
+ INIT_WORK(&device->kick_work, do_kick_device);
+ device->state = DASD_STATE_NEW;
+ device->target = DASD_STATE_NEW;
+@@ -138,6 +142,8 @@ struct dasd_block *dasd_alloc_block(void
+ INIT_LIST_HEAD(&block->ccw_queue);
+ spin_lock_init(&block->queue_lock);
+ init_timer(&block->timer);
++ block->timer.function = dasd_block_timeout;
++ block->timer.data = (unsigned long) block;
+
+ return block;
+ }
+@@ -923,19 +929,10 @@ static void dasd_device_timeout(unsigned
+ */
+ void dasd_device_set_timer(struct dasd_device *device, int expires)
+ {
+- if (expires == 0) {
+- if (timer_pending(&device->timer))
+- del_timer(&device->timer);
+- return;
+- }
+- if (timer_pending(&device->timer)) {
+- if (mod_timer(&device->timer, jiffies + expires))
+- return;
+- }
+- device->timer.function = dasd_device_timeout;
+- device->timer.data = (unsigned long) device;
+- device->timer.expires = jiffies + expires;
+- add_timer(&device->timer);
++ if (expires == 0)
++ del_timer(&device->timer);
++ else
++ mod_timer(&device->timer, jiffies + expires);
+ }
+
+ /*
+@@ -943,8 +940,7 @@ void dasd_device_set_timer(struct dasd_d
+ */
+ void dasd_device_clear_timer(struct dasd_device *device)
+ {
+- if (timer_pending(&device->timer))
+- del_timer(&device->timer);
++ del_timer(&device->timer);
+ }
+
+ static void dasd_handle_killed_request(struct ccw_device *cdev,
+@@ -1594,19 +1590,10 @@ static void dasd_block_timeout(unsigned
+ */
+ void dasd_block_set_timer(struct dasd_block *block, int expires)
+ {
+- if (expires == 0) {
+- if (timer_pending(&block->timer))
+- del_timer(&block->timer);
+- return;
+- }
+- if (timer_pending(&block->timer)) {
+- if (mod_timer(&block->timer, jiffies + expires))
+- return;
+- }
+- block->timer.function = dasd_block_timeout;
+- block->timer.data = (unsigned long) block;
+- block->timer.expires = jiffies + expires;
+- add_timer(&block->timer);
++ if (expires == 0)
++ del_timer(&block->timer);
++ else
++ mod_timer(&block->timer, jiffies + expires);
+ }
+
+ /*
+@@ -1614,8 +1601,7 @@ void dasd_block_set_timer(struct dasd_bl
+ */
+ void dasd_block_clear_timer(struct dasd_block *block)
+ {
+- if (timer_pending(&block->timer))
+- del_timer(&block->timer);
++ del_timer(&block->timer);
+ }
+
+ /*