zram: add interface to specif backing device
authorMinchan Kim <minchan@kernel.org>
Wed, 6 Sep 2017 23:19:54 +0000 (16:19 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 7 Sep 2017 00:27:25 +0000 (17:27 -0700)
For writeback feature, user should set up backing device before the zram
working.

This patch enables the interface via /sys/block/zramX/backing_dev.

Currently, it supports block device only but it could be enhanced for
file as well.

Link: http://lkml.kernel.org/r/1498459987-24562-5-git-send-email-minchan@kernel.org
Signed-off-by: Minchan Kim <minchan@kernel.org>
Cc: Juneho Choi <juno.choi@lge.com>
Cc: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/block/zram/zram_drv.c
drivers/block/zram/zram_drv.h

index 7afe0d8487c0eb33b8ae19affdca4e097ea68754..81eb81f17ef9d227388937c41b4306a2df486c97 100644 (file)
@@ -270,6 +270,141 @@ static ssize_t mem_used_max_store(struct device *dev,
        return len;
 }
 
+#ifdef CONFIG_ZRAM_WRITEBACK
+static bool zram_wb_enabled(struct zram *zram)
+{
+       return zram->backing_dev;
+}
+
+static void reset_bdev(struct zram *zram)
+{
+       struct block_device *bdev;
+
+       if (!zram_wb_enabled(zram))
+               return;
+
+       bdev = zram->bdev;
+       if (zram->old_block_size)
+               set_blocksize(bdev, zram->old_block_size);
+       blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);
+       /* hope filp_close flush all of IO */
+       filp_close(zram->backing_dev, NULL);
+       zram->backing_dev = NULL;
+       zram->old_block_size = 0;
+       zram->bdev = NULL;
+}
+
+static ssize_t backing_dev_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct zram *zram = dev_to_zram(dev);
+       struct file *file = zram->backing_dev;
+       char *p;
+       ssize_t ret;
+
+       down_read(&zram->init_lock);
+       if (!zram_wb_enabled(zram)) {
+               memcpy(buf, "none\n", 5);
+               up_read(&zram->init_lock);
+               return 5;
+       }
+
+       p = file_path(file, buf, PAGE_SIZE - 1);
+       if (IS_ERR(p)) {
+               ret = PTR_ERR(p);
+               goto out;
+       }
+
+       ret = strlen(p);
+       memmove(buf, p, ret);
+       buf[ret++] = '\n';
+out:
+       up_read(&zram->init_lock);
+       return ret;
+}
+
+static ssize_t backing_dev_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t len)
+{
+       char *file_name;
+       struct file *backing_dev = NULL;
+       struct inode *inode;
+       struct address_space *mapping;
+       unsigned int old_block_size = 0;
+       struct block_device *bdev = NULL;
+       int err;
+       struct zram *zram = dev_to_zram(dev);
+
+       file_name = kmalloc(PATH_MAX, GFP_KERNEL);
+       if (!file_name)
+               return -ENOMEM;
+
+       down_write(&zram->init_lock);
+       if (init_done(zram)) {
+               pr_info("Can't setup backing device for initialized device\n");
+               err = -EBUSY;
+               goto out;
+       }
+
+       strlcpy(file_name, buf, len);
+
+       backing_dev = filp_open(file_name, O_RDWR|O_LARGEFILE, 0);
+       if (IS_ERR(backing_dev)) {
+               err = PTR_ERR(backing_dev);
+               backing_dev = NULL;
+               goto out;
+       }
+
+       mapping = backing_dev->f_mapping;
+       inode = mapping->host;
+
+       /* Support only block device in this moment */
+       if (!S_ISBLK(inode->i_mode)) {
+               err = -ENOTBLK;
+               goto out;
+       }
+
+       bdev = bdgrab(I_BDEV(inode));
+       err = blkdev_get(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL, zram);
+       if (err < 0)
+               goto out;
+
+       old_block_size = block_size(bdev);
+       err = set_blocksize(bdev, PAGE_SIZE);
+       if (err)
+               goto out;
+
+       reset_bdev(zram);
+
+       zram->old_block_size = old_block_size;
+       zram->bdev = bdev;
+       zram->backing_dev = backing_dev;
+       up_write(&zram->init_lock);
+
+       pr_info("setup backing device %s\n", file_name);
+       kfree(file_name);
+
+       return len;
+out:
+       if (bdev)
+               blkdev_put(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL);
+
+       if (backing_dev)
+               filp_close(backing_dev, NULL);
+
+       up_write(&zram->init_lock);
+
+       kfree(file_name);
+
+       return err;
+}
+
+#else
+static bool zram_wb_enabled(struct zram *zram) { return false; }
+static inline void reset_bdev(struct zram *zram) {};
+#endif
+
+
 /*
  * We switched to per-cpu streams and this attr is not needed anymore.
  * However, we will keep it around for some time, because:
@@ -953,6 +1088,7 @@ static void zram_reset_device(struct zram *zram)
        zram_meta_free(zram, disksize);
        memset(&zram->stats, 0, sizeof(zram->stats));
        zcomp_destroy(comp);
+       reset_bdev(zram);
 }
 
 static ssize_t disksize_store(struct device *dev,
@@ -1078,6 +1214,9 @@ static DEVICE_ATTR_WO(mem_limit);
 static DEVICE_ATTR_WO(mem_used_max);
 static DEVICE_ATTR_RW(max_comp_streams);
 static DEVICE_ATTR_RW(comp_algorithm);
+#ifdef CONFIG_ZRAM_WRITEBACK
+static DEVICE_ATTR_RW(backing_dev);
+#endif
 
 static struct attribute *zram_disk_attrs[] = {
        &dev_attr_disksize.attr,
@@ -1088,6 +1227,9 @@ static struct attribute *zram_disk_attrs[] = {
        &dev_attr_mem_used_max.attr,
        &dev_attr_max_comp_streams.attr,
        &dev_attr_comp_algorithm.attr,
+#ifdef CONFIG_ZRAM_WRITEBACK
+       &dev_attr_backing_dev.attr,
+#endif
        &dev_attr_io_stat.attr,
        &dev_attr_mm_stat.attr,
        &dev_attr_debug_stat.attr,
index e34e44d02e3ec63d58ee7f9c9b6b3694c74482a0..113a41118918c0e9f0e3dfa9c3e4c55a76e87e82 100644 (file)
@@ -115,5 +115,10 @@ struct zram {
         * zram is claimed so open request will be failed
         */
        bool claim; /* Protected by bdev->bd_mutex */
+#ifdef CONFIG_ZRAM_WRITEBACK
+       struct file *backing_dev;
+       struct block_device *bdev;
+       unsigned int old_block_size;
+#endif
 };
 #endif