]> git.karo-electronics.de Git - linux-beck.git/blobdiff - drivers/block/zram/zram_drv.c
zram: fix pool name truncation
[linux-beck.git] / drivers / block / zram / zram_drv.c
index 4f76cf3e26a19fa0b78cb4bede35c1424939cfe2..763301c7828c72650f2abaa1c723425bdd3c73f4 100644 (file)
 #include <linux/vmalloc.h>
 #include <linux/err.h>
 #include <linux/idr.h>
+#include <linux/sysfs.h>
 
 #include "zram_drv.h"
 
 static DEFINE_IDR(zram_index_idr);
+/* idr index must be protected */
+static DEFINE_MUTEX(zram_index_mutex);
+
 static int zram_major;
 static const char *default_compressor = "lzo";
 
@@ -359,6 +363,8 @@ static ssize_t comp_algorithm_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t len)
 {
        struct zram *zram = dev_to_zram(dev);
+       size_t sz;
+
        down_write(&zram->init_lock);
        if (init_done(zram)) {
                up_write(&zram->init_lock);
@@ -366,6 +372,15 @@ static ssize_t comp_algorithm_store(struct device *dev,
                return -EBUSY;
        }
        strlcpy(zram->compressor, buf, sizeof(zram->compressor));
+
+       /* ignore trailing newline */
+       sz = strlen(zram->compressor);
+       if (sz > 0 && zram->compressor[sz - 1] == '\n')
+               zram->compressor[sz - 1] = 0x00;
+
+       if (!zcomp_available_algorithm(zram->compressor))
+               len = -EINVAL;
+
        up_write(&zram->init_lock);
        return len;
 }
@@ -481,10 +496,9 @@ static void zram_meta_free(struct zram_meta *meta, u64 disksize)
        kfree(meta);
 }
 
-static struct zram_meta *zram_meta_alloc(int device_id, u64 disksize)
+static struct zram_meta *zram_meta_alloc(char *pool_name, u64 disksize)
 {
        size_t num_pages;
-       char pool_name[8];
        struct zram_meta *meta = kmalloc(sizeof(*meta), GFP_KERNEL);
 
        if (!meta)
@@ -497,7 +511,6 @@ static struct zram_meta *zram_meta_alloc(int device_id, u64 disksize)
                goto out_error;
        }
 
-       snprintf(pool_name, sizeof(pool_name), "zram%d", device_id);
        meta->mem_pool = zs_create_pool(pool_name, GFP_NOIO | __GFP_HIGHMEM);
        if (!meta->mem_pool) {
                pr_err("Error creating memory pool\n");
@@ -638,8 +651,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
        struct page *page;
        unsigned char *user_mem, *cmem, *src, *uncmem = NULL;
        struct zram_meta *meta = zram->meta;
-       struct zcomp_strm *zstrm;
-       bool locked = false;
+       struct zcomp_strm *zstrm = NULL;
        unsigned long alloced_pages;
 
        page = bvec->bv_page;
@@ -659,7 +671,6 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
        }
 
        zstrm = zcomp_strm_find(zram->comp);
-       locked = true;
        user_mem = kmap_atomic(page);
 
        if (is_partial_io(bvec)) {
@@ -731,7 +742,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
        }
 
        zcomp_strm_release(zram->comp, zstrm);
