2009-09-22 04:56:53 +00:00
|
|
|
/*
|
2010-06-01 08:01:25 +00:00
|
|
|
* Compressed RAM block device
|
2009-09-22 04:56:53 +00:00
|
|
|
*
|
2010-01-28 15:51:35 +00:00
|
|
|
* Copyright (C) 2008, 2009, 2010 Nitin Gupta
|
2009-09-22 04:56:53 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
#define KMSG_COMPONENT "zram"
|
2009-09-22 04:56:53 +00:00
|
|
|
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
|
|
|
|
|
2011-01-28 14:59:26 +00:00
|
|
|
#ifdef CONFIG_ZRAM_DEBUG
|
|
|
|
#define DEBUG
|
|
|
|
#endif
|
|
|
|
|
2009-09-22 04:56:53 +00:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/kernel.h>
|
2010-06-24 03:27:09 +00:00
|
|
|
#include <linux/bio.h>
|
2009-09-22 04:56:53 +00:00
|
|
|
#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 cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 08:04:11 +00:00
|
|
|
#include <linux/slab.h>
|
2009-09-22 04:56:53 +00:00
|
|
|
#include <linux/lzo.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/vmalloc.h>
|
|
|
|
|
2010-06-01 08:01:24 +00:00
|
|
|
#include "zram_drv.h"
|
2009-09-22 04:56:53 +00:00
|
|
|
|
|
|
|
/* Globals */
|
2010-06-01 08:01:25 +00:00
|
|
|
static int zram_major;
|
2011-07-20 23:05:57 +00:00
|
|
|
struct zram *zram_devices;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
|
|
|
/* Module params (documentation at end) */
|
2010-08-09 17:26:47 +00:00
|
|
|
unsigned int num_devices;
|
|
|
|
|
|
|
|
static void zram_stat_inc(u32 *v)
|
|
|
|
{
|
|
|
|
*v = *v + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void zram_stat_dec(u32 *v)
|
|
|
|
{
|
|
|
|
*v = *v - 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);
|
|
|
|
}
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
static int zram_test_flag(struct zram *zram, u32 index,
|
|
|
|
enum zram_pageflags flag)
|
2009-09-22 04:56:53 +00:00
|
|
|
{
|
2010-06-01 08:01:25 +00:00
|
|
|
return zram->table[index].flags & BIT(flag);
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
static void zram_set_flag(struct zram *zram, u32 index,
|
|
|
|
enum zram_pageflags flag)
|
2009-09-22 04:56:53 +00:00
|
|
|
{
|
2010-06-01 08:01:25 +00:00
|
|
|
zram->table[index].flags |= BIT(flag);
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
static void zram_clear_flag(struct zram *zram, u32 index,
|
|
|
|
enum zram_pageflags flag)
|
2009-09-22 04:56:53 +00:00
|
|
|
{
|
2010-06-01 08:01:25 +00:00
|
|
|
zram->table[index].flags &= ~BIT(flag);
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
static void zram_set_disksize(struct zram *zram, size_t totalram_bytes)
|
2009-09-22 04:56:53 +00:00
|
|
|
{
|
2010-06-01 08:01:25 +00:00
|
|
|
if (!zram->disksize) {
|
2009-09-22 04:56:53 +00:00
|
|
|
pr_info(
|
|
|
|
"disk size not provided. You can use disksize_kb module "
|
|
|
|
"param to specify size.\nUsing default: (%u%% of RAM).\n",
|
|
|
|
default_disksize_perc_ram
|
|
|
|
);
|
2010-06-01 08:01:25 +00:00
|
|
|
zram->disksize = default_disksize_perc_ram *
|
2009-09-22 04:56:53 +00:00
|
|
|
(totalram_bytes / 100);
|
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
if (zram->disksize > 2 * (totalram_bytes)) {
|
2009-09-22 04:56:53 +00:00
|
|
|
pr_info(
|
2010-06-01 08:01:25 +00:00
|
|
|
"There is little point creating a zram of greater than "
|
2009-09-22 04:56:53 +00:00
|
|
|
"twice the size of memory since we expect a 2:1 compression "
|
2010-06-01 08:01:25 +00:00
|
|
|
"ratio. Note that zram uses about 0.1%% of the size of "
|
|
|
|
"the disk when not in use so a huge zram is "
|
2009-09-22 04:56:53 +00:00
|
|
|
"wasteful.\n"
|
|
|
|
"\tMemory Size: %zu kB\n"
|
2010-08-09 17:26:47 +00:00
|
|
|
"\tSize you selected: %llu kB\n"
|
2009-09-22 04:56:53 +00:00
|
|
|
"Continuing anyway ...\n",
|
2010-06-01 08:01:25 +00:00
|
|
|
totalram_bytes >> 10, zram->disksize
|
2009-09-22 04:56:53 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
zram->disksize &= PAGE_MASK;
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
static void zram_free_page(struct zram *zram, size_t index)
|
2009-09-22 04:56:53 +00:00
|
|
|
{
|
|
|
|
u32 clen;
|
|
|
|
void *obj;
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
struct page *page = zram->table[index].page;
|
|
|
|
u32 offset = zram->table[index].offset;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
|
|
|
if (unlikely(!page)) {
|
2010-01-28 15:43:41 +00:00
|
|
|
/*
|
|
|
|
* No memory is allocated for zero filled pages.
|
|
|
|
* Simply clear zero page flag.
|
|
|
|
*/
|
2010-06-01 08:01:25 +00:00
|
|
|
if (zram_test_flag(zram, index, ZRAM_ZERO)) {
|
|
|
|
zram_clear_flag(zram, index, ZRAM_ZERO);
|
|
|
|
zram_stat_dec(&zram->stats.pages_zero);
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) {
|
2009-09-22 04:56:53 +00:00
|
|
|
clen = PAGE_SIZE;
|
|
|
|
__free_page(page);
|
2010-06-01 08:01:25 +00:00
|
|
|
zram_clear_flag(zram, index, ZRAM_UNCOMPRESSED);
|
|
|
|
zram_stat_dec(&zram->stats.pages_expand);
|
2009-09-22 04:56:53 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
obj = kmap_atomic(page, KM_USER0) + offset;
|
|
|
|
clen = xv_get_object_size(obj) - sizeof(struct zobj_header);
|
|
|
|
kunmap_atomic(obj, KM_USER0);
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
xv_free(zram->mem_pool, page, offset);
|
2009-09-22 04:56:53 +00:00
|
|
|
if (clen <= PAGE_SIZE / 2)
|
2010-06-01 08:01:25 +00:00
|
|
|
zram_stat_dec(&zram->stats.good_compress);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
|
|
|
out:
|
2010-08-09 17:26:47 +00:00
|
|
|
zram_stat64_sub(zram, &zram->stats.compr_size, clen);
|
2010-06-01 08:01:25 +00:00
|
|
|
zram_stat_dec(&zram->stats.pages_stored);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
zram->table[index].page = NULL;
|
|
|
|
zram->table[index].offset = 0;
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2011-06-10 13:28:48 +00:00
|
|
|
static void handle_zero_page(struct bio_vec *bvec)
|
2009-09-22 04:56:53 +00:00
|
|
|
{
|
2011-06-10 13:28:48 +00:00
|
|
|
struct page *page = bvec->bv_page;
|
2009-09-22 04:56:53 +00:00
|
|
|
void *user_mem;
|
|
|
|
|
|
|
|
user_mem = kmap_atomic(page, KM_USER0);
|
2011-06-10 13:28:48 +00:00
|
|
|
memset(user_mem + bvec->bv_offset, 0, bvec->bv_len);
|
2009-09-22 04:56:53 +00:00
|
|
|
kunmap_atomic(user_mem, KM_USER0);
|
|
|
|
|
2009-12-12 06:14:46 +00:00
|
|
|
flush_dcache_page(page);
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2011-06-10 13:28:48 +00:00
|
|
|
static void handle_uncompressed_page(struct zram *zram, struct bio_vec *bvec,
|
|
|
|
u32 index, int offset)
|
2009-09-22 04:56:53 +00:00
|
|
|
{
|
2011-06-10 13:28:48 +00:00
|
|
|
struct page *page = bvec->bv_page;
|
2009-09-22 04:56:53 +00:00
|
|
|
unsigned char *user_mem, *cmem;
|
|
|
|
|
|
|
|
user_mem = kmap_atomic(page, KM_USER0);
|
2011-06-10 13:28:46 +00:00
|
|
|
cmem = kmap_atomic(zram->table[index].page, KM_USER1);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:48 +00:00
|
|
|
memcpy(user_mem + bvec->bv_offset, cmem + offset, bvec->bv_len);
|
2009-09-22 04:56:53 +00:00
|
|
|
kunmap_atomic(cmem, KM_USER1);
|
2011-07-13 15:20:05 +00:00
|
|
|
kunmap_atomic(user_mem, KM_USER0);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2009-12-12 06:14:46 +00:00
|
|
|
flush_dcache_page(page);
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2011-06-10 13:28:48 +00:00
|
|
|
static inline int is_partial_io(struct bio_vec *bvec)
|
|
|
|
{
|
|
|
|
return bvec->bv_len != PAGE_SIZE;
|
|
|
|
}
|
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec,
|
2011-06-10 13:28:48 +00:00
|
|
|
u32 index, int offset, struct bio *bio)
|
2009-09-22 04:56:53 +00:00
|
|
|
{
|
2011-06-10 13:28:47 +00:00
|
|
|
int ret;
|
|
|
|
size_t clen;
|
|
|
|
struct page *page;
|
|
|
|
struct zobj_header *zheader;
|
2011-06-10 13:28:48 +00:00
|
|
|
unsigned char *user_mem, *cmem, *uncmem = NULL;
|
Staging: ramzswap: Support generic I/O requests
Currently, ramzwap devices (/dev/ramzswapX) can only
be used as swap disks since it was hard-coded to consider
only the first request in bio vector.
Now, we iterate over all the segments in an incoming
bio which allows us to handle all kinds of I/O requests.
ramzswap devices can still handle PAGE_SIZE aligned and
multiple of PAGE_SIZE sized I/O requests only. To ensure
that we get always get such requests only, we set following
request_queue attributes to PAGE_SIZE:
- physical_block_size
- logical_block_size
- io_min
- io_opt
Note: physical and logical block sizes were already set
equal to PAGE_SIZE and that seems to be sufficient to get
PAGE_SIZE aligned I/O.
Since we are no longer limited to handling swap requests
only, the next few patches rename ramzswap to zram. So,
the devices will then be called /dev/zram{0, 1, 2, ...}
Usage/Examples:
1) Use as /tmp storage
- mkfs.ext4 /dev/zram0
- mount /dev/zram0 /tmp
2) Use as swap:
- mkswap /dev/zram0
- swapon /dev/zram0 -p 10 # give highest priority to zram0
Performance:
- I/O benchamark done with 'dd' command. Details can be
found here:
http://code.google.com/p/compcache/wiki/zramperf
Summary:
- Maximum read speed (approx):
- ram disk: 1200 MB/sec
- zram disk: 600 MB/sec
- Maximum write speed (approx):
- ram disk: 500 MB/sec
- zram disk: 160 MB/sec
Issues:
- Double caching: We can potentially waste memory by having
two copies of a page -- one in page cache (uncompress) and
second in the device memory (compressed). However, during
reclaim, clean page cache pages are quickly freed, so this
does not seem to be a big problem.
- Stale data: Not all filesystems support issuing 'discard'
requests to underlying block devices. So, if such filesystems
are used over zram devices, we can accumulate lot of stale
data in memory. Even for filesystems to do support discard
(example, ext4), we need to see how effective it is.
- Scalability: There is only one (per-device) de/compression
buffer stats. This can lead to significant contention, especially
when used for generic (non-swap) purposes.
Signed-off-by: Nitin Gupta <ngupta@vflare.org>
Acked-by: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2010-06-01 08:01:23 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
page = bvec->bv_page;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
if (zram_test_flag(zram, index, ZRAM_ZERO)) {
|
2011-06-10 13:28:48 +00:00
|
|
|
handle_zero_page(bvec);
|
2011-06-10 13:28:47 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
/* Requested page is not present in compressed area */
|
|
|
|
if (unlikely(!zram->table[index].page)) {
|
|
|
|
pr_debug("Read before write: sector=%lu, size=%u",
|
|
|
|
(ulong)(bio->bi_sector), bio->bi_size);
|
2011-06-10 13:28:48 +00:00
|
|
|
handle_zero_page(bvec);
|
2011-06-10 13:28:47 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
/* Page is stored uncompressed since it's incompressible */
|
|
|
|
if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) {
|
2011-06-10 13:28:48 +00:00
|
|
|
handle_uncompressed_page(zram, bvec, index, offset);
|
2011-06-10 13:28:47 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:48 +00:00
|
|
|
if (is_partial_io(bvec)) {
|
|
|
|
/* Use a temporary buffer to decompress the page */
|
|
|
|
uncmem = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
|
|
|
if (!uncmem) {
|
|
|
|
pr_info("Error allocating temp memory!\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
user_mem = kmap_atomic(page, KM_USER0);
|
2011-06-10 13:28:48 +00:00
|
|
|
if (!is_partial_io(bvec))
|
|
|
|
uncmem = user_mem;
|
2011-06-10 13:28:47 +00:00
|
|
|
clen = PAGE_SIZE;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
cmem = kmap_atomic(zram->table[index].page, KM_USER1) +
|
|
|
|
zram->table[index].offset;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
ret = lzo1x_decompress_safe(cmem + sizeof(*zheader),
|
|
|
|
xv_get_object_size(cmem) - sizeof(*zheader),
|
2011-06-10 13:28:48 +00:00
|
|
|
uncmem, &clen);
|
|
|
|
|
|
|
|
if (is_partial_io(bvec)) {
|
|
|
|
memcpy(user_mem + bvec->bv_offset, uncmem + offset,
|
|
|
|
bvec->bv_len);
|
|
|
|
kfree(uncmem);
|
|
|
|
}
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
kunmap_atomic(cmem, KM_USER1);
|
2011-07-13 15:20:05 +00:00
|
|
|
kunmap_atomic(user_mem, KM_USER0);
|
Staging: ramzswap: Support generic I/O requests
Currently, ramzwap devices (/dev/ramzswapX) can only
be used as swap disks since it was hard-coded to consider
only the first request in bio vector.
Now, we iterate over all the segments in an incoming
bio which allows us to handle all kinds of I/O requests.
ramzswap devices can still handle PAGE_SIZE aligned and
multiple of PAGE_SIZE sized I/O requests only. To ensure
that we get always get such requests only, we set following
request_queue attributes to PAGE_SIZE:
- physical_block_size
- logical_block_size
- io_min
- io_opt
Note: physical and logical block sizes were already set
equal to PAGE_SIZE and that seems to be sufficient to get
PAGE_SIZE aligned I/O.
Since we are no longer limited to handling swap requests
only, the next few patches rename ramzswap to zram. So,
the devices will then be called /dev/zram{0, 1, 2, ...}
Usage/Examples:
1) Use as /tmp storage
- mkfs.ext4 /dev/zram0
- mount /dev/zram0 /tmp
2) Use as swap:
- mkswap /dev/zram0
- swapon /dev/zram0 -p 10 # give highest priority to zram0
Performance:
- I/O benchamark done with 'dd' command. Details can be
found here:
http://code.google.com/p/compcache/wiki/zramperf
Summary:
- Maximum read speed (approx):
- ram disk: 1200 MB/sec
- zram disk: 600 MB/sec
- Maximum write speed (approx):
- ram disk: 500 MB/sec
- zram disk: 160 MB/sec
Issues:
- Double caching: We can potentially waste memory by having
two copies of a page -- one in page cache (uncompress) and
second in the device memory (compressed). However, during
reclaim, clean page cache pages are quickly freed, so this
does not seem to be a big problem.
- Stale data: Not all filesystems support issuing 'discard'
requests to underlying block devices. So, if such filesystems
are used over zram devices, we can accumulate lot of stale
data in memory. Even for filesystems to do support discard
(example, ext4), we need to see how effective it is.
- Scalability: There is only one (per-device) de/compression
buffer stats. This can lead to significant contention, especially
when used for generic (non-swap) purposes.
Signed-off-by: Nitin Gupta <ngupta@vflare.org>
Acked-by: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2010-06-01 08:01:23 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
/* 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;
|
Staging: ramzswap: Support generic I/O requests
Currently, ramzwap devices (/dev/ramzswapX) can only
be used as swap disks since it was hard-coded to consider
only the first request in bio vector.
Now, we iterate over all the segments in an incoming
bio which allows us to handle all kinds of I/O requests.
ramzswap devices can still handle PAGE_SIZE aligned and
multiple of PAGE_SIZE sized I/O requests only. To ensure
that we get always get such requests only, we set following
request_queue attributes to PAGE_SIZE:
- physical_block_size
- logical_block_size
- io_min
- io_opt
Note: physical and logical block sizes were already set
equal to PAGE_SIZE and that seems to be sufficient to get
PAGE_SIZE aligned I/O.
Since we are no longer limited to handling swap requests
only, the next few patches rename ramzswap to zram. So,
the devices will then be called /dev/zram{0, 1, 2, ...}
Usage/Examples:
1) Use as /tmp storage
- mkfs.ext4 /dev/zram0
- mount /dev/zram0 /tmp
2) Use as swap:
- mkswap /dev/zram0
- swapon /dev/zram0 -p 10 # give highest priority to zram0
Performance:
- I/O benchamark done with 'dd' command. Details can be
found here:
http://code.google.com/p/compcache/wiki/zramperf
Summary:
- Maximum read speed (approx):
- ram disk: 1200 MB/sec
- zram disk: 600 MB/sec
- Maximum write speed (approx):
- ram disk: 500 MB/sec
- zram disk: 160 MB/sec
Issues:
- Double caching: We can potentially waste memory by having
two copies of a page -- one in page cache (uncompress) and
second in the device memory (compressed). However, during
reclaim, clean page cache pages are quickly freed, so this
does not seem to be a big problem.
- Stale data: Not all filesystems support issuing 'discard'
requests to underlying block devices. So, if such filesystems
are used over zram devices, we can accumulate lot of stale
data in memory. Even for filesystems to do support discard
(example, ext4), we need to see how effective it is.
- Scalability: There is only one (per-device) de/compression
buffer stats. This can lead to significant contention, especially
when used for generic (non-swap) purposes.
Signed-off-by: Nitin Gupta <ngupta@vflare.org>
Acked-by: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2010-06-01 08:01:23 +00:00
|
|
|
}
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
flush_dcache_page(page);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
return 0;
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2011-06-10 13:28:48 +00:00
|
|
|
static int zram_read_before_write(struct zram *zram, char *mem, u32 index)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
size_t clen = PAGE_SIZE;
|
|
|
|
struct zobj_header *zheader;
|
|
|
|
unsigned char *cmem;
|
|
|
|
|
|
|
|
if (zram_test_flag(zram, index, ZRAM_ZERO) ||
|
|
|
|
!zram->table[index].page) {
|
|
|
|
memset(mem, 0, PAGE_SIZE);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmem = kmap_atomic(zram->table[index].page, KM_USER0) +
|
|
|
|
zram->table[index].offset;
|
|
|
|
|
|
|
|
/* Page is stored uncompressed since it's incompressible */
|
|
|
|
if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED))) {
|
|
|
|
memcpy(mem, cmem, PAGE_SIZE);
|
|
|
|
kunmap_atomic(cmem, KM_USER0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = lzo1x_decompress_safe(cmem + sizeof(*zheader),
|
|
|
|
xv_get_object_size(cmem) - sizeof(*zheader),
|
|
|
|
mem, &clen);
|
|
|
|
kunmap_atomic(cmem, KM_USER0);
|
|
|
|
|
|
|
|
/* 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_write(struct zram *zram, struct bio_vec *bvec, u32 index,
|
|
|
|
int offset)
|
2009-09-22 04:56:53 +00:00
|
|
|
{
|
2011-06-10 13:28:47 +00:00
|
|
|
int ret;
|
2011-06-10 13:28:48 +00:00
|
|
|
u32 store_offset;
|
2011-06-10 13:28:47 +00:00
|
|
|
size_t clen;
|
|
|
|
struct zobj_header *zheader;
|
|
|
|
struct page *page, *page_store;
|
2011-06-10 13:28:48 +00:00
|
|
|
unsigned char *user_mem, *cmem, *src, *uncmem = NULL;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
page = bvec->bv_page;
|
|
|
|
src = zram->compress_buffer;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:48 +00:00
|
|
|
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_KERNEL);
|
|
|
|
if (!uncmem) {
|
|
|
|
pr_info("Error allocating temp memory!\n");
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ret = zram_read_before_write(zram, uncmem, index);
|
|
|
|
if (ret) {
|
|
|
|
kfree(uncmem);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
/*
|
|
|
|
* System overwrites unused sectors. Free memory associated
|
|
|
|
* with this sector now.
|
|
|
|
*/
|
|
|
|
if (zram->table[index].page ||
|
|
|
|
zram_test_flag(zram, index, ZRAM_ZERO))
|
|
|
|
zram_free_page(zram, index);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
user_mem = kmap_atomic(page, KM_USER0);
|
2011-06-10 13:28:48 +00:00
|
|
|
|
|
|
|
if (is_partial_io(bvec))
|
|
|
|
memcpy(uncmem + offset, user_mem + bvec->bv_offset,
|
|
|
|
bvec->bv_len);
|
|
|
|
else
|
|
|
|
uncmem = user_mem;
|
|
|
|
|
|
|
|
if (page_zero_filled(uncmem)) {
|
2011-06-10 13:28:47 +00:00
|
|
|
kunmap_atomic(user_mem, KM_USER0);
|
2011-06-10 13:28:48 +00:00
|
|
|
if (is_partial_io(bvec))
|
|
|
|
kfree(uncmem);
|
2011-06-10 13:28:47 +00:00
|
|
|
zram_stat_inc(&zram->stats.pages_zero);
|
|
|
|
zram_set_flag(zram, index, ZRAM_ZERO);
|
2011-06-10 13:28:48 +00:00
|
|
|
ret = 0;
|
|
|
|
goto out;
|
2011-06-10 13:28:47 +00:00
|
|
|
}
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:48 +00:00
|
|
|
ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen,
|
2011-06-10 13:28:47 +00:00
|
|
|
zram->compress_workmem);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
kunmap_atomic(user_mem, KM_USER0);
|
2011-06-10 13:28:48 +00:00
|
|
|
if (is_partial_io(bvec))
|
|
|
|
kfree(uncmem);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
if (unlikely(ret != LZO_E_OK)) {
|
|
|
|
pr_err("Compression failed! err=%d\n", ret);
|
2011-06-10 13:28:48 +00:00
|
|
|
goto out;
|
2011-06-10 13:28:47 +00:00
|
|
|
}
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
/*
|
|
|
|
* Page is incompressible. Store it as-is (uncompressed)
|
|
|
|
* since we do not want to return too many disk write
|
|
|
|
* errors which has side effect of hanging the system.
|
|
|
|
*/
|
|
|
|
if (unlikely(clen > max_zpage_size)) {
|
|
|
|
clen = PAGE_SIZE;
|
|
|
|
page_store = alloc_page(GFP_NOIO | __GFP_HIGHMEM);
|
|
|
|
if (unlikely(!page_store)) {
|
|
|
|
pr_info("Error allocating memory for "
|
|
|
|
"incompressible page: %u\n", index);
|
2011-06-10 13:28:48 +00:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
Staging: ramzswap: Support generic I/O requests
Currently, ramzwap devices (/dev/ramzswapX) can only
be used as swap disks since it was hard-coded to consider
only the first request in bio vector.
Now, we iterate over all the segments in an incoming
bio which allows us to handle all kinds of I/O requests.
ramzswap devices can still handle PAGE_SIZE aligned and
multiple of PAGE_SIZE sized I/O requests only. To ensure
that we get always get such requests only, we set following
request_queue attributes to PAGE_SIZE:
- physical_block_size
- logical_block_size
- io_min
- io_opt
Note: physical and logical block sizes were already set
equal to PAGE_SIZE and that seems to be sufficient to get
PAGE_SIZE aligned I/O.
Since we are no longer limited to handling swap requests
only, the next few patches rename ramzswap to zram. So,
the devices will then be called /dev/zram{0, 1, 2, ...}
Usage/Examples:
1) Use as /tmp storage
- mkfs.ext4 /dev/zram0
- mount /dev/zram0 /tmp
2) Use as swap:
- mkswap /dev/zram0
- swapon /dev/zram0 -p 10 # give highest priority to zram0
Performance:
- I/O benchamark done with 'dd' command. Details can be
found here:
http://code.google.com/p/compcache/wiki/zramperf
Summary:
- Maximum read speed (approx):
- ram disk: 1200 MB/sec
- zram disk: 600 MB/sec
- Maximum write speed (approx):
- ram disk: 500 MB/sec
- zram disk: 160 MB/sec
Issues:
- Double caching: We can potentially waste memory by having
two copies of a page -- one in page cache (uncompress) and
second in the device memory (compressed). However, during
reclaim, clean page cache pages are quickly freed, so this
does not seem to be a big problem.
- Stale data: Not all filesystems support issuing 'discard'
requests to underlying block devices. So, if such filesystems
are used over zram devices, we can accumulate lot of stale
data in memory. Even for filesystems to do support discard
(example, ext4), we need to see how effective it is.
- Scalability: There is only one (per-device) de/compression
buffer stats. This can lead to significant contention, especially
when used for generic (non-swap) purposes.
Signed-off-by: Nitin Gupta <ngupta@vflare.org>
Acked-by: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2010-06-01 08:01:23 +00:00
|
|
|
|
2011-06-10 13:28:48 +00:00
|
|
|
store_offset = 0;
|
2011-06-10 13:28:47 +00:00
|
|
|
zram_set_flag(zram, index, ZRAM_UNCOMPRESSED);
|
|
|
|
zram_stat_inc(&zram->stats.pages_expand);
|
|
|
|
zram->table[index].page = page_store;
|
|
|
|
src = kmap_atomic(page, KM_USER0);
|
|
|
|
goto memstore;
|
|
|
|
}
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
if (xv_malloc(zram->mem_pool, clen + sizeof(*zheader),
|
2011-06-10 13:28:48 +00:00
|
|
|
&zram->table[index].page, &store_offset,
|
2011-06-10 13:28:47 +00:00
|
|
|
GFP_NOIO | __GFP_HIGHMEM)) {
|
|
|
|
pr_info("Error allocating memory for compressed "
|
|
|
|
"page: %u, size=%zu\n", index, clen);
|
2011-06-10 13:28:48 +00:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
2011-06-10 13:28:47 +00:00
|
|
|
}
|
2009-09-22 04:56:53 +00:00
|
|
|
|
|
|
|
memstore:
|
2011-06-10 13:28:48 +00:00
|
|
|
zram->table[index].offset = store_offset;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
cmem = kmap_atomic(zram->table[index].page, KM_USER1) +
|
|
|
|
zram->table[index].offset;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
|
|
|
#if 0
|
2011-06-10 13:28:47 +00:00
|
|
|
/* Back-reference needed for memory defragmentation */
|
|
|
|
if (!zram_test_flag(zram, index, ZRAM_UNCOMPRESSED)) {
|
|
|
|
zheader = (struct zobj_header *)cmem;
|
|
|
|
zheader->table_idx = index;
|
|
|
|
cmem += sizeof(*zheader);
|
|
|
|
}
|
2009-09-22 04:56:53 +00:00
|
|
|
#endif
|
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
memcpy(cmem, src, clen);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
kunmap_atomic(cmem, KM_USER1);
|
|
|
|
if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED)))
|
|
|
|
kunmap_atomic(src, KM_USER0);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
/* Update stats */
|
|
|
|
zram_stat64_add(zram, &zram->stats.compr_size, clen);
|
|
|
|
zram_stat_inc(&zram->stats.pages_stored);
|
|
|
|
if (clen <= PAGE_SIZE / 2)
|
|
|
|
zram_stat_inc(&zram->stats.good_compress);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
return 0;
|
2011-06-10 13:28:48 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
if (ret)
|
|
|
|
zram_stat64_inc(zram, &zram->stats.failed_writes);
|
|
|
|
return ret;
|
2011-06-10 13:28:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index,
|
2011-06-10 13:28:48 +00:00
|
|
|
int offset, struct bio *bio, int rw)
|
2011-06-10 13:28:47 +00:00
|
|
|
{
|
2011-06-10 13:28:49 +00:00
|
|
|
int ret;
|
2011-06-10 13:28:47 +00:00
|
|
|
|
2011-06-10 13:28:49 +00:00
|
|
|
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;
|
2011-06-10 13:28:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2011-06-10 13:28:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __zram_make_request(struct zram *zram, struct bio *bio, int rw)
|
|
|
|
{
|
2011-06-10 13:28:48 +00:00
|
|
|
int i, offset;
|
2011-06-10 13:28:47 +00:00
|
|
|
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;
|
2011-06-10 13:28:48 +00:00
|
|
|
offset = (bio->bi_sector & (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT;
|
2011-06-10 13:28:47 +00:00
|
|
|
|
|
|
|
bio_for_each_segment(bvec, bio, i) {
|
2011-06-10 13:28:48 +00:00
|
|
|
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);
|
Staging: ramzswap: Support generic I/O requests
Currently, ramzwap devices (/dev/ramzswapX) can only
be used as swap disks since it was hard-coded to consider
only the first request in bio vector.
Now, we iterate over all the segments in an incoming
bio which allows us to handle all kinds of I/O requests.
ramzswap devices can still handle PAGE_SIZE aligned and
multiple of PAGE_SIZE sized I/O requests only. To ensure
that we get always get such requests only, we set following
request_queue attributes to PAGE_SIZE:
- physical_block_size
- logical_block_size
- io_min
- io_opt
Note: physical and logical block sizes were already set
equal to PAGE_SIZE and that seems to be sufficient to get
PAGE_SIZE aligned I/O.
Since we are no longer limited to handling swap requests
only, the next few patches rename ramzswap to zram. So,
the devices will then be called /dev/zram{0, 1, 2, ...}
Usage/Examples:
1) Use as /tmp storage
- mkfs.ext4 /dev/zram0
- mount /dev/zram0 /tmp
2) Use as swap:
- mkswap /dev/zram0
- swapon /dev/zram0 -p 10 # give highest priority to zram0
Performance:
- I/O benchamark done with 'dd' command. Details can be
found here:
http://code.google.com/p/compcache/wiki/zramperf
Summary:
- Maximum read speed (approx):
- ram disk: 1200 MB/sec
- zram disk: 600 MB/sec
- Maximum write speed (approx):
- ram disk: 500 MB/sec
- zram disk: 160 MB/sec
Issues:
- Double caching: We can potentially waste memory by having
two copies of a page -- one in page cache (uncompress) and
second in the device memory (compressed). However, during
reclaim, clean page cache pages are quickly freed, so this
does not seem to be a big problem.
- Stale data: Not all filesystems support issuing 'discard'
requests to underlying block devices. So, if such filesystems
are used over zram devices, we can accumulate lot of stale
data in memory. Even for filesystems to do support discard
(example, ext4), we need to see how effective it is.
- Scalability: There is only one (per-device) de/compression
buffer stats. This can lead to significant contention, especially
when used for generic (non-swap) purposes.
Signed-off-by: Nitin Gupta <ngupta@vflare.org>
Acked-by: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2010-06-01 08:01:23 +00:00
|
|
|
}
|
2009-09-22 04:56:53 +00:00
|
|
|
|
|
|
|
set_bit(BIO_UPTODATE, &bio->bi_flags);
|
|
|
|
bio_endio(bio, 0);
|
2011-01-22 12:36:15 +00:00
|
|
|
return;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
bio_io_error(bio);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-06-10 13:28:48 +00:00
|
|
|
* Check if request is within bounds and aligned on zram logical blocks.
|
2009-09-22 04:56:53 +00:00
|
|
|
*/
|
2010-06-01 08:01:25 +00:00
|
|
|
static inline int valid_io_request(struct zram *zram, struct bio *bio)
|
2009-09-22 04:56:53 +00:00
|
|
|
{
|
|
|
|
if (unlikely(
|
2010-06-01 08:01:25 +00:00
|
|
|
(bio->bi_sector >= (zram->disksize >> SECTOR_SHIFT)) ||
|
2011-06-10 13:28:48 +00:00
|
|
|
(bio->bi_sector & (ZRAM_SECTOR_PER_LOGICAL_BLOCK - 1)) ||
|
|
|
|
(bio->bi_size & (ZRAM_LOGICAL_BLOCK_SIZE - 1)))) {
|
2009-09-22 04:56:53 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Staging: ramzswap: Support generic I/O requests
Currently, ramzwap devices (/dev/ramzswapX) can only
be used as swap disks since it was hard-coded to consider
only the first request in bio vector.
Now, we iterate over all the segments in an incoming
bio which allows us to handle all kinds of I/O requests.
ramzswap devices can still handle PAGE_SIZE aligned and
multiple of PAGE_SIZE sized I/O requests only. To ensure
that we get always get such requests only, we set following
request_queue attributes to PAGE_SIZE:
- physical_block_size
- logical_block_size
- io_min
- io_opt
Note: physical and logical block sizes were already set
equal to PAGE_SIZE and that seems to be sufficient to get
PAGE_SIZE aligned I/O.
Since we are no longer limited to handling swap requests
only, the next few patches rename ramzswap to zram. So,
the devices will then be called /dev/zram{0, 1, 2, ...}
Usage/Examples:
1) Use as /tmp storage
- mkfs.ext4 /dev/zram0
- mount /dev/zram0 /tmp
2) Use as swap:
- mkswap /dev/zram0
- swapon /dev/zram0 -p 10 # give highest priority to zram0
Performance:
- I/O benchamark done with 'dd' command. Details can be
found here:
http://code.google.com/p/compcache/wiki/zramperf
Summary:
- Maximum read speed (approx):
- ram disk: 1200 MB/sec
- zram disk: 600 MB/sec
- Maximum write speed (approx):
- ram disk: 500 MB/sec
- zram disk: 160 MB/sec
Issues:
- Double caching: We can potentially waste memory by having
two copies of a page -- one in page cache (uncompress) and
second in the device memory (compressed). However, during
reclaim, clean page cache pages are quickly freed, so this
does not seem to be a big problem.
- Stale data: Not all filesystems support issuing 'discard'
requests to underlying block devices. So, if such filesystems
are used over zram devices, we can accumulate lot of stale
data in memory. Even for filesystems to do support discard
(example, ext4), we need to see how effective it is.
- Scalability: There is only one (per-device) de/compression
buffer stats. This can lead to significant contention, especially
when used for generic (non-swap) purposes.
Signed-off-by: Nitin Gupta <ngupta@vflare.org>
Acked-by: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2010-06-01 08:01:23 +00:00
|
|
|
/* I/O request is valid */
|
2009-09-22 04:56:53 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-06-01 08:01:25 +00:00
|
|
|
* Handler function for all zram I/O requests.
|
2009-09-22 04:56:53 +00:00
|
|
|
*/
|
2010-06-01 08:01:25 +00:00
|
|
|
static int zram_make_request(struct request_queue *queue, struct bio *bio)
|
2009-09-22 04:56:53 +00:00
|
|
|
{
|
2010-06-01 08:01:25 +00:00
|
|
|
struct zram *zram = queue->queuedata;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
if (!valid_io_request(zram, bio)) {
|
|
|
|
zram_stat64_inc(zram, &zram->stats.invalid_io);
|
2009-09-22 04:56:53 +00:00
|
|
|
bio_io_error(bio);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-17 16:11:49 +00:00
|
|
|
if (unlikely(!zram->init_done) && zram_init_device(zram)) {
|
|
|
|
bio_io_error(bio);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-06-10 13:28:47 +00:00
|
|
|
__zram_make_request(zram, bio, bio_data_dir(bio));
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-01-22 12:36:15 +00:00
|
|
|
return 0;
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2010-08-09 17:26:47 +00:00
|
|
|
void zram_reset_device(struct zram *zram)
|
2009-09-22 04:56:53 +00:00
|
|
|
{
|
2010-05-13 08:54:21 +00:00
|
|
|
size_t index;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2010-08-09 17:26:48 +00:00
|
|
|
mutex_lock(&zram->init_lock);
|
2010-06-01 08:01:25 +00:00
|
|
|
zram->init_done = 0;
|
2010-01-28 15:43:38 +00:00
|
|
|
|
2009-09-22 04:56:53 +00:00
|
|
|
/* Free various per-device buffers */
|
2010-06-01 08:01:25 +00:00
|
|
|
kfree(zram->compress_workmem);
|
|
|
|
free_pages((unsigned long)zram->compress_buffer, 1);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
zram->compress_workmem = NULL;
|
|
|
|
zram->compress_buffer = NULL;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
/* Free all pages that are still in this zram device */
|
|
|
|
for (index = 0; index < zram->disksize >> PAGE_SHIFT; index++) {
|
2009-09-22 04:56:53 +00:00
|
|
|
struct page *page;
|
|
|
|
u16 offset;
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
page = zram->table[index].page;
|
|
|
|
offset = zram->table[index].offset;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
|
|
|
if (!page)
|
|
|
|
continue;
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
if (unlikely(zram_test_flag(zram, index, ZRAM_UNCOMPRESSED)))
|
2009-09-22 04:56:53 +00:00
|
|
|
__free_page(page);
|
|
|
|
else
|
2010-06-01 08:01:25 +00:00
|
|
|
xv_free(zram->mem_pool, page, offset);
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
vfree(zram->table);
|
|
|
|
zram->table = NULL;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
xv_destroy_pool(zram->mem_pool);
|
|
|
|
zram->mem_pool = NULL;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
|
|
|
/* Reset stats */
|
2010-06-01 08:01:25 +00:00
|
|
|
memset(&zram->stats, 0, sizeof(zram->stats));
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
zram->disksize = 0;
|
2010-08-09 17:26:48 +00:00
|
|
|
mutex_unlock(&zram->init_lock);
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2010-08-09 17:26:47 +00:00
|
|
|
int zram_init_device(struct zram *zram)
|
2009-09-22 04:56:53 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
size_t num_pages;
|
|
|
|
|
2010-08-09 17:26:48 +00:00
|
|
|
mutex_lock(&zram->init_lock);
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
if (zram->init_done) {
|
2010-08-09 17:26:48 +00:00
|
|
|
mutex_unlock(&zram->init_lock);
|
|
|
|
return 0;
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
zram_set_disksize(zram, totalram_pages << PAGE_SHIFT);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
zram->compress_workmem = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
|
|
|
|
if (!zram->compress_workmem) {
|
2009-09-22 04:56:53 +00:00
|
|
|
pr_err("Error allocating compressor working memory!\n");
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
zram->compress_buffer = (void *)__get_free_pages(__GFP_ZERO, 1);
|
|
|
|
if (!zram->compress_buffer) {
|
2009-09-22 04:56:53 +00:00
|
|
|
pr_err("Error allocating compressor buffer space\n");
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
num_pages = zram->disksize >> PAGE_SHIFT;
|
2010-11-05 03:07:59 +00:00
|
|
|
zram->table = vzalloc(num_pages * sizeof(*zram->table));
|
2010-06-01 08:01:25 +00:00
|
|
|
if (!zram->table) {
|
|
|
|
pr_err("Error allocating zram address table\n");
|
2009-09-22 04:56:53 +00:00
|
|
|
/* To prevent accessing table entries during cleanup */
|
2010-06-01 08:01:25 +00:00
|
|
|
zram->disksize = 0;
|
2009-09-22 04:56:53 +00:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
/* zram devices sort of resembles non-rotational disks */
|
|
|
|
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
zram->mem_pool = xv_create_pool();
|
|
|
|
if (!zram->mem_pool) {
|
2009-09-22 04:56:53 +00:00
|
|
|
pr_err("Error creating memory pool\n");
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
zram->init_done = 1;
|
2010-08-09 17:26:48 +00:00
|
|
|
mutex_unlock(&zram->init_lock);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
|
|
|
pr_debug("Initialization done!\n");
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
2010-08-09 17:26:48 +00:00
|
|
|
mutex_unlock(&zram->init_lock);
|
2010-08-09 17:26:47 +00:00
|
|
|
zram_reset_device(zram);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
|
|
|
pr_err("Initialization failed: err=%d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
void zram_slot_free_notify(struct block_device *bdev, unsigned long index)
|
2010-05-17 05:32:44 +00:00
|
|
|
{
|
2010-06-01 08:01:25 +00:00
|
|
|
struct zram *zram;
|
2010-05-17 05:32:44 +00:00
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
zram = bdev->bd_disk->private_data;
|
|
|
|
zram_free_page(zram, index);
|
|
|
|
zram_stat64_inc(zram, &zram->stats.notify_free);
|
2010-05-17 05:32:44 +00:00
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
static const struct block_device_operations zram_devops = {
|
|
|
|
.swap_slot_free_notify = zram_slot_free_notify,
|
2010-05-17 05:32:44 +00:00
|
|
|
.owner = THIS_MODULE
|
2009-09-22 04:56:53 +00:00
|
|
|
};
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
static int create_device(struct zram *zram, int device_id)
|
2009-09-22 04:56:53 +00:00
|
|
|
{
|
2010-01-28 15:43:40 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
2011-06-10 13:28:49 +00:00
|
|
|
init_rwsem(&zram->lock);
|
2010-08-09 17:26:48 +00:00
|
|
|
mutex_init(&zram->init_lock);
|
2010-06-01 08:01:25 +00:00
|
|
|
spin_lock_init(&zram->stat64_lock);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
zram->queue = blk_alloc_queue(GFP_KERNEL);
|
|
|
|
if (!zram->queue) {
|
2009-09-22 04:56:53 +00:00
|
|
|
pr_err("Error allocating disk queue for device %d\n",
|
|
|
|
device_id);
|
2010-01-28 15:43:40 +00:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
blk_queue_make_request(zram->queue, zram_make_request);
|
|
|
|
zram->queue->queuedata = zram;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
|
|
|
/* gendisk structure */
|
2010-06-01 08:01:25 +00:00
|
|
|
zram->disk = alloc_disk(1);
|
|
|
|
if (!zram->disk) {
|
|
|
|
blk_cleanup_queue(zram->queue);
|
2009-09-22 04:56:53 +00:00
|
|
|
pr_warning("Error allocating disk structure for device %d\n",
|
|
|
|
device_id);
|
2010-01-28 15:43:40 +00:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
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);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2010-08-09 17:26:47 +00:00
|
|
|
/* Actual capacity set using syfs (/sys/block/zram<id>/disksize */
|
2010-06-01 08:01:25 +00:00
|
|
|
set_capacity(zram->disk, 0);
|
2010-01-28 15:43:39 +00:00
|
|
|
|
Staging: ramzswap: Support generic I/O requests
Currently, ramzwap devices (/dev/ramzswapX) can only
be used as swap disks since it was hard-coded to consider
only the first request in bio vector.
Now, we iterate over all the segments in an incoming
bio which allows us to handle all kinds of I/O requests.
ramzswap devices can still handle PAGE_SIZE aligned and
multiple of PAGE_SIZE sized I/O requests only. To ensure
that we get always get such requests only, we set following
request_queue attributes to PAGE_SIZE:
- physical_block_size
- logical_block_size
- io_min
- io_opt
Note: physical and logical block sizes were already set
equal to PAGE_SIZE and that seems to be sufficient to get
PAGE_SIZE aligned I/O.
Since we are no longer limited to handling swap requests
only, the next few patches rename ramzswap to zram. So,
the devices will then be called /dev/zram{0, 1, 2, ...}
Usage/Examples:
1) Use as /tmp storage
- mkfs.ext4 /dev/zram0
- mount /dev/zram0 /tmp
2) Use as swap:
- mkswap /dev/zram0
- swapon /dev/zram0 -p 10 # give highest priority to zram0
Performance:
- I/O benchamark done with 'dd' command. Details can be
found here:
http://code.google.com/p/compcache/wiki/zramperf
Summary:
- Maximum read speed (approx):
- ram disk: 1200 MB/sec
- zram disk: 600 MB/sec
- Maximum write speed (approx):
- ram disk: 500 MB/sec
- zram disk: 160 MB/sec
Issues:
- Double caching: We can potentially waste memory by having
two copies of a page -- one in page cache (uncompress) and
second in the device memory (compressed). However, during
reclaim, clean page cache pages are quickly freed, so this
does not seem to be a big problem.
- Stale data: Not all filesystems support issuing 'discard'
requests to underlying block devices. So, if such filesystems
are used over zram devices, we can accumulate lot of stale
data in memory. Even for filesystems to do support discard
(example, ext4), we need to see how effective it is.
- Scalability: There is only one (per-device) de/compression
buffer stats. This can lead to significant contention, especially
when used for generic (non-swap) purposes.
Signed-off-by: Nitin Gupta <ngupta@vflare.org>
Acked-by: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2010-06-01 08:01:23 +00:00
|
|
|
/*
|
|
|
|
* To ensure that we always get PAGE_SIZE aligned
|
|
|
|
* and n*PAGE_SIZED sized I/O requests.
|
|
|
|
*/
|
2010-06-01 08:01:25 +00:00
|
|
|
blk_queue_physical_block_size(zram->disk->queue, PAGE_SIZE);
|
2011-01-28 14:58:17 +00:00
|
|
|
blk_queue_logical_block_size(zram->disk->queue,
|
|
|
|
ZRAM_LOGICAL_BLOCK_SIZE);
|
2010-06-01 08:01:25 +00:00
|
|
|
blk_queue_io_min(zram->disk->queue, PAGE_SIZE);
|
|
|
|
blk_queue_io_opt(zram->disk->queue, PAGE_SIZE);
|
2010-01-28 15:43:39 +00:00
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
add_disk(zram->disk);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2010-08-09 17:26:47 +00:00
|
|
|
ret = sysfs_create_group(&disk_to_dev(zram->disk)->kobj,
|
|
|
|
&zram_disk_attr_group);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_warning("Error creating sysfs group");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
zram->init_done = 0;
|
2010-01-28 15:43:40 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
static void destroy_device(struct zram *zram)
|
2009-09-22 04:56:53 +00:00
|
|
|
{
|
2010-08-09 17:26:47 +00:00
|
|
|
sysfs_remove_group(&disk_to_dev(zram->disk)->kobj,
|
|
|
|
&zram_disk_attr_group);
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
if (zram->disk) {
|
|
|
|
del_gendisk(zram->disk);
|
|
|
|
put_disk(zram->disk);
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
if (zram->queue)
|
|
|
|
blk_cleanup_queue(zram->queue);
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
static int __init zram_init(void)
|
2009-09-22 04:56:53 +00:00
|
|
|
{
|
2010-01-28 15:43:40 +00:00
|
|
|
int ret, dev_id;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
|
|
|
if (num_devices > max_num_devices) {
|
|
|
|
pr_warning("Invalid value for num_devices: %u\n",
|
|
|
|
num_devices);
|
2010-01-28 15:43:40 +00:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
zram_major = register_blkdev(0, "zram");
|
|
|
|
if (zram_major <= 0) {
|
2009-09-22 04:56:53 +00:00
|
|
|
pr_warning("Unable to get major number\n");
|
2010-01-28 15:43:40 +00:00
|
|
|
ret = -EBUSY;
|
|
|
|
goto out;
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!num_devices) {
|
|
|
|
pr_info("num_devices not specified. Using default: 1\n");
|
|
|
|
num_devices = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate the device array and initialize each one */
|
|
|
|
pr_info("Creating %u devices ...\n", num_devices);
|
2011-07-20 23:05:57 +00:00
|
|
|
zram_devices = kzalloc(num_devices * sizeof(struct zram), GFP_KERNEL);
|
|
|
|
if (!zram_devices) {
|
2010-01-28 15:43:40 +00:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto unregister;
|
|
|
|
}
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2010-01-28 15:43:40 +00:00
|
|
|
for (dev_id = 0; dev_id < num_devices; dev_id++) {
|
2011-07-20 23:05:57 +00:00
|
|
|
ret = create_device(&zram_devices[dev_id], dev_id);
|
2010-01-28 15:43:40 +00:00
|
|
|
if (ret)
|
2010-01-11 07:15:53 +00:00
|
|
|
goto free_devices;
|
2010-01-28 15:43:40 +00:00
|
|
|
}
|
|
|
|
|
2009-09-22 04:56:53 +00:00
|
|
|
return 0;
|
2010-01-28 15:43:40 +00:00
|
|
|
|
2010-01-11 07:15:53 +00:00
|
|
|
free_devices:
|
2010-01-28 15:43:40 +00:00
|
|
|
while (dev_id)
|
2011-07-20 23:05:57 +00:00
|
|
|
destroy_device(&zram_devices[--dev_id]);
|
|
|
|
kfree(zram_devices);
|
2010-01-28 15:43:40 +00:00
|
|
|
unregister:
|
2010-06-01 08:01:25 +00:00
|
|
|
unregister_blkdev(zram_major, "zram");
|
2010-01-28 15:43:40 +00:00
|
|
|
out:
|
2009-09-22 04:56:53 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
static void __exit zram_exit(void)
|
2009-09-22 04:56:53 +00:00
|
|
|
{
|
|
|
|
int i;
|
2010-06-01 08:01:25 +00:00
|
|
|
struct zram *zram;
|
2009-09-22 04:56:53 +00:00
|
|
|
|
|
|
|
for (i = 0; i < num_devices; i++) {
|
2011-07-20 23:05:57 +00:00
|
|
|
zram = &zram_devices[i];
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
destroy_device(zram);
|
|
|
|
if (zram->init_done)
|
2010-08-09 17:26:47 +00:00
|
|
|
zram_reset_device(zram);
|
2009-09-22 04:56:53 +00:00
|
|
|
}
|
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
unregister_blkdev(zram_major, "zram");
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2011-07-20 23:05:57 +00:00
|
|
|
kfree(zram_devices);
|
2009-09-22 04:56:53 +00:00
|
|
|
pr_debug("Cleanup done!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
module_param(num_devices, uint, 0);
|
2010-06-01 08:01:25 +00:00
|
|
|
MODULE_PARM_DESC(num_devices, "Number of zram devices");
|
2009-09-22 04:56:53 +00:00
|
|
|
|
2010-06-01 08:01:25 +00:00
|
|
|
module_init(zram_init);
|
|
|
|
module_exit(zram_exit);
|
2009-09-22 04:56:53 +00:00
|
|
|
|
|
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
|
|
|
MODULE_AUTHOR("Nitin Gupta <ngupta@vflare.org>");
|
2010-06-01 08:01:25 +00:00
|
|
|
MODULE_DESCRIPTION("Compressed RAM Block Device");
|