block: zram: Backport from Linux 4.1.52
authorSultan Qasim Khan <sultanqasim@gmail.com>
Sun, 6 Sep 2015 21:59:58 +0000 (17:59 -0400)
committerDanny Wood <danwood76@gmail.com>
Fri, 27 Aug 2021 13:38:37 +0000 (14:38 +0100)
Change-Id: I23f6f75979077992298d848efd79a6efc0d776bd

27 files changed:
Documentation/ABI/testing/sysfs-block-zram
Documentation/blockdev/zram.txt [new file with mode: 0644]
drivers/block/Kconfig
drivers/block/Makefile
drivers/block/zram/Kconfig [new file with mode: 0644]
drivers/block/zram/Makefile [new file with mode: 0644]
drivers/block/zram/zcomp.c [new file with mode: 0644]
drivers/block/zram/zcomp.h [new file with mode: 0644]
drivers/block/zram/zcomp_lz4.c [new file with mode: 0644]
drivers/block/zram/zcomp_lz4.h [new file with mode: 0644]
drivers/block/zram/zcomp_lzo.c [new file with mode: 0644]
drivers/block/zram/zcomp_lzo.h [new file with mode: 0644]
drivers/block/zram/zram_drv.c [new file with mode: 0644]
drivers/block/zram/zram_drv.h [new file with mode: 0644]
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/zram/Kconfig [deleted file]
drivers/staging/zram/Makefile [deleted file]
drivers/staging/zram/zram.txt [deleted file]
drivers/staging/zram/zram_drv.c [deleted file]
drivers/staging/zram/zram_drv.h [deleted file]
fs/bio.c
include/linux/bio.h
include/linux/device.h
include/linux/sysfs.h
mm/backing-dev.c
mm/zsmalloc.c

index ec93fe33baa6bc853528dc7179adf1eec852078c..2e69e83bf51057501662e606fb6791338e3838cd 100644 (file)
@@ -5,20 +5,21 @@ Description:
                The disksize file is read-write and specifies the disk size
                which represents the limit on the *uncompressed* worth of data
                that can be stored in this disk.
+               Unit: bytes
 
 What:          /sys/block/zram<id>/initstate
 Date:          August 2010
 Contact:       Nitin Gupta <ngupta@vflare.org>
 Description:
-               The disksize file is read-only and shows the initialization
+               The initstate file is read-only and shows the initialization
                state of the device.
 
 What:          /sys/block/zram<id>/reset
 Date:          August 2010
 Contact:       Nitin Gupta <ngupta@vflare.org>
 Description:
-               The disksize file is write-only and allows resetting the
-               device. The reset operation frees all the memory assocaited
+               The reset file is write-only and allows resetting the
+               device. The reset operation frees all the memory associated
                with this device.
 
 What:          /sys/block/zram<id>/num_reads
@@ -42,24 +43,48 @@ Description:
                The invalid_io file is read-only and specifies the number of
                non-page-size-aligned I/O requests issued to this device.
 
-What:          /sys/block/zram<id>/notify_free
-Date:          August 2010
-Contact:       Nitin Gupta <ngupta@vflare.org>
+What:          /sys/block/zram<id>/failed_reads
+Date:          February 2014
+Contact:       Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
+Description:
+               The failed_reads file is read-only and specifies the number of
+               failed reads happened on this device.
+
+What:          /sys/block/zram<id>/failed_writes
+Date:          February 2014
+Contact:       Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
 Description:
-               The notify_free file is read-only and specifies the number of
-               swap slot free notifications received by this device. These
-               notifications are send to a swap block device when a swap slot
-               is freed. This statistic is applicable only when this disk is
-               being used as a swap disk.
+               The failed_writes file is read-only and specifies the number of
+               failed writes happened on this device.
 
-What:          /sys/block/zram<id>/discard
+What:          /sys/block/zram<id>/max_comp_streams
+Date:          February 2014
+Contact:       Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
+Description:
+               The max_comp_streams file is read-write and specifies the
+               number of backend's zcomp_strm compression streams (number of
+               concurrent compress operations).
+
+What:          /sys/block/zram<id>/comp_algorithm
+Date:          February 2014
+Contact:       Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
+Description:
+               The comp_algorithm file is read-write and lets to show
+               available and selected compression algorithms, change
+               compression algorithm selection.
+
+What:          /sys/block/zram<id>/notify_free
 Date:          August 2010
 Contact:       Nitin Gupta <ngupta@vflare.org>
 Description:
-               The discard file is read-only and specifies the number of
-               discard requests received by this device. These requests
-               provide information to block device regarding blocks which are
-               no longer used by filesystem.
+               The notify_free file is read-only. Depending on device usage
+               scenario it may account a) the number of pages freed because
+               of swap slot free notifications or b) the number of pages freed
+               because of REQ_DISCARD requests sent by bio. The former ones
+               are sent to a swap block device when a swap slot is freed, which
+               implies that this disk is being used as a swap disk. The latter
+               ones are sent by filesystem mounted with discard option,
+               whenever some data blocks are getting discarded.
 
 What:          /sys/block/zram<id>/zero_pages
 Date:          August 2010
@@ -97,3 +122,47 @@ Description:
                efficiency can be calculated using compr_data_size and this
                statistic.
                Unit: bytes
+
+What:          /sys/block/zram<id>/mem_used_max
+Date:          August 2014
+Contact:       Minchan Kim <minchan@kernel.org>
+Description:
+               The mem_used_max file is read/write and specifies the amount
+               of maximum memory zram have consumed to store compressed data.
+               For resetting the value, you should write "0". Otherwise,
+               you could see -EINVAL.
+               Unit: bytes
+
+What:          /sys/block/zram<id>/mem_limit
+Date:          August 2014
+Contact:       Minchan Kim <minchan@kernel.org>
+Description:
+               The mem_limit file is read/write and specifies the maximum
+               amount of memory ZRAM can use to store the compressed data.  The
+               limit could be changed in run time and "0" means disable the
+               limit.  No limit is the initial state.  Unit: bytes
+
+What:          /sys/block/zram<id>/compact
+Date:          August 2015
+Contact:       Minchan Kim <minchan@kernel.org>
+Description:
+               The compact file is write-only and trigger compaction for
+               allocator zrm uses. The allocator moves some objects so that
+               it could free fragment space.
+
+What:          /sys/block/zram<id>/io_stat
+Date:          August 2015
+Contact:       Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
+Description:
+               The io_stat file is read-only and accumulates device's I/O
+               statistics not accounted by block layer. For example,
+               failed_reads, failed_writes, etc. File format is similar to
+               block layer statistics file format.
+
+What:          /sys/block/zram<id>/mm_stat
+Date:          August 2015
+Contact:       Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
+Description:
+               The mm_stat file is read-only and represents device's mm
+               statistics (orig_data_size, compr_data_size, etc.) in a format
+               similar to block layer statistics file format.
diff --git a/Documentation/blockdev/zram.txt b/Documentation/blockdev/zram.txt
new file mode 100644 (file)
index 0000000..48a183e
--- /dev/null
@@ -0,0 +1,189 @@
+zram: Compressed RAM based block devices
+----------------------------------------
+
+* Introduction
+
+The zram module creates RAM based block devices named /dev/zram<id>
+(<id> = 0, 1, ...). Pages written to these disks are compressed and stored
+in memory itself. These disks allow very fast I/O and compression provides
+good amounts of memory savings. Some of the usecases include /tmp storage,
+use as swap disks, various caches under /var and maybe many more :)
+
+Statistics for individual zram devices are exported through sysfs nodes at
+/sys/block/zram<id>/
+
+* Usage
+
+Following shows a typical sequence of steps for using zram.
+
+1) Load Module:
+       modprobe zram num_devices=4
+       This creates 4 devices: /dev/zram{0,1,2,3}
+       (num_devices parameter is optional. Default: 1)
+
+2) Set max number of compression streams
+       Compression backend may use up to max_comp_streams compression streams,
+       thus allowing up to max_comp_streams concurrent compression operations.
+       By default, compression backend uses single compression stream.
+
+       Examples:
+       #show max compression streams number
+       cat /sys/block/zram0/max_comp_streams
+
+       #set max compression streams number to 3
+       echo 3 > /sys/block/zram0/max_comp_streams
+
+Note:
+In order to enable compression backend's multi stream support max_comp_streams
+must be initially set to desired concurrency level before ZRAM device
+initialisation. Once the device initialised as a single stream compression
+backend (max_comp_streams equals to 1), you will see error if you try to change
+the value of max_comp_streams because single stream compression backend
+implemented as a special case by lock overhead issue and does not support
+dynamic max_comp_streams. Only multi stream backend supports dynamic
+max_comp_streams adjustment.
+
+3) Select compression algorithm
+       Using comp_algorithm device attribute one can see available and
+       currently selected (shown in square brackets) compression algortithms,
+       change selected compression algorithm (once the device is initialised
+       there is no way to change compression algorithm).
+
+       Examples:
+       #show supported compression algorithms
+       cat /sys/block/zram0/comp_algorithm
+       lzo [lz4]
+
+       #select lzo compression algorithm
+       echo lzo > /sys/block/zram0/comp_algorithm
+
+4) Set Disksize
+        Set disk size by writing the value to sysfs node 'disksize'.
+        The value can be either in bytes or you can use mem suffixes.
+        Examples:
+            # Initialize /dev/zram0 with 50MB disksize
+            echo $((50*1024*1024)) > /sys/block/zram0/disksize
+
+            # Using mem suffixes
+            echo 256K > /sys/block/zram0/disksize
+            echo 512M > /sys/block/zram0/disksize
+            echo 1G > /sys/block/zram0/disksize
+
+Note:
+There is little point creating a zram of greater than twice the size of memory
+since we expect a 2:1 compression ratio. Note that zram uses about 0.1% of the
+size of the disk when not in use so a huge zram is wasteful.
+
+5) Set memory limit: Optional
+       Set memory limit by writing the value to sysfs node 'mem_limit'.
+       The value can be either in bytes or you can use mem suffixes.
+       In addition, you could change the value in runtime.
+       Examples:
+           # limit /dev/zram0 with 50MB memory
+           echo $((50*1024*1024)) > /sys/block/zram0/mem_limit
+
+           # Using mem suffixes
+           echo 256K > /sys/block/zram0/mem_limit
+           echo 512M > /sys/block/zram0/mem_limit
+           echo 1G > /sys/block/zram0/mem_limit
+
+           # To disable memory limit
+           echo 0 > /sys/block/zram0/mem_limit
+
+6) Activate:
+       mkswap /dev/zram0
+       swapon /dev/zram0
+
+       mkfs.ext4 /dev/zram1
+       mount /dev/zram1 /tmp
+
+7) Stats:
+Per-device statistics are exported as various nodes under /sys/block/zram<id>/
+
+A brief description of exported device attritbutes. For more details please
+read Documentation/ABI/testing/sysfs-block-zram.
+
+Name            access            description
+----            ------            -----------
+disksize          RW    show and set the device's disk size
+initstate         RO    shows the initialization state of the device
+reset             WO    trigger device reset
+num_reads         RO    the number of reads
+failed_reads      RO    the number of failed reads
+num_write         RO    the number of writes
+failed_writes     RO    the number of failed writes
+invalid_io        RO    the number of non-page-size-aligned I/O requests
+max_comp_streams  RW    the number of possible concurrent compress operations
+comp_algorithm    RW    show and change the compression algorithm
+notify_free       RO    the number of notifications to free pages (either
+                        slot free notifications or REQ_DISCARD requests)
+zero_pages        RO    the number of zero filled pages written to this disk
+orig_data_size    RO    uncompressed size of data stored in this disk
+compr_data_size   RO    compressed size of data stored in this disk
+mem_used_total    RO    the amount of memory allocated for this disk
+mem_used_max      RW    the maximum amount memory zram have consumed to
+                        store compressed data
+mem_limit         RW    the maximum amount of memory ZRAM can use to store
+                        the compressed data
+num_migrated      RO    the number of objects migrated migrated by compaction
+
+
+WARNING
+=======
+per-stat sysfs attributes are considered to be deprecated.
+The basic strategy is:
+-- the existing RW nodes will be downgraded to WO nodes (in linux 4.11)
+-- deprecated RO sysfs nodes will eventually be removed (in linux 4.11)
+
+The list of deprecated attributes can be found here:
+Documentation/ABI/obsolete/sysfs-block-zram
+
+Basically, every attribute that has its own read accessible sysfs node
+(e.g. num_reads) *AND* is accessible via one of the stat files (zram<id>/stat
+or zram<id>/io_stat or zram<id>/mm_stat) is considered to be deprecated.
+
+User space is advised to use the following files to read the device statistics.
+
+File /sys/block/zram<id>/stat
+
+Represents block layer statistics. Read Documentation/block/stat.txt for
+details.
+
+File /sys/block/zram<id>/io_stat
+
+The stat file represents device's I/O statistics not accounted by block
+layer and, thus, not available in zram<id>/stat file. It consists of a
+single line of text and contains the following stats separated by
+whitespace:
+       failed_reads
+       failed_writes
+       invalid_io
+       notify_free
+
+File /sys/block/zram<id>/mm_stat
+
+The stat file represents device's mm statistics. It consists of a single
+line of text and contains the following stats separated by whitespace:
+       orig_data_size
+       compr_data_size
+       mem_used_total
+       mem_limit
+       mem_used_max
+       zero_pages
+       num_migrated
+
+8) Deactivate:
+       swapoff /dev/zram0
+       umount /dev/zram1
+
+9) Reset:
+       Write any positive value to 'reset' sysfs node
+       echo 1 > /sys/block/zram0/reset
+       echo 1 > /sys/block/zram1/reset
+
+       This frees all the memory allocated for the given device and
+       resets the disksize to zero. You must set the disksize again
+       before reusing the device.
+
+Nitin Gupta
+ngupta@vflare.org
index b81ddfea1da075ad3c1d0cbeb9ab7bf854593436..9da952c9af9131169184deeb000df1d934548a43 100644 (file)
@@ -105,6 +105,8 @@ source "drivers/block/paride/Kconfig"
 
 source "drivers/block/mtip32xx/Kconfig"
 
+source "drivers/block/zram/Kconfig"
+
 config BLK_CPQ_DA
        tristate "Compaq SMART2 support"
        depends on PCI && VIRT_TO_BUS