-       locked = false;
+       zstrm = NULL;
        zs_unmap_object(meta->mem_pool, handle);
 
        /*
@@ -749,7 +760,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
        atomic64_add(clen, &zram->stats.compr_data_size);
        atomic64_inc(&zram->stats.pages_stored);
 out:
-       if (locked)
+       if (zstrm)
                zcomp_strm_release(zram->comp, zstrm);
        if (is_partial_io(bvec))
                kfree(uncmem);
@@ -1018,7 +1029,7 @@ static ssize_t disksize_store(struct device *dev,
                return -EINVAL;
 
        disksize = PAGE_ALIGN(disksize);
-       meta = zram_meta_alloc(zram->disk->first_minor, disksize);
+       meta = zram_meta_alloc(zram->disk->disk_name, disksize);
        if (!meta)
                return -ENOMEM;
 
@@ -1070,45 +1081,60 @@ static ssize_t reset_store(struct device *dev,
        struct zram *zram;
        struct block_device *bdev;
 
+       ret = kstrtou16(buf, 10, &do_reset);
+       if (ret)
+               return ret;
+
+       if (!do_reset)
+               return -EINVAL;
+
        zram = dev_to_zram(dev);
        bdev = bdget_disk(zram->disk, 0);
-
        if (!bdev)
                return -ENOMEM;
 
        mutex_lock(&bdev->bd_mutex);
-       /* Do not reset an active device! */
-       if (bdev->bd_openers) {
-               ret = -EBUSY;
-               goto out;
+       /* Do not reset an active device or claimed device */
+       if (bdev->bd_openers || zram->claim) {
+               mutex_unlock(&bdev->bd_mutex);
+               bdput(bdev);
+               return -EBUSY;
        }
 
-       ret = kstrtou16(buf, 10, &do_reset);
-       if (ret)
-               goto out;
-
-       if (!do_reset) {
-               ret = -EINVAL;
-               goto out;
-       }
+       /* From now on, anyone can't open /dev/zram[0-9] */
+       zram->claim = true;
+       mutex_unlock(&bdev->bd_mutex);
 
-       /* Make sure all pending I/O is finished */
+       /* Make sure all the pending I/O are finished */
        fsync_bdev(bdev);
        zram_reset_device(zram);
-
-       mutex_unlock(&bdev->bd_mutex);
        revalidate_disk(zram->disk);
        bdput(bdev);
 
+       mutex_lock(&bdev->bd_mutex);
+       zram->claim = false;
+       mutex_unlock(&bdev->bd_mutex);
+
        return len;
+}
+
+static int zram_open(struct block_device *bdev, fmode_t mode)
+{
+       int ret = 0;
+       struct zram *zram;
+
+       WARN_ON(!mutex_is_locked(&bdev->bd_mutex));
+
+       zram = bdev->bd_disk->private_data;
+       /* zram was claimed to reset so open request fails */
+       if (zram->claim)
+               ret = -EBUSY;
 
-out:
-       mutex_unlock(&bdev->bd_mutex);
-       bdput(bdev);
        return ret;
 }
 
 static const struct block_device_operations zram_devops = {
+       .open = zram_open,
        .swap_slot_free_notify = zram_slot_free_notify,
        .rw_page = zram_rw_page,
        .owner = THIS_MODULE
@@ -1153,20 +1179,24 @@ static struct attribute_group zram_disk_attr_group = {
        .attrs = zram_disk_attrs,
 };
 
-static int zram_add(int device_id)
+/*
+ * Allocate and initialize new zram device. the function returns
+ * '>= 0' device_id upon success, and negative value otherwise.
+ */
+static int zram_add(void)
 {
        struct zram *zram;
        struct request_queue *queue;
-       int ret;
+       int ret, device_id;
 
        zram = kzalloc(sizeof(struct zram), GFP_KERNEL);
        if (!zram)
                return -ENOMEM;
 
-       ret = idr_alloc(&zram_index_idr, zram, device_id,
-                       device_id + 1, GFP_KERNEL);
+       ret = idr_alloc(&zram_index_idr, zram, 0, 0, GFP_KERNEL);
        if (ret < 0)
                goto out_free_dev;
+       device_id = ret;
 
        init_rwsem(&zram->init_lock);
 
@@ -1240,7 +1270,7 @@ static int zram_add(int device_id)
        zram->max_comp_streams = 1;
 
        pr_info("Added device: %s\n", zram->disk->disk_name);
-       return 0;
+       return device_id;
 
 out_free_disk:
        del_gendisk(zram->disk);
@@ -1254,24 +1284,104 @@ out_free_dev:
        return ret;
 }
 
-static void zram_remove(struct zram *zram)
+static int zram_remove(struct zram *zram)
 {
-       pr_info("Removed device: %s\n", zram->disk->disk_name);
+       struct block_device *bdev;
+
+       bdev = bdget_disk(zram->disk, 0);
+       if (!bdev)
+               return -ENOMEM;
+
+       mutex_lock(&bdev->bd_mutex);
+       if (bdev->bd_openers || zram->claim) {
+               mutex_unlock(&bdev->bd_mutex);
+               bdput(bdev);
+               return -EBUSY;
+       }
+
+       zram->claim = true;
+       mutex_unlock(&bdev->bd_mutex);
+
        /*
         * Remove sysfs first, so no one will perform a disksize
-        * store while we destroy the devices
+        * store while we destroy the devices. This also helps during
+        * hot_remove -- zram_reset_device() is the last holder of
+        * ->init_lock, no later/concurrent disksize_store() or any
+        * other sysfs handlers are possible.
         */
        sysfs_remove_group(&disk_to_dev(zram->disk)->kobj,
                        &zram_disk_attr_group);
 
+       /* Make sure all the pending I/O are finished */
+       fsync_bdev(bdev);
        zram_reset_device(zram);
+       bdput(bdev);
+
+       pr_info("Removed device: %s\n", zram->disk->disk_name);
+
        idr_remove(&zram_index_idr, zram->disk->first_minor);
        blk_cleanup_queue(zram->disk->queue);
        del_gendisk(zram->disk);
        put_disk(zram->disk);
        kfree(zram);
+       return 0;
+}
+
+/* zram-control sysfs attributes */
+static ssize_t hot_add_show(struct class *class,
+                       struct class_attribute *attr,
+                       char *buf)
+{
+       int ret;
+
+       mutex_lock(&zram_index_mutex);
+       ret = zram_add();
+       mutex_unlock(&zram_index_mutex);
+
+       if (ret < 0)
+               return ret;
+       return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
 }
 
+static ssize_t hot_remove_store(struct class *class,
+                       struct class_attribute *attr,
+                       const char *buf,
+                       size_t count)
+{
+       struct zram *zram;
+       int ret, dev_id;
+
+       /* dev_id is gendisk->first_minor, which is `int' */
+       ret = kstrtoint(buf, 10, &dev_id);
+       if (ret)
+               return ret;
+       if (dev_id < 0)
+               return -EINVAL;
+
+       mutex_lock(&zram_index_mutex);
+
+       zram = idr_find(&zram_index_idr, dev_id);
+       if (zram)
+               ret = zram_remove(zram);
+       else
+               ret = -ENODEV;
+
+       mutex_unlock(&zram_index_mutex);
+       return ret ? ret : count;
+}
+
+static struct class_attribute zram_control_class_attrs[] = {
+       __ATTR_RO(hot_add),
+       __ATTR_WO(hot_remove),
+       __ATTR_NULL,
+};
+
+static struct class zram_control_class = {
+       .name           = "zram-control",
+       .owner          = THIS_MODULE,
+       .class_attrs    = zram_control_class_attrs,
+};
+
 static int zram_remove_cb(int id, void *ptr, void *data)
 {
        zram_remove(ptr);
@@ -1280,6 +1390,7 @@ static int zram_remove_cb(int id, void *ptr, void *data)
 
 static void destroy_devices(void)
 {
+       class_unregister(&zram_control_class);
        idr_for_each(&zram_index_idr, &zram_remove_cb, NULL);
        idr_destroy(&zram_index_idr);
        unregister_blkdev(zram_major, "zram");
@@ -1287,18 +1398,28 @@ static void destroy_devices(void)
 
 static int __init zram_init(void)
 {
-       int ret, dev_id;
+       int ret;
+
+       ret = class_register(&zram_control_class);
+       if (ret) {
+               pr_warn("Unable to register zram-control class\n");
+               return ret;
+       }
 
        zram_major = register_blkdev(0, "zram");
        if (zram_major <= 0) {
                pr_warn("Unable to get major number\n");
+               class_unregister(&zram_control_class);
                return -EBUSY;
        }
 
-       for (dev_id = 0; dev_id < num_devices; dev_id++) {
-               ret = zram_add(dev_id);
-               if (ret != 0)
+       while (num_devices != 0) {
+               mutex_lock(&zram_index_mutex);
+               ret = zram_add();
+               mutex_unlock(&zram_index_mutex);
+               if (ret < 0)
                        goto out_error;
+               num_devices--;
        }
 
        return 0;