From: Hans-Joachim Picht <hpicht@redhat.com> Date: Fri, 13 Mar 2009 17:54:03 +0100 Subject: [s390] dasd: fix race in dasd timer handling Message-id: 20090313165403.GA32287@redhat.com O-Subject: [RHEL5 U4 PATCH 1/1] s390 - dasd: fix race in dasd timer handling Bugzilla: 490128 RH-Acked-by: Pete Zaitcev <zaitcev@redhat.com> Description ============ 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. Bugzilla ========= BZ 490128 https://bugzilla.redhat.com/show_bug.cgi?id=490128 Upstream status of the patch: ============================= The patch is upstream. http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=48cae885d5a896030588978f503c73c5ed5e62b1 Test status: ============ The patch has been tested and fixes the problem. The fix has been verified by the IBM test department. Please ACK. With best regards, --Hans diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 6414a4c..17c7224 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -55,6 +55,7 @@ static void dasd_int_handler(struct ccw_device *, unsigned long, struct irb *); static int dasd_flush_ccw_queue(struct dasd_device *, int); static void dasd_tasklet(struct dasd_device *); static void do_kick_device(void *data); +static void dasd_timeout_device(unsigned long); /* * SECTION: Operations on the device structure. @@ -101,6 +102,8 @@ dasd_alloc_device(void) (unsigned long) device); INIT_LIST_HEAD(&device->ccw_queue); init_timer(&device->timer); + device->timer.function = dasd_timeout_device; + device->timer.data = (unsigned long) device; INIT_WORK(&device->kick_work, do_kick_device, device); device->state = DASD_STATE_NEW; device->target = DASD_STATE_NEW; @@ -855,19 +858,10 @@ dasd_timeout_device(unsigned long ptr) void dasd_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_timeout_device; - 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); } /* @@ -876,8 +870,7 @@ dasd_set_timer(struct dasd_device *device, int expires) void dasd_clear_timer(struct dasd_device *device) { - if (timer_pending(&device->timer)) - del_timer(&device->timer); + del_timer(&device->timer); } static void