index ca07399a8d99efb2d11b80bc266c52c496c902e3..3675937ab651afc8df72448cc56ee8bfe15d7b96 100644 (file)
@@ -41,6 +41,7 @@ obj-$(CONFIG_BLK_DEV_RBD)     += rbd.o
 obj-$(CONFIG_BLK_DEV_PCIESSD_MTIP32XX) += mtip32xx/
 
 obj-$(CONFIG_BLK_DEV_RSXX) += rsxx/
+obj-$(CONFIG_ZRAM) += zram/
 
 nvme-y         := nvme-core.o nvme-scsi.o
 swim_mod-y     := swim.o swim_asm.o
diff --git a/drivers/block/zram/Kconfig b/drivers/block/zram/Kconfig
new file mode 100644 (file)
index 0000000..6489c0f
--- /dev/null
@@ -0,0 +1,34 @@
+config ZRAM
+       tristate "Compressed RAM block device support"
+       depends on BLOCK && SYSFS && ZSMALLOC
+       select LZO_COMPRESS
+       select LZO_DECOMPRESS
+       default n
+       help
+         Creates virtual block devices called /dev/zramX (X = 0, 1, ...).
+         Pages written to these disks are compressed and stored in memory
+         itself. These disks allow very fast I/O and compression provides
+         good amounts of memory savings.
+
+         It has several use cases, for example: /tmp storage, use as swap
+         disks and maybe many more.
+
+         See zram.txt for more information.
+
+config ZRAM_LZ4_COMPRESS
+       bool "Enable LZ4 algorithm support"
+       depends on ZRAM
+       select LZ4_COMPRESS
+       select LZ4_DECOMPRESS
+       default n
+       help
+         This option enables LZ4 compression algorithm support. Compression
+         algorithm can be changed using `comp_algorithm' device attribute.
+
+config ZRAM_DEBUG
+       bool "Compressed RAM block device debug support"
+       depends on ZRAM
+       default n
+       help
+         This option adds additional debugging code to the compressed
+         RAM block device driver.
diff --git a/drivers/block/zram/Makefile b/drivers/block/zram/Makefile
new file mode 100644 (file)
index 0000000..be0763f
--- /dev/null
@@ -0,0 +1,5 @@
+zram-y :=      zcomp_lzo.o zcomp.o zram_drv.o
+
+zram-$(CONFIG_ZRAM_LZ4_COMPRESS) += zcomp_lz4.o
+
+obj-$(CONFIG_ZRAM)     +=      zram.o
diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c
new file mode 100644 (file)
index 0000000..6fbb10c
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2014 Sergey Senozhatsky.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+
+#include "zcomp.h"
+#include "zcomp_lzo.h"
+#ifdef CONFIG_ZRAM_LZ4_COMPRESS
+#include "zcomp_lz4.h"
+#endif
+
+/*
+ * single zcomp_strm backend
+ */
+struct zcomp_strm_single {
+       struct mutex strm_lock;
+       struct zcomp_strm *zstrm;
+};
+
+/*
+ * multi zcomp_strm backend
+ */
+struct zcomp_strm_multi {
+       /* protect strm list */
+       spinlock_t strm_lock;
+       /* max possible number of zstrm streams */
+       int max_strm;
+       /* number of available zstrm streams */
+       int avail_strm;
+       /* list of available strms */
+       struct list_head idle_strm;
+       wait_queue_head_t strm_wait;
+};
+
+static struct zcomp_backend *backends[] = {
+       &zcomp_lzo,
+#ifdef CONFIG_ZRAM_LZ4_COMPRESS
+       &zcomp_lz4,
+#endif
+       NULL
+};
+
+static struct zcomp_backend *find_backend(const char *compress)
+{
+       int i = 0;
+       while (backends[i]) {
+               if (sysfs_streq(compress, backends[i]->name))
+                       break;
+               i++;
+       }
+       return backends[i];
+}
+
+static void zcomp_strm_free(struct zcomp *comp, struct zcomp_strm *zstrm)
+{
+       if (zstrm->private)
+               comp->backend->destroy(zstrm->private);
+       free_pages((unsigned long)zstrm->buffer, 1);
+       kfree(zstrm);
+}
+
+/*
+ * allocate new zcomp_strm structure with ->private initialized by
+ * backend, return NULL on error
+ */
+static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp)
+{
+       struct zcomp_strm *zstrm = kmalloc(sizeof(*zstrm), GFP_NOIO);
+       if (!zstrm)
+               return NULL;
+
+       zstrm->private = comp->backend->create();
+       /*
+        * allocate 2 pages. 1 for compressed data, plus 1 extra for the
+        * case when compressed size is larger than the original one
+        */
+       zstrm->buffer = (void *)__get_free_pages(GFP_NOIO | __GFP_ZERO, 1);
+       if (!zstrm->private || !zstrm->buffer) {
+               zcomp_strm_free(comp, zstrm);
+               zstrm = NULL;
+       }
+       return zstrm;
+}
+
+/*
+ * get idle zcomp_strm or wait until other process release
+ * (zcomp_strm_release()) one for us
+ */
+static struct zcomp_strm *zcomp_strm_multi_find(struct zcomp *comp)
+{
+       struct zcomp_strm_multi *zs = comp->stream;
+       struct zcomp_strm *zstrm;
+
+       while (1) {
+               spin_lock(&zs->strm_lock);
+               if (!list_empty(&zs->idle_strm)) {
+                       zstrm = list_entry(zs->idle_strm.next,
+                                       struct zcomp_strm, list);
+                       list_del(&zstrm->list);
+                       spin_unlock(&zs->strm_lock);
+                       return zstrm;
+               }
+               /* zstrm streams limit reached, wait for idle stream */
+               if (zs->avail_strm >= zs->max_strm) {
+                       spin_unlock(&zs->strm_lock);
+                       wait_event(zs->strm_wait, !list_empty(&zs->idle_strm));
+                       continue;
+               }
+               /* allocate new zstrm stream */
+               zs->avail_strm++;
+               spin_unlock(&zs->strm_lock);
+
+               zstrm = zcomp_strm_alloc(comp);
+               if (!zstrm) {
+                       spin_lock(&zs->strm_lock);
+                       zs->avail_strm--;
+                       spin_unlock(&zs->strm_lock);
+                       wait_event(zs->strm_wait, !list_empty(&zs->idle_strm));
+                       continue;
+               }
+               break;
+       }
+       return zstrm;
+}
+
+/* add stream back to idle list and wake up waiter or free the stream */
+static void zcomp_strm_multi_release(struct zcomp *comp, struct zcomp_strm *zstrm)
+{
+       struct zcomp_strm_multi *zs = comp->stream;
+
+       spin_lock(&zs->strm_lock);
+       if (zs->avail_strm <= zs->max_strm) {
+               list_add(&zstrm->list, &zs->idle_strm);
+               spin_unlock(&zs->strm_lock);
+               wake_up(&zs->strm_wait);
+               return;
+       }
+
+       zs->avail_strm--;
+       spin_unlock(&zs->strm_lock);
+       zcomp_strm_free(comp, zstrm);
+}
+
+/* change max_strm limit */
+static bool zcomp_strm_multi_set_max_streams(struct zcomp *comp, int num_strm)
+{
+       struct zcomp_strm_multi *zs = comp->stream;
+       struct zcomp_strm *zstrm;
+
+       spin_lock(&zs->strm_lock);
+       zs->max_strm = num_strm;
+       /*
+        * if user has lowered the limit and there are idle streams,
+        * immediately free as much streams (and memory) as we can.
+        */
+       while (zs->avail_strm > num_strm && !list_empty(&zs->idle_strm)) {
+               zstrm = list_entry(zs->idle_strm.next,
+                               struct zcomp_strm, list);
+               list_del(&zstrm->list);
+               zcomp_strm_free(comp, zstrm);
+               zs->avail_strm--;
+       }
+       spin_unlock(&zs->strm_lock);
+       return true;
+}
+
+static void zcomp_strm_multi_destroy(struct zcomp *comp)
+{
+       struct zcomp_strm_multi *zs = comp->stream;
+       struct zcomp_strm *zstrm;
+
+       while (!list_empty(&zs->idle_strm)) {
+               zstrm = list_entry(zs->idle_strm.next,
+                               struct zcomp_strm, list);
+               list_del(&zstrm->list);
+               zcomp_strm_free(comp, zstrm);
+       }
+       kfree(zs);
+}
+
+static int zcomp_strm_multi_create(struct zcomp *comp, int max_strm)
+{
+       struct zcomp_strm *zstrm;
+       struct zcomp_strm_multi *zs;
+
+       comp->destroy = zcomp_strm_multi_destroy;
+       comp->strm_find = zcomp_strm_multi_find;
+       comp->strm_release = zcomp_strm_multi_release;
+       comp->set_max_streams = zcomp_strm_multi_set_max_streams;
+       zs = kmalloc(sizeof(struct zcomp_strm_multi), GFP_KERNEL);
+       if (!zs)
+               return -ENOMEM;
+
+       comp->stream = zs;
+       spin_lock_init(&zs->strm_lock);
+       INIT_LIST_HEAD(&zs->idle_strm);
+       init_waitqueue_head(&zs->strm_wait);
+       zs->max_strm = max_strm;
+       zs->avail_strm = 1;
+
+       zstrm = zcomp_strm_alloc(comp);
+       if (!zstrm) {
+               kfree(zs);
+               return -ENOMEM;
+       }
+       list_add(&zstrm->list, &zs->idle_strm);
+       return 0;
+}
+
+static struct zcomp_strm *zcomp_strm_single_find(struct zcomp *comp)
+{
+       struct zcomp_strm_single *zs = comp->stream;
+       mutex_lock(&zs->strm_lock);
+       return zs->zstrm;
+}
+
+static void zcomp_strm_single_release(struct zcomp *comp,
+               struct zcomp_strm *zstrm)
+{
+       struct zcomp_strm_single *zs = comp->stream;
+       mutex_unlock(&zs->strm_lock);
+}
+
+static bool zcomp_strm_single_set_max_streams(struct zcomp *comp, int num_strm)
+{
+       /* zcomp_strm_single support only max_comp_streams == 1 */
+       return false;
+}
+
+static void zcomp_strm_single_destroy(struct zcomp *comp)
+{
+       struct zcomp_strm_single *zs = comp->stream;
+       zcomp_strm_free(comp, zs->zstrm);
+       kfree(zs);
+}
+
+static int zcomp_strm_single_create(struct zcomp *comp)
+{
+       struct zcomp_strm_single *zs;
+
+       comp->destroy = zcomp_strm_single_destroy;
+       comp->strm_find = zcomp_strm_single_find;
+       comp->strm_release = zcomp_strm_single_release;
+       comp->set_max_streams = zcomp_strm_single_set_max_streams;
+       zs = kmalloc(sizeof(struct zcomp_strm_single), GFP_KERNEL);
+       if (!zs)
+               return -ENOMEM;
+
+       comp->stream = zs;
+       mutex_init(&zs->strm_lock);
+       zs->zstrm = zcomp_strm_alloc(comp);
+       if (!zs->zstrm) {
+               kfree(zs);
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+/* show available compressors */
+ssize_t zcomp_available_show(const char *comp, char *buf)
+{
+       ssize_t sz = 0;
+       int i = 0;
+
+       while (backends[i]) {
+               if (sysfs_streq(comp, backends[i]->name))
+                       sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2,
+                                       "[%s] ", backends[i]->name);
+               else
+                       sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2,
+                                       "%s ", backends[i]->name);
+               i++;
+       }
+       sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n");
+       return sz;
+}
+
+bool zcomp_set_max_streams(struct zcomp *comp, int num_strm)
+{
+       return comp->set_max_streams(comp, num_strm);
+}
+
+struct zcomp_strm *zcomp_strm_find(struct zcomp *comp)
+{
+       return comp->strm_find(comp);
+}
+
+void zcomp_strm_release(struct zcomp *comp, struct zcomp_strm *zstrm)
+{
+       comp->strm_release(comp, zstrm);
+}
+
+int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm,
+               const unsigned char *src, size_t *dst_len)
+{
+       return comp->backend->compress(src, zstrm->buffer, dst_len,
+                       zstrm->private);
+}
+
+int zcomp_decompress(struct zcomp *comp, const unsigned char *src,
+               size_t src_len, unsigned char *dst)
+{
+       return comp->backend->decompress(src, src_len, dst);
+}
+
+void zcomp_destroy(struct zcomp *comp)
+{
+       comp->destroy(comp);
+       kfree(comp);
+}
+
+/*
+ * search available compressors for requested algorithm.
+ * allocate new zcomp and initialize it. return compressing
+ * backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL)
+ * if requested algorithm is not supported, ERR_PTR(-ENOMEM) in
+ * case of allocation error, or any other error potentially
+ * returned by functions zcomp_strm_{multi,single}_create.
+ */
+struct zcomp *zcomp_create(const char *compress, int max_strm)
+{
+       struct zcomp *comp;
+       struct zcomp_backend *backend;
+       int error;
+
+       backend = find_backend(compress);
+       if (!backend)
+               return ERR_PTR(-EINVAL);
+
+       comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL);
+       if (!comp)
+               return ERR_PTR(-ENOMEM);
+
+       comp->backend = backend;
+       if (max_strm > 1)
+               error = zcomp_strm_multi_create(comp, max_strm);
+       else
+               error = zcomp_strm_single_create(comp);
+       if (error) {
+               kfree(comp);
+               return ERR_PTR(error);
+       }
+       return comp;
+}
diff --git a/drivers/block/zram/zcomp.h b/drivers/block/zram/zcomp.h
new file mode 100644 (file)
index 0000000..c59d1fc
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 Sergey Senozhatsky.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ZCOMP_H_
+#define _ZCOMP_H_
+
+#include <linux/mutex.h>
+
+struct zcomp_strm {
+       /* compression/decompression buffer */
+       void *buffer;
+       /*
+        * The private data of the compression stream, only compression
+        * stream backend can touch this (e.g. compression algorithm
+        * working memory)
+        */
+       void *private;
+       /* used in multi stream backend, protected by backend strm_lock */
+       struct list_head list;
+};
+
+/* static compression backend */
+struct zcomp_backend {
+       int (*compress)(const unsigned char *src, unsigned char *dst,
+                       size_t *dst_len, void *private);
+
+       int (*decompress)(const unsigned char *src, size_t src_len,
+                       unsigned char *dst);
+
+       void *(*create)(void);
+       void (*destroy)(void *private);
+
+       const char *name;
+};
+
+/* dynamic per-device compression frontend */
+struct zcomp {
+       void *stream;
+       struct zcomp_backend *backend;
+
+       struct zcomp_strm *(*strm_find)(struct zcomp *comp);
+       void (*strm_release)(struct zcomp *comp, struct zcomp_strm *zstrm);
+       bool (*set_max_streams)(struct zcomp *comp, int num_strm);
+       void (*destroy)(struct zcomp *comp);
+};
+
+ssize_t zcomp_available_show(const char *comp, char *buf);
+
+struct zcomp *zcomp_create(const char *comp, int max_strm);
+void zcomp_destroy(struct zcomp *comp);
+
+struct zcomp_strm *zcomp_strm_find(struct zcomp *comp);
+void zcomp_strm_release(struct zcomp *comp, struct zcomp_strm *zstrm);
+
+int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm,
+               const unsigned char *src, size_t *dst_len);
+
+int zcomp_decompress(struct zcomp *comp, const unsigned char *src,
+               size_t src_len, unsigned char *dst);
+
+bool zcomp_set_max_streams(struct zcomp *comp, int num_strm);
+#endif /* _ZCOMP_H_ */
diff --git a/drivers/block/zram/zcomp_lz4.c b/drivers/block/zram/zcomp_lz4.c
new file mode 100644 (file)
index 0000000..dd60831
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 Sergey Senozhatsky.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/lz4.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+
+#include "zcomp_lz4.h"
+
+static void *zcomp_lz4_create(void)
+{
+       void *ret;
+
+       /*
+        * This function can be called in swapout/fs write path
+        * so we can't use GFP_FS|IO. And it assumes we already
+        * have at least one stream in zram initialization so we
+        * don't do best effort to allocate more stream in here.
+        * A default stream will work well without further multiple
+        * streams. That's why we use NORETRY | NOWARN.
+        */
+       ret = kzalloc(LZ4_MEM_COMPRESS, GFP_NOIO | __GFP_NORETRY |
+                                       __GFP_NOWARN);
+       if (!ret)
+               ret = __vmalloc(LZ4_MEM_COMPRESS,
+                               GFP_NOIO | __GFP_NORETRY | __GFP_NOWARN |
+                               __GFP_ZERO | __GFP_HIGHMEM,
+                               PAGE_KERNEL);
+       return ret;
+}
+
+static void zcomp_lz4_destroy(void *private)
+{
+       kvfree(private);
+}
+
+static int zcomp_lz4_compress(const unsigned char *src, unsigned char *dst,
+               size_t *dst_len, void *private)
+{
+       /* return  : Success if return 0 */
+       return lz4_compress(src, PAGE_SIZE, dst, dst_len, private);
+}
+
+static int zcomp_lz4_decompress(const unsigned char *src, size_t src_len,
+               unsigned char *dst)
+{
+       size_t dst_len = PAGE_SIZE;
+       /* return  : Success if return 0 */
+       return lz4_decompress_unknownoutputsize(src, src_len, dst, &dst_len);
+}
+
+struct zcomp_backend zcomp_lz4 = {
+       .compress = zcomp_lz4_compress,
+       .decompress = zcomp_lz4_decompress,
+       .create = zcomp_lz4_create,
+       .destroy = zcomp_lz4_destroy,
+       .name = "lz4",
+};
diff --git a/drivers/block/zram/zcomp_lz4.h b/drivers/block/zram/zcomp_lz4.h
new file mode 100644 (file)
index 0000000..60613fb
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2014 Sergey Senozhatsky.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ZCOMP_LZ4_H_
+#define _ZCOMP_LZ4_H_
+
+#include "zcomp.h"
+
+extern struct zcomp_backend zcomp_lz4;
+
+#endif /* _ZCOMP_LZ4_H_ */
diff --git a/drivers/block/zram/zcomp_lzo.c b/drivers/block/zram/zcomp_lzo.c
new file mode 100644 (file)
index 0000000..edc5499
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 Sergey Senozhatsky.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/lzo.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+
+#include "zcomp_lzo.h"
+
+static void *lzo_create(void)
+{
+       void *ret;
+
+       /*
+        * This function can be called in swapout/fs write path
+        * so we can't use GFP_FS|IO. And it assumes we already
+        * have at least one stream in zram initialization so we
+        * don't do best effort to allocate more stream in here.
+        * A default stream will work well without further multiple
+        * streams. That's why we use NORETRY | NOWARN.
+        */
+       ret = kzalloc(LZO1X_MEM_COMPRESS, GFP_NOIO | __GFP_NORETRY |
+                                       __GFP_NOWARN);
+       if (!ret)
+               ret = __vmalloc(LZO1X_MEM_COMPRESS,
+                               GFP_NOIO | __GFP_NORETRY | __GFP_NOWARN |
+                               __GFP_ZERO | __GFP_HIGHMEM,
+                               PAGE_KERNEL);
+       return ret;
+}
+
+static void lzo_destroy(void *private)
+{
+       kvfree(private);
+}
+
+static int lzo_compress(const unsigned char *src, unsigned char *dst,
+               size_t *dst_len, void *private)
+{
+       int ret = lzo1x_1_compress(src, PAGE_SIZE, dst, dst_len, private);
+       return ret == LZO_E_OK ? 0 : ret;
+}
+
+static int lzo_decompress(const unsigned char *src, size_t src_len,
+               unsigned char *dst)
+{
+       size_t dst_len = PAGE_SIZE;
+       int ret = lzo1x_decompress_safe(src, src_len, dst, &dst_len);
+       return ret == LZO_E_OK ? 0 : ret;
+}
+
+struct zcomp_backend zcomp_lzo = {
+       .compress = lzo_compress,
+       .decompress = lzo_decompress,
+       .create = lzo_create,
+       .destroy = lzo_destroy,
+       .name = "lzo",
+};
diff --git a/drivers/block/zram/zcomp_lzo.h b/drivers/block/zram/zcomp_lzo.h
new file mode 100644 (file)
index 0000000..128c580
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2014 Sergey Senozhatsky.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ZCOMP_LZO_H_
+#define _ZCOMP_LZO_H_
+
+#include "zcomp.h"
+
+extern struct zcomp_backend zcomp_lzo;
+
+#endif /* _ZCOMP_LZO_H_ */
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
new file mode 100644 (file)
index 0000000..f960586
--- /dev/null
@@ -0,0 +1,1278 @@
+/*
+ * Compressed RAM block device
+ *
+ * Copyright (C) 2008, 2009, 2010  Nitin Gupta
+ *               2012, 2013 Minchan Kim
+ *
+ * This code is released using a dual license strategy: BSD/GPL
+ * You can choose the licence that better fits your requirements.
+ *
+ * Released under the terms of 3-clause BSD License
+ * Released under the terms of GNU General Public License Version 2.0
+ *
+ */
+
+#define KMSG_COMPONENT "zram"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#ifdef CONFIG_ZRAM_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/bio.h>
+#include <linux/bitops.h>
+#include <linux/blkdev.h>
+#include <linux/buffer_head.h>
+#include <linux/device.h>
+#include <linux/genhd.h>
+#include <linux/highmem.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/vmalloc.h>
+#include <linux/err.h>
+
+#include "zram_drv.h"
+
+/* Globals */
+static int zram_major;
+static struct zram *zram_devices;
+static const char *default_compressor = "lzo";
+
+/* Module params (documentation at end) */
+static unsigned int num_devices = 1;
+
+static inline void deprecated_attr_warn(const char *name)
+{
+       pr_warn_once("%d (%s) Attribute %s (and others) will be removed. %s\n",
+                       task_pid_nr(current),
+                       current->comm,
+                       name,
+                       "See zram documentation.");
+}
+
+#define ZRAM_ATTR_RO(name)                                             \
+static ssize_t name##_show(struct device *d,           \
+                               struct device_attribute *attr, char *b) \
+{                                                                      \
+       struct zram *zram = dev_to_zram(d);                             \
+                                                                       \
+       deprecated_attr_warn(__stringify(name));                        \
+       return scnprintf(b, PAGE_SIZE, "%llu\n",                        \
+               (u64)atomic64_read(&zram->stats.name));                 \
+}                                                                      \
+static DEVICE_ATTR_RO(name);
+
+static inline bool init_done(struct zram *zram)
+{
+       return zram->disksize;
+}
+
+static inline struct zram *dev_to_zram(struct device *dev)
+{
+       return (struct zram *)dev_to_disk(dev)->private_data;
+}
+
+static ssize_t compact_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t len)
+{
+       unsigned long nr_migrated;
+       struct zram *zram = dev_to_zram(dev);
+       struct zram_meta *meta;
+
+       down_read(&zram->init_lock);
+       if (!init_done(zram)) {
+               up_read(&zram->init_lock);
+               return -EINVAL;
+       }
+
+       meta = zram->meta;
+       nr_migrated = zs_compact(meta->mem_pool);
+       atomic64_add(nr_migrated, &zram->stats.num_migrated);
+       up_read(&zram->init_lock);
+
+       return len;
+}
+
+static ssize_t disksize_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct zram *zram = dev_to_zram(dev);
+
+       return scnprintf(buf, PAGE_SIZE, "%llu\n", zram->disksize);
+}
+
+static ssize_t initstate_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       u32 val;
+       struct zram *zram = dev_to_zram(dev);
+
+       down_read(&zram->init_lock);
+       val = init_done(zram);
+       up_read(&zram->init_lock);
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", val);
+}
+
+static ssize_t orig_data_size_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct zram *zram = dev_to_zram(dev);
+
+       deprecated_attr_warn("orig_data_size");
+       return scnprintf(buf, PAGE_SIZE, "%llu\n",
+               (u64)(atomic64_read(&zram->stats.pages_stored)) << PAGE_SHIFT);
+}
+
+static ssize_t mem_used_total_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       u64 val = 0;
+       struct zram *zram = dev_to_zram(dev);
+
+       deprecated_attr_warn("mem_used_total");
+       down_read(&zram->init_lock);
+       if (init_done(zram)) {
+               struct zram_meta *meta = zram->meta;
+               val = zs_get_total_pages(meta->mem_pool);
+       }
+       up_read(&zram->init_lock);
+
+       return scnprintf(buf, PAGE_SIZE, "%llu\n", val << PAGE_SHIFT);
+}
+
+static ssize_t max_comp_streams_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       int val;
+       struct zram *zram = dev_to_zram(dev);
+
+       down_read(&zram->init_lock);
+       val = zram->max_comp_streams;
+       up_read(&zram->init_lock);
+
+       return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+
+static ssize_t mem_limit_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       u64 val;
+       struct zram *zram = dev_to_zram(dev);
+
+       deprecated_attr_warn("mem_limit");
+       down_read(&zram->init_lock);
+       val = zram->limit_pages;
+       up_read(&zram->init_lock);
+
+       return scnprintf(buf, PAGE_SIZE, "%llu\n", val << PAGE_SHIFT);
+}
+
+static ssize_t mem_limit_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t len)
+{
+       u64 limit;
+       char *tmp;
+       struct zram *zram = dev_to_zram(dev);
+
+       limit = memparse(buf, &tmp);
+       if (buf == tmp) /* no chars parsed, invalid input */
+               return -EINVAL;
+
+       down_write(&zram->init_lock);
+       zram->limit_pages = PAGE_ALIGN(limit) >> PAGE_SHIFT;
+       up_write(&zram->init_lock);
+
+       return len;
+}
+
+static ssize_t mem_used_max_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       u64 val = 0;
+       struct zram *zram = dev_to_zram(dev);
+
+       deprecated_attr_warn("mem_used_max");
+       down_read(&zram->init_lock);
+       if (init_done(zram))
+               val = atomic_long_read(&zram->stats.max_used_pages);
+       up_read(&zram->init_lock);
+
+       return scnprintf(buf, PAGE_SIZE, "%llu\n", val << PAGE_SHIFT);
+}
+
+static ssize_t mem_used_max_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t len)
+{
+       int err;
+       unsigned long val;
+       struct zram *zram = dev_to_zram(dev);
+
+       err = kstrtoul(buf, 10, &val);
+       if (err || val != 0)
+               return -EINVAL;
+
+       down_read(&zram->init_lock);
+       if (init_done(zram)) {
+               struct zram_meta *meta = zram->meta;
+               atomic_long_set(&zram->stats.max_used_pages,
+                               zs_get_total_pages(meta->mem_pool));
+       }
+       up_read(&zram->init_lock);
+
+       return len;
+}
+
+static ssize_t max_comp_streams_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t len)
+{
+       int num;
+       struct zram *zram = dev_to_zram(dev);
+       int ret;
+
+       ret = kstrtoint(buf, 0, &num);
+       if (ret < 0)
+               return ret;
+       if (num < 1)
+               return -EINVAL;
+
+       down_write(&zram->init_lock);
+       if (init_done(zram)) {
+               if (!zcomp_set_max_streams(zram->comp, num)) {
+                       pr_info("Cannot change max compression streams\n");
+                       ret = -EINVAL;
+                       goto out;
+               }
+       }
+
+       zram->max_comp_streams = num;
+       ret = len;
+out:
+       up_write(&zram->init_lock);
+       return ret;
+}
+
+static ssize_t comp_algorithm_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       size_t sz;
+       struct zram *zram = dev_to_zram(dev);
+
+       down_read(&zram->init_lock);
+       sz = zcomp_available_show(zram->compressor, buf);
+       up_read(&zram->init_lock);
+
+       return sz;
+}
+
+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);
+       down_write(&zram->init_lock);
+       if (init_done(zram)) {
+               up_write(&zram->init_lock);
+               pr_info("Can't change algorithm for initialized device\n");
+               return -EBUSY;
+       }
+       strlcpy(zram->compressor, buf, sizeof(zram->compressor));
+       up_write(&zram->init_lock);
+       return len;
+}
+
+/* flag operations needs meta->tb_lock */
+static int zram_test_flag(struct zram_meta *meta, u32 index,
+                       enum zram_pageflags flag)
+{
+       return meta->table[index].value & BIT(flag);
+}
+
+static void zram_set_flag(struct zram_meta *meta, u32 index,
+                       enum zram_pageflags flag)
+{
+       meta->table[index].value |= BIT(flag);
+}
+
+static void zram_clear_flag(struct zram_meta *meta, u32 index,
+                       enum zram_pageflags flag)
+{
+       meta->table[index].value &= ~BIT(flag);
+}
+
+static size_t zram_get_obj_size(struct zram_meta *meta, u32 index)
+{
+       return meta->table[index].value & (BIT(ZRAM_FLAG_SHIFT) - 1);
+}
+
+static void zram_set_obj_size(struct zram_meta *meta,
+                                       u32 index, size_t size)
+{
+       unsigned long flags = meta->table[index].value >> ZRAM_FLAG_SHIFT;
+
+       meta->table[index].value = (flags << ZRAM_FLAG_SHIFT) | size;
+}
+
+static inline int is_partial_io(struct bio_vec *bvec)
+{
+       return bvec->bv_len != PAGE_SIZE;
+}
+
+/*
+ * Check if request is within bounds and aligned on zram logical blocks.
+ */
+static inline int valid_io_request(struct zram *zram,
+               sector_t start, unsigned int size)
+{
+       u64 end, bound;
+
+       /* unaligned request */
+       if (unlikely(start & (ZRAM_SECTOR_PER_LOGICAL_BLOCK - 1)))
+               return 0;
+       if (unlikely(size & (ZRAM_LOGICAL_BLOCK_SIZE - 1)))
+               return 0;
+
+       end = start + (size >> SECTOR_SHIFT);
+       bound = zram->disksize >> SECTOR_SHIFT;
+       /* out of range range */
+       if (unlikely(start >= bound || end > bound || start > end))
+               return 0;
+
+       /* I/O request is valid */
+       return 1;
+}
+
+static void zram_meta_free(struct zram_meta *meta, u64 disksize)
+{
+       size_t num_pages = disksize >> PAGE_SHIFT;
+       size_t index;
+
+       /* Free all pages that are still in this zram device */
+       for (index = 0; index < num_pages; index++) {
+               unsigned long handle = meta->table[index].handle;
+
+               if (!handle)
+                       continue;
+
+               zs_free(meta->mem_pool, handle);
+       }
+
+       zs_destroy_pool(meta->mem_pool);
+       vfree(meta->table);
+       kfree(meta);
+}
+
+static struct zram_meta *zram_meta_alloc(int device_id, u64 disksize)
+{
+       size_t num_pages;
+       char pool_name[8];
+       struct zram_meta *meta = kmalloc(sizeof(*meta), GFP_KERNEL);
+
+       if (!meta)
+               return NULL;
+
+       num_pages = disksize >> PAGE_SHIFT;
+       meta->table = vzalloc(num_pages * sizeof(*meta->table));
+       if (!meta->table) {
+               pr_err("Error allocating zram address table\n");
+               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");
+               goto out_error;
+       }
+
+       return meta;
+
+out_error:
+       vfree(meta->table);
+       kfree(meta);
+       return NULL;
+}
+
+static inline bool zram_meta_get(struct zram *zram)
+{
+       if (atomic_inc_not_zero(&zram->refcount))
+               return true;
+       return false;
+}
+
+static inline void zram_meta_put(struct zram *zram)
+{
+       atomic_dec(&zram->refcount);
+}
+
+static void update_position(u32 *index, int *offset, struct bio_vec *bvec)
+{
+       if (*offset + bvec->bv_len >= PAGE_SIZE)
+               (*index)++;
+       *offset = (*offset + bvec->bv_len) % PAGE_SIZE;
+}
+
+static int page_zero_filled(void *ptr)
+{
+       unsigned int pos;
+       unsigned long *page;
+
+       page = (unsigned long *)ptr;
+
+       for (pos = 0; pos != PAGE_SIZE / sizeof(*page); pos++) {
+               if (page[pos])
+                       return 0;
+       }
+
+       return 1;
+}
+
+static void handle_zero_page(struct bio_vec *bvec)
+{
+       struct page *page = bvec->bv_page;
+       void *user_mem;
+
+       user_mem = kmap_atomic(page);
+       if (is_partial_io(bvec))
+               memset(user_mem + bvec->bv_offset, 0, bvec->bv_len);
+       else
+               clear_page(user_mem);
+       kunmap_atomic(user_mem);
+
+       flush_dcache_page(page);
+}
+
+
+/*
+ * To protect concurrent access to the same index entry,
+ * caller should hold this table index entry's bit_spinlock to
+ * indicate this index entry is accessing.
+ */
+static void zram_free_page(struct zram *zram, size_t index)
+{
+       struct zram_meta *meta = zram->meta;
+       unsigned long handle = meta->table[index].handle;
+
+       if (unlikely(!handle)) {
+               /*
+                * No memory is allocated for zero filled pages.
+                * Simply clear zero page flag.
+                */
+               if (zram_test_flag(meta, index, ZRAM_ZERO)) {
+                       zram_clear_flag(meta, index, ZRAM_ZERO);
+                       atomic64_dec(&zram->stats.zero_pages);
+               }
+               return;
+       }
+
+       zs_free(meta->mem_pool, handle);
+
+       atomic64_sub(zram_get_obj_size(meta, index),
+                       &zram->stats.compr_data_size);
+       atomic64_dec(&zram->stats.pages_stored);
+
+       meta->table[index].handle = 0;
+       zram_set_obj_size(meta, index, 0);
+}
+
+static int zram_decompress_page(struct zram *zram, char *mem, u32 index)
+{
+       int ret = 0;
+       unsigned char *cmem;
+       struct zram_meta *meta = zram->meta;
+       unsigned long handle;
+       size_t size;
+
+       bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value);
+       handle = meta->table[index].handle;
+       size = zram_get_obj_size(meta, index);
+
+       if (!handle || zram_test_flag(meta, index, ZRAM_ZERO)) {
+               bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
+               clear_page(mem);
+               return 0;
+       }
+
+       cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO);
+       if (size == PAGE_SIZE)
+               copy_page(mem, cmem);
+       else
+               ret = zcomp_decompress(zram->comp, cmem, size, mem);
+       zs_unmap_object(meta->mem_pool, handle);
+       bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
+
+       /* Should NEVER happen. Return bio error if it does. */
+       if (unlikely(ret)) {
+               pr_err("Decompression failed! err=%d, page=%u\n", ret, index);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec,
+                         u32 index, int offset)
+{
+       int ret;
+       struct page *page;
+       unsigned char *user_mem, *uncmem = NULL;
+       struct zram_meta *meta = zram->meta;
+       page = bvec->bv_page;
+
+       bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value);
+       if (unlikely(!meta->table[index].handle) ||
+                       zram_test_flag(meta, index, ZRAM_ZERO)) {
+               bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
+               handle_zero_page(bvec);
+               return 0;
+       }
+       bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
+
+       if (is_partial_io(bvec))
+               /* Use  a temporary buffer to decompress the page */
+               uncmem = kmalloc(PAGE_SIZE, GFP_NOIO);
+
+       user_mem = kmap_atomic(page);
+       if (!is_partial_io(bvec))
+               uncmem = user_mem;
+
+       if (!uncmem) {
+               pr_info("Unable to allocate temp memory\n");
+               ret = -ENOMEM;
+               goto out_cleanup;
+       }
+
+       ret = zram_decompress_page(zram, uncmem, index);
+       /* Should NEVER happen. Return bio error if it does. */
+       if (unlikely(ret))
+               goto out_cleanup;
+
+       if (is_partial_io(bvec))
+               memcpy(user_mem + bvec->bv_offset, uncmem + offset,
+                               bvec->bv_len);
+
+       flush_dcache_page(page);
+       ret = 0;
+out_cleanup:
+       kunmap_atomic(user_mem);
+       if (is_partial_io(bvec))
+               kfree(uncmem);
+       return ret;
+}
+
+static inline void update_used_max(struct zram *zram,
+                                       const unsigned long pages)
+{
+       unsigned long old_max, cur_max;
+
+       old_max = atomic_long_read(&zram->stats.max_used_pages);
+
+       do {
+               cur_max = old_max;
+               if (pages > cur_max)
+                       old_max = atomic_long_cmpxchg(
+                               &zram->stats.max_used_pages, cur_max, pages);
+       } while (old_max != cur_max);
+}
+
+static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
+                          int offset)
+{
+       int ret = 0;
+       size_t clen;
+       unsigned long handle;
+       struct page *page;
+       unsigned char *user_mem, *cmem, *src, *uncmem = NULL;
+       struct zram_meta *meta = zram->meta;
+       struct zcomp_strm *zstrm;
+       bool locked = false;
+       unsigned long alloced_pages;
+
+       page = bvec->bv_page;
+       if (is_partial_io(bvec)) {
+               /*
+                * This is a partial IO. We need to read the full page
+                * before to write the changes.
+                */
+               uncmem = kmalloc(PAGE_SIZE, GFP_NOIO);
+               if (!uncmem) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               ret = zram_decompress_page(zram, uncmem, index);
+               if (ret)
+                       goto out;
+       }
+
+       zstrm = zcomp_strm_find(zram->comp);
+       locked = true;
+       user_mem = kmap_atomic(page);
+
+       if (is_partial_io(bvec)) {
+               memcpy(uncmem + offset, user_mem + bvec->bv_offset,
+                      bvec->bv_len);
+               kunmap_atomic(user_mem);
+               user_mem = NULL;
+       } else {
+               uncmem = user_mem;
+       }
+
+       if (page_zero_filled(uncmem)) {
+               if (user_mem)
+                       kunmap_atomic(user_mem);
+               /* Free memory associated with this sector now. */
+               bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value);
+               zram_free_page(zram, index);
+               zram_set_flag(meta, index, ZRAM_ZERO);
+               bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
+
+               atomic64_inc(&zram->stats.zero_pages);
+               ret = 0;
+               goto out;
+       }
+
+       ret = zcomp_compress(zram->comp, zstrm, uncmem, &clen);
+       if (!is_partial_io(bvec)) {
+               kunmap_atomic(user_mem);
+               user_mem = NULL;
+               uncmem = NULL;
+       }
+
+       if (unlikely(ret)) {
+               pr_err("Compression failed! err=%d\n", ret);
+               goto out;
+       }
+       src = zstrm->buffer;
+       if (unlikely(clen > max_zpage_size)) {
+               clen = PAGE_SIZE;
+               if (is_partial_io(bvec))
+                       src = uncmem;
+       }
+
+       handle = zs_malloc(meta->mem_pool, clen);
+       if (!handle) {
+               pr_info("Error allocating memory for compressed page: %u, size=%zu\n",
+                       index, clen);
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       alloced_pages = zs_get_total_pages(meta->mem_pool);
+       if (zram->limit_pages && alloced_pages > zram->limit_pages) {
+               zs_free(meta->mem_pool, handle);
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       update_used_max(zram, alloced_pages);
+
+       cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_WO);
+
+       if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) {
+               src = kmap_atomic(page);
+               copy_page(cmem, src);
+               kunmap_atomic(src);
+       } else {
+               memcpy(cmem, src, clen);
+       }
+
+       zcomp_strm_release(zram->comp, zstrm);
+       locked = false;
+       zs_unmap_object(meta->mem_pool, handle);
+
+       /*
+        * Free memory associated with this sector
+        * before overwriting unused sectors.
+        */
+       bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value);
+       zram_free_page(zram, index);
+
+       meta->table[index].handle = handle;
+       zram_set_obj_size(meta, index, clen);
+       bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
+
+       /* Update stats */
+       atomic64_add(clen, &zram->stats.compr_data_size);
+       atomic64_inc(&zram->stats.pages_stored);
+out:
+       if (locked)
+               zcomp_strm_release(zram->comp, zstrm);
+       if (is_partial_io(bvec))
+               kfree(uncmem);
+       return ret;
+}
+
+static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index,
+                       int offset, int rw)
+{
+       unsigned long start_time = jiffies;
+       int ret;
+
+       generic_start_io_acct(rw, bvec->bv_len >> SECTOR_SHIFT,
+                       &zram->disk->part0);
+
+       if (rw == READ) {
+               atomic64_inc(&zram->stats.num_reads);
+               ret = zram_bvec_read(zram, bvec, index, offset);
+       } else {
+               atomic64_inc(&zram->stats.num_writes);
+               ret = zram_bvec_write(zram, bvec, index, offset);
+       }
+
+       generic_end_io_acct(rw, &zram->disk->part0, start_time);
+
+       if (unlikely(ret)) {
+               if (rw == READ)
+                       atomic64_inc(&zram->stats.failed_reads);
+               else
+                       atomic64_inc(&zram->stats.failed_writes);
+       }
+
+       return ret;
+}
+
+/*
+ * zram_bio_discard - handler on discard request
+ * @index: physical block index in PAGE_SIZE units
+ * @offset: byte offset within physical block
+ */
+static void zram_bio_discard(struct zram *zram, u32 index,
+                            int offset, struct bio *bio)
+{
+       size_t n = bio->bi_size;
+       struct zram_meta *meta = zram->meta;
+
+       /*
+        * zram manages data in physical block size units. Because logical block
+        * size isn't identical with physical block size on some arch, we
+        * could get a discard request pointing to a specific offset within a
+        * certain physical block.  Although we can handle this request by
+        * reading that physiclal block and decompressing and partially zeroing
+        * and re-compressing and then re-storing it, this isn't reasonable
+        * because our intent with a discard request is to save memory.  So
+        * skipping this logical block is appropriate here.
+        */
+       if (offset) {
+               if (n <= (PAGE_SIZE - offset))
+                       return;
+
+               n -= (PAGE_SIZE - offset);
+               index++;
+       }
+
+       while (n >= PAGE_SIZE) {
+               bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value);
+               zram_free_page(zram, index);
+               bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
+               atomic64_inc(&zram->stats.notify_free);
+               index++;
+               n -= PAGE_SIZE;
+       }
+}
+
+static void zram_reset_device(struct zram *zram)
+{
+       struct zram_meta *meta;
+       struct zcomp *comp;
+       u64 disksize;
+
+       down_write(&zram->init_lock);
+
+       zram->limit_pages = 0;
+
+       if (!init_done(zram)) {
+               up_write(&zram->init_lock);
+               return;
+       }
+
+       meta = zram->meta;
+       comp = zram->comp;
+       disksize = zram->disksize;
+       /*
+        * Refcount will go down to 0 eventually and r/w handler
+        * cannot handle further I/O so it will bail out by
+        * check zram_meta_get.
+        */
+       zram_meta_put(zram);
+       /*
+        * We want to free zram_meta in process context to avoid
+        * deadlock between reclaim path and any other locks.
+        */
+       wait_event(zram->io_done, atomic_read(&zram->refcount) == 0);
+
+       /* Reset stats */
+       memset(&zram->stats, 0, sizeof(zram->stats));
+       zram->disksize = 0;
+       zram->max_comp_streams = 1;
+
+       set_capacity(zram->disk, 0);
+       part_stat_set_all(&zram->disk->part0, 0);
+
+       up_write(&zram->init_lock);
+       /* I/O operation under all of CPU are done so let's free */
+       zram_meta_free(meta, disksize);
+       zcomp_destroy(comp);
+}
+
+static ssize_t disksize_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t len)
+{
+       u64 disksize;
+       struct zcomp *comp;
+       struct zram_meta *meta;
+       struct zram *zram = dev_to_zram(dev);
+       int err;
+
+       disksize = memparse(buf, NULL);
+       if (!disksize)
+               return -EINVAL;
+
+       disksize = PAGE_ALIGN(disksize);
+       meta = zram_meta_alloc(zram->disk->first_minor, disksize);
+       if (!meta)
+               return -ENOMEM;
+
+       comp = zcomp_create(zram->compressor, zram->max_comp_streams);
+       if (IS_ERR(comp)) {
+               pr_info("Cannot initialise %s compressing backend\n",
+                               zram->compressor);
+               err = PTR_ERR(comp);
+               goto out_free_meta;
+       }
+
+       down_write(&zram->init_lock);
+       if (init_done(zram)) {
+               pr_info("Cannot change disksize for initialized device\n");
+               err = -EBUSY;
+               goto out_destroy_comp;
+       }
+
+       init_waitqueue_head(&zram->io_done);
+       atomic_set(&zram->refcount, 1);
+       zram->meta = meta;
+       zram->comp = comp;
+       zram->disksize = disksize;
+       set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT);
+       up_write(&zram->init_lock);
+
+       /*
+        * Revalidate disk out of the init_lock to avoid lockdep splat.
+        * It's okay because disk's capacity is protected by init_lock
+        * so that revalidate_disk always sees up-to-date capacity.
+        */
+       revalidate_disk(zram->disk);
+
+       return len;
+
+out_destroy_comp:
+       up_write(&zram->init_lock);
+       zcomp_destroy(comp);
+out_free_meta:
+       zram_meta_free(meta, disksize);
+       return err;
+}
+
+static ssize_t reset_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t len)
+{
+       int ret;
+       unsigned short do_reset;
+       struct zram *zram;
+       struct block_device *bdev;
+
+       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;
+       }
+
+       ret = kstrtou16(buf, 10, &do_reset);
+       if (ret)
+               goto out;
+
+       if (!do_reset) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /* Make sure all pending I/O is finished */
+       fsync_bdev(bdev);
+       zram_reset_device(zram);
+
+       mutex_unlock(&bdev->bd_mutex);
+       revalidate_disk(zram->disk);
+       bdput(bdev);
+
+       return len;
+
+out:
+       mutex_unlock(&bdev->bd_mutex);
+       bdput(bdev);
+       return ret;
+}
+
+static void __zram_make_request(struct zram *zram, struct bio *bio)
+{
+       int i, offset, rw;
+       u32 index;
+       struct bio_vec *bvec;
+
+       index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT;
+       offset = (bio->bi_sector &
+                 (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT;
+
+       if (unlikely(bio->bi_rw & REQ_DISCARD)) {
+               zram_bio_discard(zram, index, offset, bio);
+               bio_endio(bio, 0);
+               return;
+       }
+
+       rw = bio_data_dir(bio);
+       bio_for_each_segment(bvec, bio, i) {
+               int max_transfer_size = PAGE_SIZE - offset;
+
+               if (bvec->bv_len > max_transfer_size) {
+                       /*
+                        * zram_bvec_rw() can only make operation on a single
+                        * zram page. Split the bio vector.
+                        */
+                       struct bio_vec bv;
+
+                       bv.bv_page = bvec->bv_page;
+                       bv.bv_len = max_transfer_size;
+                       bv.bv_offset = bvec->bv_offset;
+
+                       if (zram_bvec_rw(zram, &bv, index, offset, rw) < 0)
+                               goto out;
+
+                       bv.bv_len = bvec->bv_len - max_transfer_size;
+                       bv.bv_offset += max_transfer_size;
+                       if (zram_bvec_rw(zram, &bv, index + 1, 0, rw) < 0)
+                               goto out;
+               } else
+                       if (zram_bvec_rw(zram, bvec, index, offset, rw) < 0)
+                               goto out;
+
+               update_position(&index, &offset, bvec);
+       }
+
+       set_bit(BIO_UPTODATE, &bio->bi_flags);
+       bio_endio(bio, 0);
+       return;
+
+out:
+       bio_io_error(bio);
+}
+
+/*
+ * Handler function for all zram I/O requests.
+ */
+static void zram_make_request(struct request_queue *queue, struct bio *bio)
+{
+       struct zram *zram = queue->queuedata;
+
+       if (unlikely(!zram_meta_get(zram)))
+               goto error;
+
+       if (!valid_io_request(zram, bio->bi_sector,
+                                       bio->bi_size)) {
+               atomic64_inc(&zram->stats.invalid_io);
+               goto put_zram;
+       }
+
+       __zram_make_request(zram, bio);
+       zram_meta_put(zram);
+       return;
+put_zram:
+       zram_meta_put(zram);
+error:
+       bio_io_error(bio);
+}
+
+static void zram_slot_free_notify(struct block_device *bdev,
+                               unsigned long index)
+{
+       struct zram *zram;
+       struct zram_meta *meta;
+
+       zram = bdev->bd_disk->private_data;
+       meta = zram->meta;
+
+       bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value);
+       zram_free_page(zram, index);
+       bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
+       atomic64_inc(&zram->stats.notify_free);
+}
+
+static const struct block_device_operations zram_devops = {
+       .swap_slot_free_notify = zram_slot_free_notify,
+       .owner = THIS_MODULE
+};
+
+static DEVICE_ATTR_WO(compact);
+static DEVICE_ATTR_RW(disksize);
+static DEVICE_ATTR_RO(initstate);
+static DEVICE_ATTR_WO(reset);
+static DEVICE_ATTR_RO(orig_data_size);
+static DEVICE_ATTR_RO(mem_used_total);
+static DEVICE_ATTR_RW(mem_limit);
+static DEVICE_ATTR_RW(mem_used_max);
+static DEVICE_ATTR_RW(max_comp_streams);
+static DEVICE_ATTR_RW(comp_algorithm);
+
+static ssize_t io_stat_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct zram *zram = dev_to_zram(dev);
+       ssize_t ret;
+
+       down_read(&zram->init_lock);
+       ret = scnprintf(buf, PAGE_SIZE,
+                       "%8llu %8llu %8llu %8llu\n",
+                       (u64)atomic64_read(&zram->stats.failed_reads),
+                       (u64)atomic64_read(&zram->stats.failed_writes),
+                       (u64)atomic64_read(&zram->stats.invalid_io),
+                       (u64)atomic64_read(&zram->stats.notify_free));
+       up_read(&zram->init_lock);
+
+       return ret;
+}
+
+static ssize_t mm_stat_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct zram *zram = dev_to_zram(dev);
+       u64 orig_size, mem_used = 0;
+       long max_used;
+       ssize_t ret;
+
+       down_read(&zram->init_lock);
+       if (init_done(zram))
+               mem_used = zs_get_total_pages(zram->meta->mem_pool);
+
+       orig_size = atomic64_read(&zram->stats.pages_stored);
+       max_used = atomic_long_read(&zram->stats.max_used_pages);
+
+       ret = scnprintf(buf, PAGE_SIZE,
+                       "%8llu %8llu %8llu %8lu %8ld %8llu %8llu\n",
+                       orig_size << PAGE_SHIFT,
+                       (u64)atomic64_read(&zram->stats.compr_data_size),
+                       mem_used << PAGE_SHIFT,
+                       zram->limit_pages << PAGE_SHIFT,
+                       max_used << PAGE_SHIFT,
+                       (u64)atomic64_read(&zram->stats.zero_pages),
+                       (u64)atomic64_read(&zram->stats.num_migrated));
+       up_read(&zram->init_lock);
+
+       return ret;
+}
+
+static DEVICE_ATTR_RO(io_stat);
+static DEVICE_ATTR_RO(mm_stat);
+ZRAM_ATTR_RO(num_reads);
+ZRAM_ATTR_RO(num_writes);
+ZRAM_ATTR_RO(failed_reads);
+ZRAM_ATTR_RO(failed_writes);
+ZRAM_ATTR_RO(invalid_io);
+ZRAM_ATTR_RO(notify_free);
+ZRAM_ATTR_RO(zero_pages);
+ZRAM_ATTR_RO(compr_data_size);
+
+static struct attribute *zram_disk_attrs[] = {
+       &dev_attr_disksize.attr,
+       &dev_attr_initstate.attr,
+       &dev_attr_reset.attr,
+       &dev_attr_num_reads.attr,
+       &dev_attr_num_writes.attr,
+       &dev_attr_failed_reads.attr,
+       &dev_attr_failed_writes.attr,
+       &dev_attr_compact.attr,
+       &dev_attr_invalid_io.attr,
+       &dev_attr_notify_free.attr,
+       &dev_attr_zero_pages.attr,
+       &dev_attr_orig_data_size.attr,
+       &dev_attr_compr_data_size.attr,
+       &dev_attr_mem_used_total.attr,
+       &dev_attr_mem_limit.attr,
+       &dev_attr_mem_used_max.attr,
+       &dev_attr_max_comp_streams.attr,
+       &dev_attr_comp_algorithm.attr,
+       &dev_attr_io_stat.attr,
+       &dev_attr_mm_stat.attr,
+       NULL,
+};
+
+static struct attribute_group zram_disk_attr_group = {
+       .attrs = zram_disk_attrs,
+};
+
+static int create_device(struct zram *zram, int device_id)
+{
+       struct request_queue *queue;
+       int ret = -ENOMEM;
+
+       init_rwsem(&zram->init_lock);
+
+       queue = blk_alloc_queue(GFP_KERNEL);
+       if (!queue) {
+               pr_err("Error allocating disk queue for device %d\n",
+                       device_id);
+               goto out;
+       }
+
+       blk_queue_make_request(queue, zram_make_request);
+
+        /* gendisk structure */
+       zram->disk = alloc_disk(1);
+       if (!zram->disk) {
+               pr_warn("Error allocating disk structure for device %d\n",
+                       device_id);
+               ret = -ENOMEM;
+               goto out_free_queue;
+       }
+
+       zram->disk->major = zram_major;
+       zram->disk->first_minor = device_id;
+       zram->disk->fops = &zram_devops;
+       zram->disk->queue = queue;
+       zram->disk->queue->queuedata = zram;
+       zram->disk->private_data = zram;
+       snprintf(zram->disk->disk_name, 16, "zram%d", device_id);
+
+       /* Actual capacity set using syfs (/sys/block/zram<id>/disksize */
+       set_capacity(zram->disk, 0);
+       /* zram devices sort of resembles non-rotational disks */
+       queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue);
+       queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, zram->disk->queue);
+       /*
+        * To ensure that we always get PAGE_SIZE aligned
+        * and n*PAGE_SIZED sized I/O requests.
+        */
+       blk_queue_physical_block_size(zram->disk->queue, PAGE_SIZE);
+       blk_queue_logical_block_size(zram->disk->queue,
+                                       ZRAM_LOGICAL_BLOCK_SIZE);
+       blk_queue_io_min(zram->disk->queue, PAGE_SIZE);
+       blk_queue_io_opt(zram->disk->queue, PAGE_SIZE);
+       zram->disk->queue->limits.discard_granularity = PAGE_SIZE;
+       zram->disk->queue->limits.max_discard_sectors = UINT_MAX;
+       /*
+        * zram_bio_discard() will clear all logical blocks if logical block
+        * size is identical with physical block size(PAGE_SIZE). But if it is
+        * different, we will skip discarding some parts of logical blocks in
+        * the part of the request range which isn't aligned to physical block
+        * size.  So we can't ensure that all discarded logical blocks are
+        * zeroed.
+        */
+       if (ZRAM_LOGICAL_BLOCK_SIZE == PAGE_SIZE)
+               zram->disk->queue->limits.discard_zeroes_data = 1;
+       else
+               zram->disk->queue->limits.discard_zeroes_data = 0;
+       queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, zram->disk->queue);
+
+       add_disk(zram->disk);
+
+       ret = sysfs_create_group(&disk_to_dev(zram->disk)->kobj,
+                               &zram_disk_attr_group);
+       if (ret < 0) {
+               pr_warn("Error creating sysfs group");
+               goto out_free_disk;
+       }
+       strlcpy(zram->compressor, default_compressor, sizeof(zram->compressor));
+       zram->meta = NULL;
+       zram->max_comp_streams = 1;
+       return 0;
+
+out_free_disk:
+       del_gendisk(zram->disk);
+       put_disk(zram->disk);
+out_free_queue:
+       blk_cleanup_queue(queue);
+out:
+       return ret;
+}
+
+static void destroy_devices(unsigned int nr)
+{
+       struct zram *zram;
+       unsigned int i;
+
+       for (i = 0; i < nr; i++) {
+               zram = &zram_devices[i];
+               /*
+                * Remove sysfs first, so no one will perform a disksize
+                * store while we destroy the devices
+                */
+               sysfs_remove_group(&disk_to_dev(zram->disk)->kobj,
+                               &zram_disk_attr_group);
+
+               zram_reset_device(zram);
+
+               blk_cleanup_queue(zram->disk->queue);
+               del_gendisk(zram->disk);
+               put_disk(zram->disk);
+       }
+
+       kfree(zram_devices);
+       unregister_blkdev(zram_major, "zram");
+       pr_info("Destroyed %u device(s)\n", nr);
+}
+
+static int __init zram_init(void)
+{
+       int ret, dev_id;
+
+       if (num_devices > max_num_devices) {
+               pr_warn("Invalid value for num_devices: %u\n",
+                               num_devices);
+               return -EINVAL;
+       }
+
+       zram_major = register_blkdev(0, "zram");
+       if (zram_major <= 0) {
+               pr_warn("Unable to get major number\n");
+               return -EBUSY;
+       }
+
+       /* Allocate the device array and initialize each one */
+       zram_devices = kzalloc(num_devices * sizeof(struct zram), GFP_KERNEL);
+       if (!zram_devices) {
+               unregister_blkdev(zram_major, "zram");
+               return -ENOMEM;
+       }
+
+       for (dev_id = 0; dev_id < num_devices; dev_id++) {
+               ret = create_device(&zram_devices[dev_id], dev_id);
+               if (ret)
+                       goto out_error;
+       }
+
+       pr_info("Created %u device(s)\n", num_devices);
+       return 0;
+
+out_error:
+       destroy_devices(dev_id);
+       return ret;
+}
+
+static void __exit zram_exit(void)
+{
+       destroy_devices(num_devices);
+}
+
+module_init(zram_init);
+module_exit(zram_exit);
+
+module_param(num_devices, uint, 0);
+MODULE_PARM_DESC(num_devices, "Number of zram devices");
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Nitin Gupta <ngupta@vflare.org>");
+MODULE_DESCRIPTION("Compressed RAM Block Device");
diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h
new file mode 100644 (file)
index 0000000..570c598
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Compressed RAM block device
+ *
+ * Copyright (C) 2008, 2009, 2010  Nitin Gupta
+ *               2012, 2013 Minchan Kim
+ *
+ * This code is released using a dual license strategy: BSD/GPL
+ * You can choose the licence that better fits your requirements.
+ *
+ * Released under the terms of 3-clause BSD License
+ * Released under the terms of GNU General Public License Version 2.0
+ *
+ */
+
+#ifndef _ZRAM_DRV_H_
+#define _ZRAM_DRV_H_
+
+#include <linux/spinlock.h>
+#include <linux/zsmalloc.h>
+
+#include "zcomp.h"
+
+/*
+ * Some arbitrary value. This is just to catch
+ * invalid value for num_devices module parameter.
+ */
+static const unsigned max_num_devices = 32;
+
+/*-- Configurable parameters */
+
+/*
+ * Pages that compress to size greater than this are stored
+ * uncompressed in memory.
+ */
+static const size_t max_zpage_size = PAGE_SIZE / 4 * 3;
+
+/*
+ * NOTE: max_zpage_size must be less than or equal to:
+ *   ZS_MAX_ALLOC_SIZE. Otherwise, zs_malloc() would
+ * always return failure.
+ */
+
+/*-- End of configurable params */
+
+#define SECTOR_SHIFT           9
+#define SECTORS_PER_PAGE_SHIFT (PAGE_SHIFT - SECTOR_SHIFT)
+#define SECTORS_PER_PAGE       (1 << SECTORS_PER_PAGE_SHIFT)
+#define ZRAM_LOGICAL_BLOCK_SHIFT 12
+#define ZRAM_LOGICAL_BLOCK_SIZE        (1 << ZRAM_LOGICAL_BLOCK_SHIFT)
+#define ZRAM_SECTOR_PER_LOGICAL_BLOCK  \
+       (1 << (ZRAM_LOGICAL_BLOCK_SHIFT - SECTOR_SHIFT))
+
+
+/*
+ * The lower ZRAM_FLAG_SHIFT bits of table.value is for
+ * object size (excluding header), the higher bits is for
+ * zram_pageflags.
+ *
+ * zram is mainly used for memory efficiency so we want to keep memory
+ * footprint small so we can squeeze size and flags into a field.
+ * The lower ZRAM_FLAG_SHIFT bits is for object size (excluding header),
+ * the higher bits is for zram_pageflags.
+ */
+#define ZRAM_FLAG_SHIFT 24
+
+/* Flags for zram pages (table[page_no].value) */
+enum zram_pageflags {
+       /* Page consists entirely of zeros */
+       ZRAM_ZERO = ZRAM_FLAG_SHIFT,
+       ZRAM_ACCESS,    /* page is now accessed */
+
+       __NR_ZRAM_PAGEFLAGS,
+};
+
+/*-- Data structures */
+
+/* Allocated for each disk page */
+struct zram_table_entry {
+       unsigned long handle;
+       unsigned long value;
+};
+
+struct zram_stats {
+       atomic64_t compr_data_size;     /* compressed size of pages stored */
+       atomic64_t num_reads;   /* failed + successful */
+       atomic64_t num_writes;  /* --do-- */
+       atomic64_t num_migrated;        /* no. of migrated object */
+       atomic64_t failed_reads;        /* can happen when memory is too low */
+       atomic64_t failed_writes;       /* can happen when memory is too low */
+       atomic64_t invalid_io;  /* non-page-aligned I/O requests */
+       atomic64_t notify_free; /* no. of swap slot free notifications */
+       atomic64_t zero_pages;          /* no. of zero filled pages */
+       atomic64_t pages_stored;        /* no. of pages currently stored */
+       atomic_long_t max_used_pages;   /* no. of maximum pages stored */
+};
+
+struct zram_meta {
+       struct zram_table_entry *table;
+       struct zs_pool *mem_pool;
+};
+
+struct zram {
+       struct zram_meta *meta;
+       struct zcomp *comp;
+       struct gendisk *disk;
+       /* Prevent concurrent execution of device init */
+       struct rw_semaphore init_lock;
+       /*
+        * the number of pages zram can consume for storing compressed data
+        */
+       unsigned long limit_pages;
+       int max_comp_streams;
+
+       struct zram_stats stats;
+       atomic_t refcount; /* refcount for zram_meta */
+       /* wait all IO under all of cpu are done */
+       wait_queue_head_t io_done;
+       /*
+        * This is the limit on amount of *uncompressed* worth of data
+        * we can store in a disk.
+        */
+       u64 disksize;   /* bytes */
+       char compressor[10];
+};
+#endif
index 7ceb20535b89cb7f328f8c9bcd51c226ac4e30fd..ebb459fa201af30d17aa0719fd7255dd95adada0 100644 (file)
@@ -72,8 +72,6 @@ source "drivers/staging/sep/Kconfig"
 
 source "drivers/staging/iio/Kconfig"
 
-source "drivers/staging/zram/Kconfig"
-
 source "drivers/staging/wlags49_h2/Kconfig"
 
 source "drivers/staging/wlags49_h25/Kconfig"
index f9d2609dcb47c7783ea0dfbca891da9ed69afa1c..7b2b50c2fee44389904edc6eec003a5755c30e58 100644 (file)
@@ -30,7 +30,6 @@ obj-$(CONFIG_VT6656)          += vt6656/
 obj-$(CONFIG_VME_BUS)          += vme/
 obj-$(CONFIG_DX_SEP)            += sep/
 obj-$(CONFIG_IIO)              += iio/
-obj-$(CONFIG_ZRAM)             += zram/
 obj-$(CONFIG_WLAGS49_H2)       += wlags49_h2/
 obj-$(CONFIG_WLAGS49_H25)      += wlags49_h25/
 obj-$(CONFIG_FB_SM7XX)         += sm7xxfb/
diff --git a/drivers/staging/zram/Kconfig b/drivers/staging/zram/Kconfig
deleted file mode 100644 (file)
index 983314c..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-config ZRAM
-       tristate "Compressed RAM block device support"
-       depends on BLOCK && SYSFS && ZSMALLOC
-       select LZO_COMPRESS
-       select LZO_DECOMPRESS
-       default n
-       help
-         Creates virtual block devices called /dev/zramX (X = 0, 1, ...).
-         Pages written to these disks are compressed and stored in memory
-         itself. These disks allow very fast I/O and compression provides
-         good amounts of memory savings.
-
-         It has several use cases, for example: /tmp storage, use as swap
-         disks and maybe many more.
-
-         See zram.txt for more information.
-         Project home: <https://compcache.googlecode.com/>
-
-config ZRAM_DEBUG
-       bool "Compressed RAM block device debug support"
-       depends on ZRAM
-       default n
-       help
-         This option adds additional debugging code to the compressed
-         RAM block device driver.
diff --git a/drivers/staging/zram/Makefile b/drivers/staging/zram/Makefile
deleted file mode 100644 (file)
index 7f4a301..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-zram-y :=      zram_drv.o zram_sysfs.o
-
-obj-$(CONFIG_ZRAM)     +=      zram.o
diff --git a/drivers/staging/zram/zram.txt b/drivers/staging/zram/zram.txt
deleted file mode 100644 (file)
index 765d790..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-zram: Compressed RAM based block devices
-----------------------------------------
-
-Project home: http://compcache.googlecode.com/
-
-* Introduction
-
-The zram module creates RAM based block devices named /dev/zram<id>
-(<id> = 0, 1, ...). Pages written to these disks are compressed and stored
-in memory itself. These disks allow very fast I/O and compression provides
-good amounts of memory savings. Some of the usecases include /tmp storage,
-use as swap disks, various caches under /var and maybe many more :)
-
-Statistics for individual zram devices are exported through sysfs nodes at
-/sys/block/zram<id>/
-
-* Usage
-
-Following shows a typical sequence of steps for using zram.
-
-1) Load Module:
-       modprobe zram num_devices=4
-       This creates 4 devices: /dev/zram{0,1,2,3}
-       (num_devices parameter is optional. Default: 1)
-
-2) Set Disksize
-        Set disk size by writing the value to sysfs node 'disksize'.
-        The value can be either in bytes or you can use mem suffixes.
-        Examples:
-            # Initialize /dev/zram0 with 50MB disksize
-            echo $((50*1024*1024)) > /sys/block/zram0/disksize
-
-            # Using mem suffixes
-            echo 256K > /sys/block/zram0/disksize
-            echo 512M > /sys/block/zram0/disksize
-            echo 1G > /sys/block/zram0/disksize
-
-3) Activate:
-       mkswap /dev/zram0
-       swapon /dev/zram0
-
-       mkfs.ext4 /dev/zram1
-       mount /dev/zram1 /tmp
-
-4) Stats:
-       Per-device statistics are exported as various nodes under
-       /sys/block/zram<id>/
-               disksize
-               num_reads
-               num_writes
-               invalid_io
-               notify_free
-               discard
-               zero_pages
-               orig_data_size
-               compr_data_size
-               mem_used_total
-
-5) Deactivate:
-       swapoff /dev/zram0
-       umount /dev/zram1
-
-6) Reset:
-       Write any positive value to 'reset' sysfs node
-       echo 1 > /sys/block/zram0/reset
-       echo 1 > /sys/block/zram1/reset
-
-       This frees all the memory allocated for the given device and
-       resets the disksize to zero. You must set the disksize again
-       before reusing the device.
-
-Please report any problems at:
- - Mailing list: linux-mm-cc at laptop dot org
- - Issue tracker: http://code.google.com/p/compcache/issues/list
-
-Nitin Gupta
-ngupta@vflare.org
diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c
deleted file mode 100644 (file)
index 15d5751..0000000
+++ /dev/null
@@ -1,760 +0,0 @@
-/*
- * Compressed RAM block device
- *
- * Copyright (C) 2008, 2009, 2010  Nitin Gupta
- *
- * This code is released using a dual license strategy: BSD/GPL
- * You can choose the licence that better fits your requirements.
- *
- * Released under the terms of 3-clause BSD License
- * Released under the terms of GNU General Public License Version 2.0
- *
- * Project home: http://compcache.googlecode.com
- */
-
-#define KMSG_COMPONENT "zram"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-
-#ifdef CONFIG_ZRAM_DEBUG
-#define DEBUG
-#endif
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/bio.h>
-#include <linux/bitops.h>
-#include <linux/blkdev.h>
-#include <linux/buffer_head.h>
-#include <linux/device.h>
-#include <linux/genhd.h>
-#include <linux/highmem.h>
-#include <linux/slab.h>
-#include <linux/lzo.h>
-#include <linux/string.h>
-#include <linux/vmalloc.h>
-
-#include "zram_drv.h"
-
-/* Globals */
-static int zram_major;
-struct zram *zram_devices;
-
-/* Module params (documentation at end) */
-static unsigned int num_devices = 1;
-
-static void zram_stat64_add(struct zram *zram, u64 *v, u64 inc)
-{
-       spin_lock(&zram->stat64_lock);
-       *v = *v + inc;
-       spin_unlock(&zram->stat64_lock);
-}
-
-static void zram_stat64_sub(struct zram *zram, u64 *v, u64 dec)
-{
-       spin_lock(&zram->stat64_lock);
-       *v = *v - dec;
-       spin_unlock(&zram->stat64_lock);
-}
-
-static void zram_stat64_inc(struct zram *zram, u64 *v)
-{
-       zram_stat64_add(zram, v, 1);
-}
-
-static int zram_test_flag(struct zram_meta *meta, u32 index,
-                       enum zram_pageflags flag)
-{
-       return meta->table[index].flags & BIT(flag);
-}
-
-static void zram_set_flag(struct zram_meta *meta, u32 index,
-                       enum zram_pageflags flag)
-{
-       meta->table[index].flags |= BIT(flag);
-}
-
-static void zram_clear_flag(struct zram_meta *meta, u32 index,
-                       enum zram_pageflags flag)
-{
-       meta->table[index].flags &= ~BIT(flag);
-}
-
-static int page_zero_filled(void *ptr)
-{
-       unsigned int pos;
-       unsigned long *page;
-
-       page = (unsigned long *)ptr;
-
-       for (pos = 0; pos != PAGE_SIZE / sizeof(*page); pos++) {
-               if (page[pos])
-                       return 0;
-       }
-
-       return 1;
-}
-
-static void zram_free_page(struct zram *zram, size_t index)
-{
-       struct zram_meta *meta = zram->meta;
-       unsigned long handle = meta->table[index].handle;
-       u16 size = meta->table[index].size;
-
-       if (unlikely(!handle)) {
-               /*
-                * No memory is allocated for zero filled pages.
-                * Simply clear zero page flag.
-                */
-               if (zram_test_flag(meta, index, ZRAM_ZERO)) {
-                       zram_clear_flag(meta, index, ZRAM_ZERO);
-                       zram->stats.pages_zero--;
-               }
-               return;
-       }
-
-       if (unlikely(size > max_zpage_size))
-               zram->stats.bad_compress--;
-
-       zs_free(meta->mem_pool, handle);
-
-       if (size <= PAGE_SIZE / 2)
-               zram->stats.good_compress--;
-
-       zram_stat64_sub(zram, &zram->stats.compr_size,
-                       meta->table[index].size);
-       zram->stats.pages_stored--;
-
-       meta->table[index].handle = 0;
-       meta->table[index].size = 0;
-}
-
-static void handle_zero_page(struct bio_vec *bvec)
-{
-       struct page *page = bvec->bv_page;
-       void *user_mem;
-
-       user_mem = kmap_atomic(page);
-       memset(user_mem + bvec->bv_offset, 0, bvec->bv_len);
-       kunmap_atomic(user_mem);
-
-       flush_dcache_page(page);
-}
-
-static inline int is_partial_io(struct bio_vec *bvec)
-{
-       return bvec->bv_len != PAGE_SIZE;
-}
-
-static int zram_decompress_page(struct zram *zram, char *mem, u32 index)
-{
-       int ret = LZO_E_OK;
-       size_t clen = PAGE_SIZE;
-       unsigned char *cmem;
-       struct zram_meta *meta = zram->meta;
-       unsigned long handle = meta->table[index].handle;
-
-       if (!handle || zram_test_flag(meta, index, ZRAM_ZERO)) {
-               memset(mem, 0, PAGE_SIZE);
-               return 0;
-       }
-
-       cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO);
-       if (meta->table[index].size == PAGE_SIZE)
-               memcpy(mem, cmem, PAGE_SIZE);
-       else
-               ret = lzo1x_decompress_safe(cmem, meta->table[index].size,
-                                               mem, &clen);
-       zs_unmap_object(meta->mem_pool, handle);
-
-       /* Should NEVER happen. Return bio error if it does. */
-       if (unlikely(ret != LZO_E_OK)) {
-               pr_err("Decompression failed! err=%d, page=%u\n", ret, index);
-               zram_stat64_inc(zram, &zram->stats.failed_reads);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec,
-                         u32 index, int offset, struct bio *bio)
-{
-       int ret;
-       struct page *page;
-       unsigned char *user_mem, *uncmem = NULL;
-       struct zram_meta *meta = zram->meta;
-       page = bvec->bv_page;
-
-       if (unlikely(!meta->table[index].handle) ||
-                       zram_test_flag(meta, index, ZRAM_ZERO)) {
-               handle_zero_page(bvec);
-               return 0;
-       }
-
-       if (is_partial_io(bvec))
-               /* Use  a temporary buffer to decompress the page */
-               uncmem = kmalloc(PAGE_SIZE, GFP_NOIO);
-
-       user_mem = kmap_atomic(page);
-       if (!is_partial_io(bvec))
-               uncmem = user_mem;
-
-       if (!uncmem) {
-               pr_info("Unable to allocate temp memory\n");
-               ret = -ENOMEM;
-               goto out_cleanup;
-       }
-
-       ret = zram_decompress_page(zram, uncmem, index);
-       /* Should NEVER happen. Return bio error if it does. */
-       if (unlikely(ret != LZO_E_OK))
-               goto out_cleanup;
-
-       if (is_partial_io(bvec))
-               memcpy(user_mem + bvec->bv_offset, uncmem + offset,
-                               bvec->bv_len);
-
-       flush_dcache_page(page);
-       ret = 0;
-out_cleanup:
-       kunmap_atomic(user_mem);
-       if (is_partial_io(bvec))
-               kfree(uncmem);
-       return ret;
-}
-
-static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
-                          int offset)
-{
-       int ret = 0;
-       size_t clen;
-       unsigned long handle;
-       struct page *page;
-       unsigned char *user_mem, *cmem, *src, *uncmem = NULL;
-       struct zram_meta *meta = zram->meta;
-
-       page = bvec->bv_page;
-       src = meta->compress_buffer;
-
-       if (is_partial_io(bvec)) {
-               /*
-                * This is a partial IO. We need to read the full page
-                * before to write the changes.
-                */
-               uncmem = kmalloc(PAGE_SIZE, GFP_NOIO);
-               if (!uncmem) {
-                       ret = -ENOMEM;
-                       goto out;
-               }
-               ret = zram_decompress_page(zram, uncmem, index);
-               if (ret)
-                       goto out;
-       }
-
-       /*
-        * System overwrites unused sectors. Free memory associated
-        * with this sector now.
-        */
-       if (meta->table[index].handle ||
-           zram_test_flag(meta, index, ZRAM_ZERO))
-               zram_free_page(zram, index);
-
-       user_mem = kmap_atomic(page);
-
-       if (is_partial_io(bvec)) {
-               memcpy(uncmem + offset, user_mem + bvec->bv_offset,
-                      bvec->bv_len);
-               kunmap_atomic(user_mem);
-               user_mem = NULL;
-       } else {
-               uncmem = user_mem;
-       }
-
-       if (page_zero_filled(uncmem)) {
-               kunmap_atomic(user_mem);
-               zram->stats.pages_zero++;
-               zram_set_flag(meta, index, ZRAM_ZERO);
-               ret = 0;
-               goto out;
-       }
-
-       ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen,
-                              meta->compress_workmem);
-
-       if (!is_partial_io(bvec)) {
-               kunmap_atomic(user_mem);
-               user_mem = NULL;
-               uncmem = NULL;
-       }
-
-       if (unlikely(ret != LZO_E_OK)) {
-               pr_err("Compression failed! err=%d\n", ret);
-               goto out;
-       }
-
-       if (unlikely(clen > max_zpage_size)) {
-               zram->stats.bad_compress++;
-               clen = PAGE_SIZE;
-               src = NULL;
-               if (is_partial_io(bvec))
-                       src = uncmem;
-       }
-
-       handle = zs_malloc(meta->mem_pool, clen);
-       if (!handle) {
-               pr_info("Error allocating memory for compressed "
-                       "page: %u, size=%zu\n", index, clen);
-               ret = -ENOMEM;
-               goto out;
-       }
-       cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_WO);
-
-       if ((clen == PAGE_SIZE) && !is_partial_io(bvec))
-               src = kmap_atomic(page);
-       memcpy(cmem, src, clen);
-       if ((clen == PAGE_SIZE) && !is_partial_io(bvec))
-               kunmap_atomic(src);
-
-       zs_unmap_object(meta->mem_pool, handle);
-
-       meta->table[index].handle = handle;
-       meta->table[index].size = clen;
-
-       /* Update stats */
-       zram_stat64_add(zram, &zram->stats.compr_size, clen);
-       zram->stats.pages_stored++;
-       if (clen <= PAGE_SIZE / 2)
-               zram->stats.good_compress++;
-
-out:
-       if (is_partial_io(bvec))
-               kfree(uncmem);
-
-       if (ret)
-               zram_stat64_inc(zram, &zram->stats.failed_writes);
-       return ret;
-}
-
-static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index,
-                       int offset, struct bio *bio, int rw)
-{
-       int ret;
-
-       if (rw == READ) {
-               down_read(&zram->lock);
-               ret = zram_bvec_read(zram, bvec, index, offset, bio);
-               up_read(&zram->lock);
-       } else {
-               down_write(&zram->lock);
-               ret = zram_bvec_write(zram, bvec, index, offset);
-               up_write(&zram->lock);
-       }
-
-       return ret;
-}
-
-static void update_position(u32 *index, int *offset, struct bio_vec *bvec)
-{
-       if (*offset + bvec->bv_len >= PAGE_SIZE)
-               (*index)++;
-       *offset = (*offset + bvec->bv_len) % PAGE_SIZE;
-}
-
-static void __zram_make_request(struct zram *zram, struct bio *bio, int rw)
-{
-       int i, offset;
-       u32 index;
-       struct bio_vec *bvec;
-
-       switch (rw) {
-       case READ:
-               zram_stat64_inc(zram, &zram->stats.num_reads);
-               break;
-       case WRITE:
-               zram_stat64_inc(zram, &zram->stats.num_writes);
-               break;
-       }
-
-       index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT;
-       offset = (bio->bi_sector & (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT;
-
-       bio_for_each_segment(bvec, bio, i) {
-               int max_transfer_size = PAGE_SIZE - offset;
-
-               if (bvec->bv_len > max_transfer_size) {
-                       /*
-                        * zram_bvec_rw() can only make operation on a single
-                        * zram page. Split the bio vector.
-                        */
-                       struct bio_vec bv;
-
-                       bv.bv_page = bvec->bv_page;
-                       bv.bv_len = max_transfer_size;
-                       bv.bv_offset = bvec->bv_offset;
-
-                       if (zram_bvec_rw(zram, &bv, index, offset, bio, rw) < 0)
-                               goto out;
-
-                       bv.bv_len = bvec->bv_len - max_transfer_size;
-                       bv.bv_offset += max_transfer_size;
-                       if (zram_bvec_rw(zram, &bv, index+1, 0, bio, rw) < 0)
-                               goto out;
-               } else
-                       if (zram_bvec_rw(zram, bvec, index, offset, bio, rw)
-                           < 0)
-                               goto out;
-
-               update_position(&index, &offset, bvec);
-       }
-
-       set_bit(BIO_UPTODATE, &bio->bi_flags);
-       bio_endio(bio, 0);
-       return;
-
-out:
-       bio_io_error(bio);
-}
-
-/*
- * Check if request is within bounds and aligned on zram logical blocks.
- */
-static inline int valid_io_request(struct zram *zram, struct bio *bio)
-{
-       u64 start, end, bound;
-
-       /* unaligned request */
-       if (unlikely(bio->bi_sector & (ZRAM_SECTOR_PER_LOGICAL_BLOCK - 1)))
-               return 0;
-       if (unlikely(bio->bi_size & (ZRAM_LOGICAL_BLOCK_SIZE - 1)))
-               return 0;
-
-       start = bio->bi_sector;
-       end = start + (bio->bi_size >> SECTOR_SHIFT);
-       bound = zram->disksize >> SECTOR_SHIFT;
-       /* out of range range */
-       if (unlikely(start >= bound || end > bound || start > end))
-               return 0;
-
-       /* I/O request is valid */
-       return 1;
-}
-
-/*
- * Handler function for all zram I/O requests.
- */
-static void zram_make_request(struct request_queue *queue, struct bio *bio)
-{
-       struct zram *zram = queue->queuedata;
-
-       down_read(&zram->init_lock);
-       if (unlikely(!zram->init_done))
-               goto error;
-
-       if (!valid_io_request(zram, bio)) {
-               zram_stat64_inc(zram, &zram->stats.invalid_io);
-               goto error;
-       }
-
-       __zram_make_request(zram, bio, bio_data_dir(bio));
-       up_read(&zram->init_lock);
-
-       return;
-
-error:
-       up_read(&zram->init_lock);
-       bio_io_error(bio);
-}
-
-static void __zram_reset_device(struct zram *zram)
-{
-       size_t index;
-       struct zram_meta *meta;
-
-       if (!zram->init_done)
-               return;
-
-       meta = zram->meta;
-       zram->init_done = 0;
-
-       /* Free all pages that are still in this zram device */
-       for (index = 0; index < zram->disksize >> PAGE_SHIFT; index++) {
-               unsigned long handle = meta->table[index].handle;
-               if (!handle)
-                       continue;
-
-               zs_free(meta->mem_pool, handle);
-       }
-
-       zram_meta_free(zram->meta);
-       zram->meta = NULL;
-       /* Reset stats */
-       memset(&zram->stats, 0, sizeof(zram->stats));
-
-       zram->disksize = 0;
-       set_capacity(zram->disk, 0);
-}
-
-void zram_reset_device(struct zram *zram)
-{
-       down_write(&zram->init_lock);
-       __zram_reset_device(zram);
-       up_write(&zram->init_lock);
-}
-
-void zram_meta_free(struct zram_meta *meta)
-{
-       zs_destroy_pool(meta->mem_pool);
-       kfree(meta->compress_workmem);
-       free_pages((unsigned long)meta->compress_buffer, 1);
-       vfree(meta->table);
-       kfree(meta);
-}
-
-struct zram_meta *zram_meta_alloc(u64 disksize)
-{
-       size_t num_pages;
-       struct zram_meta *meta = kmalloc(sizeof(*meta), GFP_KERNEL);
-       if (!meta)
-               goto out;
-
-       meta->compress_workmem = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
-       if (!meta->compress_workmem)
-               goto free_meta;
-
-       meta->compress_buffer =
-               (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
-       if (!meta->compress_buffer) {
-               pr_err("Error allocating compressor buffer space\n");
-               goto free_workmem;
-       }
-
-       num_pages = disksize >> PAGE_SHIFT;
-       meta->table = vzalloc(num_pages * sizeof(*meta->table));
-       if (!meta->table) {
-               pr_err("Error allocating zram address table\n");
-               goto free_buffer;
-       }
-
-       meta->mem_pool = zs_create_pool(GFP_NOIO | __GFP_HIGHMEM, NULL);
-       if (!meta->mem_pool) {
-               pr_err("Error creating memory pool\n");
-               goto free_table;
-       }
-
-       return meta;
-
-free_table:
-       vfree(meta->table);
-free_buffer:
-       free_pages((unsigned long)meta->compress_buffer, 1);
-free_workmem:
-       kfree(meta->compress_workmem);
-free_meta:
-       kfree(meta);
-       meta = NULL;
-out:
-       return meta;
-}
-
-void zram_init_device(struct zram *zram, struct zram_meta *meta)
-{
-       if (zram->disksize > 2 * (totalram_pages << PAGE_SHIFT)) {
-               pr_info(
-               "There is little point creating a zram of greater than "
-               "twice the size of memory since we expect a 2:1 compression "
-               "ratio. Note that zram uses about 0.1%% of the size of "
-               "the disk when not in use so a huge zram is "
-               "wasteful.\n"
-               "\tMemory Size: %lu kB\n"
-               "\tSize you selected: %llu kB\n"
-               "Continuing anyway ...\n",
-               (totalram_pages << PAGE_SHIFT) >> 10, zram->disksize >> 10
-               );
-       }
-
-       /* zram devices sort of resembles non-rotational disks */
-       queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue);
-
-       zram->meta = meta;
-       zram->init_done = 1;
-
-       pr_debug("Initialization done!\n");
-}
-
-static void zram_slot_free_notify(struct block_device *bdev,
-                               unsigned long index)
-{
-       struct zram *zram;
-
-       zram = bdev->bd_disk->private_data;
-       down_write(&zram->lock);
-       zram_free_page(zram, index);
-       up_write(&zram->lock);
-       zram_stat64_inc(zram, &zram->stats.notify_free);
-}
-
-static const struct block_device_operations zram_devops = {
-       .swap_slot_free_notify = zram_slot_free_notify,
-       .owner = THIS_MODULE
-};
-
-static int create_device(struct zram *zram, int device_id)
-{
-       int ret = -ENOMEM;
-
-       init_rwsem(&zram->lock);
-       init_rwsem(&zram->init_lock);
-       spin_lock_init(&zram->stat64_lock);
-
-       zram->queue = blk_alloc_queue(GFP_KERNEL);
-       if (!zram->queue) {
-               pr_err("Error allocating disk queue for device %d\n",
-                       device_id);
-               goto out;
-       }
-
-       blk_queue_make_request(zram->queue, zram_make_request);
-       zram->queue->queuedata = zram;
-
-        /* gendisk structure */
-       zram->disk = alloc_disk(1);
-       if (!zram->disk) {
-               pr_warn("Error allocating disk structure for device %d\n",
-                       device_id);
-               goto out_free_queue;
-       }
-
-       zram->disk->major = zram_major;
-       zram->disk->first_minor = device_id;
-       zram->disk->fops = &zram_devops;
-       zram->disk->queue = zram->queue;
-       zram->disk->private_data = zram;
-       snprintf(zram->disk->disk_name, 16, "zram%d", device_id);
-
-       /* Actual capacity set using syfs (/sys/block/zram<id>/disksize */
-       set_capacity(zram->disk, 0);
-
-       /*
-        * To ensure that we always get PAGE_SIZE aligned
-        * and n*PAGE_SIZED sized I/O requests.
-        */
-       blk_queue_physical_block_size(zram->disk->queue, PAGE_SIZE);
-       blk_queue_logical_block_size(zram->disk->queue,
-                                       ZRAM_LOGICAL_BLOCK_SIZE);
-       blk_queue_io_min(zram->disk->queue, PAGE_SIZE);
-       blk_queue_io_opt(zram->disk->queue, PAGE_SIZE);
-
-       add_disk(zram->disk);
-
-       ret = sysfs_create_group(&disk_to_dev(zram->disk)->kobj,
-                               &zram_disk_attr_group);
-       if (ret < 0) {
-               pr_warn("Error creating sysfs group");
-               goto out_free_disk;
-       }
-
-       zram->init_done = 0;
-       return 0;
-
-out_free_disk:
-       del_gendisk(zram->disk);
-       put_disk(zram->disk);
-out_free_queue:
-       blk_cleanup_queue(zram->queue);
-out:
-       return ret;
-}
-
-static void destroy_device(struct zram *zram)
-{
-       sysfs_remove_group(&disk_to_dev(zram->disk)->kobj,
-                       &zram_disk_attr_group);
-
-       if (zram->disk) {
-               del_gendisk(zram->disk);
-               put_disk(zram->disk);
-       }
-
-       if (zram->queue)
-               blk_cleanup_queue(zram->queue);
-}
-
-unsigned int zram_get_num_devices(void)
-{
-       return num_devices;
-}
-
-static int __init zram_init(void)
-{
-       int ret, dev_id;
-
-       if (num_devices > max_num_devices) {
-               pr_warn("Invalid value for num_devices: %u\n",
-                               num_devices);
-               ret = -EINVAL;
-               goto out;
-       }
-
-       zram_major = register_blkdev(0, "zram");
-       if (zram_major <= 0) {
-               pr_warn("Unable to get major number\n");
-               ret = -EBUSY;
-               goto out;
-       }
-
-       /* Allocate the device array and initialize each one */
-       zram_devices = kzalloc(num_devices * sizeof(struct zram), GFP_KERNEL);
-       if (!zram_devices) {
-               ret = -ENOMEM;
-               goto unregister;
-       }
-
-       for (dev_id = 0; dev_id < num_devices; dev_id++) {
-               ret = create_device(&zram_devices[dev_id], dev_id);
-               if (ret)
-                       goto free_devices;
-       }
-
-       pr_info("Created %u device(s) ...\n", num_devices);
-
-       return 0;
-
-free_devices:
-       while (dev_id)
-               destroy_device(&zram_devices[--dev_id]);
-       kfree(zram_devices);
-unregister:
-       unregister_blkdev(zram_major, "zram");
-out:
-       return ret;
-}
-
-static void __exit zram_exit(void)
-{
-       int i;
-       struct zram *zram;
-
-       for (i = 0; i < num_devices; i++) {
-               zram = &zram_devices[i];
-
-               get_disk(zram->disk);
-               destroy_device(zram);
-               zram_reset_device(zram);
-               put_disk(zram->disk);
-       }
-
-       unregister_blkdev(zram_major, "zram");
-
-       kfree(zram_devices);
-       pr_debug("Cleanup done!\n");
-}
-
-module_param(num_devices, uint, 0);
-MODULE_PARM_DESC(num_devices, "Number of zram devices");
-
-module_init(zram_init);
-module_exit(zram_exit);
-
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_AUTHOR("Nitin Gupta <ngupta@vflare.org>");
-MODULE_DESCRIPTION("Compressed RAM Block Device");
diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h
deleted file mode 100644 (file)
index 7f5c8aa..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Compressed RAM block device
- *
- * Copyright (C) 2008, 2009, 2010  Nitin Gupta
- *
- * This code is released using a dual license strategy: BSD/GPL
- * You can choose the licence that better fits your requirements.
- *
- * Released under the terms of 3-clause BSD License
- * Released under the terms of GNU General Public License Version 2.0
- *
- * Project home: http://compcache.googlecode.com
- */
-
-#ifndef _ZRAM_DRV_H_
-#define _ZRAM_DRV_H_
-
-#include <linux/spinlock.h>
-#include <linux/mutex.h>
-#include <linux/zsmalloc.h>
-
-/*
- * Some arbitrary value. This is just to catch
- * invalid value for num_devices module parameter.
- */
-static const unsigned max_num_devices = 32;
-
-/*-- Configurable parameters */
-
-/*
- * Pages that compress to size greater than this are stored
- * uncompressed in memory.
- */
-static const size_t max_zpage_size = PAGE_SIZE / 4 * 3;
-
-/*
- * NOTE: max_zpage_size must be less than or equal to:
- *   ZS_MAX_ALLOC_SIZE. Otherwise, zs_malloc() would
- * always return failure.
- */
-
-/*-- End of configurable params */
-
-#define SECTOR_SHIFT           9
-#define SECTOR_SIZE            (1 << SECTOR_SHIFT)
-#define SECTORS_PER_PAGE_SHIFT (PAGE_SHIFT - SECTOR_SHIFT)
-#define SECTORS_PER_PAGE       (1 << SECTORS_PER_PAGE_SHIFT)
-#define ZRAM_LOGICAL_BLOCK_SHIFT 12
-#define ZRAM_LOGICAL_BLOCK_SIZE        (1 << ZRAM_LOGICAL_BLOCK_SHIFT)
-#define ZRAM_SECTOR_PER_LOGICAL_BLOCK  \
-       (1 << (ZRAM_LOGICAL_BLOCK_SHIFT - SECTOR_SHIFT))
-
-/* Flags for zram pages (table[page_no].flags) */
-enum zram_pageflags {
-       /* Page consists entirely of zeros */
-       ZRAM_ZERO,
-
-       __NR_ZRAM_PAGEFLAGS,
-};
-
-/*-- Data structures */
-
-/* Allocated for each disk page */
-struct table {
-       unsigned long handle;
-       u16 size;       /* object size (excluding header) */
-       u8 count;       /* object ref count (not yet used) */
-       u8 flags;
-} __aligned(4);
-
-struct zram_stats {
-       u64 compr_size;         /* compressed size of pages stored */
-       u64 num_reads;          /* failed + successful */
-       u64 num_writes;         /* --do-- */
-       u64 failed_reads;       /* should NEVER! happen */
-       u64 failed_writes;      /* can happen when memory is too low */
-       u64 invalid_io;         /* non-page-aligned I/O requests */
-       u64 notify_free;        /* no. of swap slot free notifications */
-       u32 pages_zero;         /* no. of zero filled pages */
-       u32 pages_stored;       /* no. of pages currently stored */
-       u32 good_compress;      /* % of pages with compression ratio<=50% */
-       u32 bad_compress;       /* % of pages with compression ratio>=75% */
-};
-
-struct zram_meta {
-       void *compress_workmem;
-       void *compress_buffer;
-       struct table *table;
-       struct zs_pool *mem_pool;
-};
-
-struct zram {
-       struct zram_meta *meta;
-       spinlock_t stat64_lock; /* protect 64-bit stats */
-       struct rw_semaphore lock; /* protect compression buffers, table,
-                                  * 32bit stat counters against concurrent
-                                  * notifications, reads and writes */
-       struct request_queue *queue;
-       struct gendisk *disk;
-       int init_done;
-       /* 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.
-        */
-       u64 disksize;   /* bytes */
-
-       struct zram_stats stats;
-};
-
-extern struct zram *zram_devices;
-unsigned int zram_get_num_devices(void);
-#ifdef CONFIG_SYSFS
-extern struct attribute_group zram_disk_attr_group;
-#endif
-
-extern void zram_reset_device(struct zram *zram);
-extern struct zram_meta *zram_meta_alloc(u64 disksize);
-extern void zram_meta_free(struct zram_meta *meta);
-extern void zram_init_device(struct zram *zram, struct zram_meta *meta);
-
-#endif
index 5139fbd5676ba1ccd71f1db27171e4898cbefe37..91a1854040f28ccbe7eff03fa9216ef20ae72225 100644 (file)
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -1686,6 +1686,34 @@ void bio_check_pages_dirty(struct bio *bio)
        }
 }
 
+void generic_start_io_acct(int rw, unsigned long sectors,
+                          struct hd_struct *part)
+{
+       int cpu = part_stat_lock();
+
+       part_round_stats(cpu, part);
+       part_stat_inc(cpu, part, ios[rw]);
+       part_stat_add(cpu, part, sectors[rw], sectors);
+       part_inc_in_flight(part, rw);
+
+       part_stat_unlock();
+}
+EXPORT_SYMBOL(generic_start_io_acct);
+
+void generic_end_io_acct(int rw, struct hd_struct *part,
+                        unsigned long start_time)
+{
+       unsigned long duration = jiffies - start_time;
+       int cpu = part_stat_lock();
+
+       part_stat_add(cpu, part, ticks[rw], duration);
+       part_round_stats(cpu, part);
+       part_dec_in_flight(part, rw);
+
+       part_stat_unlock();
+}
+EXPORT_SYMBOL(generic_end_io_acct);
+
 #if ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE
 void bio_flush_dcache_pages(struct bio *bi)
 {
index ef24466d8f82516a76029577df9c8f1ec530cf20..080de01da764ac8c55e98dfbda22fcb715095942 100644 (file)
@@ -282,6 +282,11 @@ extern struct bio *bio_copy_kern(struct request_queue *, void *, unsigned int,
 extern void bio_set_pages_dirty(struct bio *bio);
 extern void bio_check_pages_dirty(struct bio *bio);
 
+void generic_start_io_acct(int rw, unsigned long sectors,
+                          struct hd_struct *part);
+void generic_end_io_acct(int rw, struct hd_struct *part,
+                        unsigned long start_time);
+
 #ifndef ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE
 # error        "You should define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE for your platform"
 #endif
index c8709398a7540b80145c48a867f91b5972f2c77b..b617080846d5da51efe4ca78540e07a7fc94c655 100644 (file)
@@ -505,6 +505,12 @@ ssize_t device_store_bool(struct device *dev, struct device_attribute *attr,
 
 #define DEVICE_ATTR(_name, _mode, _show, _store) \
        struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
+#define DEVICE_ATTR_RW(_name) \
+       struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
+#define DEVICE_ATTR_RO(_name) \
+       struct device_attribute dev_attr_##_name = __ATTR_RO(_name)
+#define DEVICE_ATTR_WO(_name) \
+       struct device_attribute dev_attr_##_name = __ATTR_WO(_name)
 #define DEVICE_ULONG_ATTR(_name, _mode, _var) \
        struct dev_ext_attribute dev_attr_##_name = \
                { __ATTR(_name, _mode, device_show_ulong, device_store_ulong), &(_var) }
index 03d75a1a6dcf0a310dbb1db97eccf9602465ed94..c3fe0a39f6a6d507e4c3028f17a64c53c23690c9 100644 (file)
@@ -79,6 +79,15 @@ struct attribute_group {
        .show   = _name##_show,                                 \
 }
 
+
+#define __ATTR_WO(_name) {                                             \
+       .attr   = { .name = __stringify(_name), .mode = S_IWUSR },      \
+       .store  = _name##_store,                                        \
+}
+
+#define __ATTR_RW(_name) __ATTR(_name, (S_IWUSR | S_IRUGO),            \
+                        _name##_show, _name##_store)
+
 #define __ATTR_NULL { .attr = { .name = NULL } }
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
index 7597965704cba5eba6231c8d32de6a803fd46909..7b816c748db41a204843bddbc50c5727d271ded8 100644 (file)
@@ -232,8 +232,6 @@ static ssize_t stable_pages_required_show(struct device *dev,
                        bdi_cap_stable_pages_required(bdi) ? 1 : 0);
 }
 
-#define __ATTR_RW(attr) __ATTR(attr, 0644, attr##_show, attr##_store)
-
 static struct device_attribute bdi_dev_attrs[] = {
        __ATTR_RW(read_ahead_kb),
        __ATTR_RW(min_ratio),
index 9e8d46294a50567e637162b291cab8343ac6d50b..fb1ec10ce449d74e9e5ed2c32ac1fa4be92f5864 100644 (file)
@@ -1052,7 +1052,7 @@ static inline void __zs_cpu_down(struct mapping_area *area)
 static inline void *__zs_map_object(struct mapping_area *area,
                                struct page *pages[2], int off, int size)
 {
-       BUG_ON(map_vm_area(area->vm, PAGE_KERNEL, &pages));
+       BUG_ON(map_vm_area(area->vm, PAGE_KERNEL, pages));
        area->vm_addr = area->vm->addr;
        return area->vm_addr + off;
 }