]> bbs.cooldavid.org Git - net-next-2.6.git/commitdiff
[S390] dasd: fix online/offline race
authorStefan Haberland <stefan.haberland@de.ibm.com>
Fri, 26 Feb 2010 21:37:46 +0000 (22:37 +0100)
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>
Fri, 26 Feb 2010 21:37:32 +0000 (22:37 +0100)
Setting a DASD online and offline in quick succession may cause
a kernel panic or let the chhccwdev command wait forever.
The Online process is split into two parts. After the first part
is finished the offline process may be called. This may result
in a situation where the second online processing part tries to
set the DASD offline as well.
Use a mutex to protect online and offline against each other.
Also correct some checking.

Signed-off-by: Stefan Haberland <stefan.haberland@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
drivers/s390/block/dasd.c
drivers/s390/block/dasd_genhd.c
drivers/s390/block/dasd_int.h

index 5905936c7c60909a30bd539e301dd5c49d7c5014..56df3c5ed3850839bc2b6a2623a101d84fab21e7 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/buffer_head.h>
 #include <linux/hdreg.h>
 #include <linux/async.h>
+#include <linux/mutex.h>
 
 #include <asm/ccwdev.h>
 #include <asm/ebcdic.h>
@@ -112,6 +113,7 @@ struct dasd_device *dasd_alloc_device(void)
        INIT_WORK(&device->restore_device, do_restore_device);
        device->state = DASD_STATE_NEW;
        device->target = DASD_STATE_NEW;
+       mutex_init(&device->state_mutex);
 
        return device;
 }
@@ -484,10 +486,8 @@ static void dasd_change_state(struct dasd_device *device)
        if (rc)
                device->target = device->state;
 
-       if (device->state == device->target) {
+       if (device->state == device->target)
                wake_up(&dasd_init_waitq);
-               dasd_put_device(device);
-       }
 
        /* let user-space know that the device status changed */
        kobject_uevent(&device->cdev->dev.kobj, KOBJ_CHANGE);
@@ -502,7 +502,9 @@ static void dasd_change_state(struct dasd_device *device)
 static void do_kick_device(struct work_struct *work)
 {
        struct dasd_device *device = container_of(work, struct dasd_device, kick_work);
+       mutex_lock(&device->state_mutex);
        dasd_change_state(device);
+       mutex_unlock(&device->state_mutex);
        dasd_schedule_device_bh(device);
        dasd_put_device(device);
 }
@@ -539,18 +541,19 @@ void dasd_restore_device(struct dasd_device *device)
 void dasd_set_target_state(struct dasd_device *device, int target)
 {
        dasd_get_device(device);
+       mutex_lock(&device->state_mutex);
        /* If we are in probeonly mode stop at DASD_STATE_READY. */
        if (dasd_probeonly && target > DASD_STATE_READY)
                target = DASD_STATE_READY;
        if (device->target != target) {
-               if (device->state == target) {
+               if (device->state == target)
                        wake_up(&dasd_init_waitq);
-                       dasd_put_device(device);
-               }
                device->target = target;
        }
        if (device->state != device->target)
                dasd_change_state(device);
+       mutex_unlock(&device->state_mutex);
+       dasd_put_device(device);
 }
 
 /*
@@ -1692,7 +1695,6 @@ int dasd_cancel_req(struct dasd_ccw_req *cqr)
                                cqr, rc);
                } else {
                        cqr->stopclk = get_clock();
-                       rc = 1;
                }
                break;
        default: /* already finished or clear pending - do nothing */
@@ -2170,9 +2172,13 @@ static void dasd_flush_request_queue(struct dasd_block *block)
 static int dasd_open(struct block_device *bdev, fmode_t mode)
 {
        struct dasd_block *block = bdev->bd_disk->private_data;
-       struct dasd_device *base = block->base;
+       struct dasd_device *base;
        int rc;
 
+       if (!block)
+               return -ENODEV;
+
+       base = block->base;
        atomic_inc(&block->open_count);
        if (test_bit(DASD_FLAG_OFFLINE, &base->flags)) {
                rc = -ENODEV;
index d3198303b93c5b004f0a14712fc39f6ef16ee0c0..94f92a1247f2039b19ae29581b7e7c63e155f418 100644 (file)
@@ -88,6 +88,7 @@ void dasd_gendisk_free(struct dasd_block *block)
        if (block->gdp) {
                del_gendisk(block->gdp);
                block->gdp->queue = NULL;
+               block->gdp->private_data = NULL;
                put_disk(block->gdp);
                block->gdp = NULL;
        }
index e4c2143dabf67835ac50b077a5740f60b6dcbe0e..ed73ce5508228e27f855f99a223d11c455951087 100644 (file)
@@ -368,6 +368,7 @@ struct dasd_device {
 
        /* Device state and target state. */
        int state, target;
+       struct mutex state_mutex;
        int stopped;            /* device (ccw_device_start) was stopped */
 
        /* reference count. */