staging: zram: fix zram locking
authorJerome Marchand <jmarchan@redhat.com>
Tue, 6 Sep 2011 13:02:11 +0000 (15:02 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 6 Sep 2011 23:56:15 +0000 (16:56 -0700)
Currently init_lock only prevents concurrent execution of zram_init_device()
and zram_reset_device() but not zram_make_request() nor sysfs store functions.

This patch changes init_lock into a rw_semaphore. A write lock is taken by
init, reset and store functions, a read lock is taken by zram_make_request().
Also, avoids to release the lock before calling __zram_reset_device() for
cleaning after a failed init, thus preventing any concurrent task to see an
inconsistent state of zram.

Signed-off-by: Jerome Marchand <jmarchan@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/staging/zram/zram_drv.c
drivers/staging/zram/zram_drv.h
drivers/staging/zram/zram_sysfs.c

index ec66b0a..6fb2be3 100644 (file)
@@ -560,27 +560,34 @@ static int zram_make_request(struct request_queue *queue, struct bio *bio)
 {
        struct zram *zram = queue->queuedata;
 
+       if (unlikely(!zram->init_done) && zram_init_device(zram))
+               goto error;
+
+       down_read(&zram->init_lock);
+       if (unlikely(!zram->init_done))
+               goto error_unlock;
+
        if (!valid_io_request(zram, bio)) {
                zram_stat64_inc(zram, &zram->stats.invalid_io);
-               bio_io_error(bio);
-               return 0;
-       }
-
-       if (unlikely(!zram->init_done) && zram_init_device(zram)) {
-               bio_io_error(bio);
-               return 0;
+               goto error_unlock;
        }
 
        __zram_make_request(zram, bio, bio_data_dir(bio));
+       up_read(&zram->init_lock);
 
        return 0;
+
+error_unlock:
+       up_read(&zram->init_lock);
+error:
+       bio_io_error(bio);
+       return 0;
 }
 
-void zram_reset_device(struct zram *zram)
+void __zram_reset_device(struct zram *zram)
 {
        size_t index;
 
-       mutex_lock(&zram->init_lock);
        zram->init_done = 0;
 
        /* Free various per-device buffers */
@@ -617,7 +624,13 @@ void zram_reset_device(struct zram *zram)
        memset(&zram->stats, 0, sizeof(zram->stats));
 
        zram->disksize = 0;
-       mutex_unlock(&zram->init_lock);
+}
+
+void zram_reset_device(struct zram *zram)
+{
+       down_write(&zram->init_lock);
+       __zram_reset_device(zram);
+       up_write(&zram->init_lock);
 }
 
 int zram_init_device(struct zram *zram)
@@ -625,10 +638,10 @@ int zram_init_device(struct zram *zram)
        int ret;
        size_t num_pages;
 
-       mutex_lock(&zram->init_lock);
+       down_write(&zram->init_lock);
 
        if (zram->init_done) {
-               mutex_unlock(&zram->init_lock);
+               up_write(&zram->init_lock);
                return 0;
        }
 
@@ -671,15 +684,14 @@ int zram_init_device(struct zram *zram)
        }
 
        zram->init_done = 1;
-       mutex_unlock(&zram->init_lock);
+       up_write(&zram->init_lock);
 
        pr_debug("Initialization done!\n");
        return 0;
 
 fail:
-       mutex_unlock(&zram->init_lock);
-       zram_reset_device(zram);
-
+       __zram_reset_device(zram);
+       up_write(&zram->init_lock);
        pr_err("Initialization failed: err=%d\n", ret);
        return ret;
 }
@@ -703,7 +715,7 @@ static int create_device(struct zram *zram, int device_id)
        int ret = 0;
 
        init_rwsem(&zram->lock);
-       mutex_init(&zram->init_lock);
+       init_rwsem(&zram->init_lock);
        spin_lock_init(&zram->stat64_lock);
 
        zram->queue = blk_alloc_queue(GFP_KERNEL);
index 59b01d6..7fdeb7d 100644 (file)
@@ -112,8 +112,8 @@ struct zram {
        struct request_queue *queue;
        struct gendisk *disk;
        int init_done;
-       /* Prevent concurrent execution of device init and reset */
-       struct mutex init_lock;
+       /* Prevent concurrent execution of device init, reset and R/W request */
+       struct rw_semaphore init_lock;
        /*
         * This is the limit on amount of *uncompressed* worth of data
         * we can store in a disk.
@@ -130,6 +130,6 @@ extern struct attribute_group zram_disk_attr_group;
 #endif
 
 extern int zram_init_device(struct zram *zram);
-extern void zram_reset_device(struct zram *zram);
+extern void __zram_reset_device(struct zram *zram);
 
 #endif
index df1f9dc..0ea8ed2 100644 (file)
@@ -55,19 +55,23 @@ static ssize_t disksize_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t len)
 {
        int ret;
+       u64 disksize;
        struct zram *zram = dev_to_zram(dev);
 
+       ret = strict_strtoull(buf, 10, &disksize);
+       if (ret)
+               return ret;
+
+       down_write(&zram->init_lock);
        if (zram->init_done) {
+               up_write(&zram->init_lock);
                pr_info("Cannot change disksize for initialized device\n");
                return -EBUSY;
        }
 
-       ret = strict_strtoull(buf, 10, &zram->disksize);
-       if (ret)
-               return ret;
-
-       zram->disksize = PAGE_ALIGN(zram->disksize);
+       zram->disksize = PAGE_ALIGN(disksize);
        set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT);
+       up_write(&zram->init_lock);
 
        return len;
 }
@@ -106,8 +110,10 @@ static ssize_t reset_store(struct device *dev,
        if (bdev)
                fsync_bdev(bdev);
 
+       down_write(&zram->init_lock);
        if (zram->init_done)
-               zram_reset_device(zram);
+               __zram_reset_device(zram);
+       up_write(&zram->init_lock);
 
        return len;
 }