linux/drivers/staging/zram/zram_drv.c

777 lines
17 KiB
C
Raw Normal View History

/*
* 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 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>
#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;
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);
}
static int zram_test_flag(struct zram *zram, u32 index,
enum zram_pageflags flag)
{
return zram->table[index].flags & BIT(flag);
}
static void zram_set_flag(struct zram *zram, u32 index,
enum zram_pageflags flag)
{
zram->table[index].flags |= BIT(flag);
}
static void zram_clear_flag(struct zram *zram, u32 index,
enum zram_pageflags flag)
{
zram->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_set_disksize(struct zram *zram, size_t totalram_bytes)
{
if (!zram->disksize) {
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
);
zram->disksize = default_disksize_perc_ram *
(totalram_bytes / 100);
}
if (zram->disksize > 2 * (totalram_bytes)) {
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: %zu kB\n"
"\tSize you selected: %llu kB\n"
"Continuing anyway ...\n",
totalram_bytes >> 10, zram->disksize
);
}
zram->disksize &= PAGE_MASK;
}
static void zram_free_page(struct zram *zram, size_t index)
{
unsigned long handle = zram->table[index].handle;
u16 size = zram->table[index].size;
if (unlikely(!handle)) {
/*
* No memory is allocated for zero filled pages.
* Simply clear zero page flag.
*/
if (zram_test_flag(zram, index, ZRAM_ZERO)) {
zram_clear_flag(zram, index, ZRAM_ZERO);
zram_stat_dec(&zram->stats.pages_zero);
}
return;
}
if (unlikely(size > max_zpage_size))
zram_stat_dec(&zram->stats.bad_compress);
zs_free(zram->mem_pool, handle);
if (size <= PAGE_SIZE / 2)
zram_stat_dec(&zram->stats.good_compress);
zram_stat64_sub(zram, &zram->stats.compr_size,
zram->table[index].size);
zram_stat_dec(&zram->stats.pages_stored);
zram->table[index].handle = 0;
zram->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;
unsigned long handle = zram->table[index].handle;
if (!handle || zram_test_flag(zram, index, ZRAM_ZERO)) {
memset(mem, 0, PAGE_SIZE);
return 0;
}
cmem = zs_map_object(zram->mem_pool, handle, ZS_MM_RO);
if (zram->table[index].size == PAGE_SIZE)
memcpy(mem, cmem, PAGE_SIZE);
else
ret = lzo1x_decompress_safe(cmem, zram->table[index].size,
mem, &clen);
zs_unmap_object(zram->mem_pool, handle);
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
/* 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
}
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;
page = bvec->bv_page;
if (unlikely(!zram->table[index].handle) ||
zram_test_flag(zram, index, ZRAM_ZERO)) {
handle_zero_page(bvec);
return 0;
}
user_mem = kmap_atomic(page);
if (is_partial_io(bvec))
/* Use a temporary buffer to decompress the page */
uncmem = kmalloc(PAGE_SIZE, GFP_KERNEL);
else
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)) {
pr_err("Decompression failed! err=%d, page=%u\n", ret, index);
zram_stat64_inc(zram, &zram->stats.failed_reads);
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;
size_t clen;
unsigned long handle;
struct page *page;
unsigned char *user_mem, *cmem, *src, *uncmem = NULL;
page = bvec->bv_page;
src = zram->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_KERNEL);
if (!uncmem) {
pr_info("Error allocating temp memory!\n");
ret = -ENOMEM;
goto out;
}
ret = zram_decompress_page(zram, uncmem, index);
if (ret) {
kfree(uncmem);
goto out;
}
}
/*
* System overwrites unused sectors. Free memory associated
* with this sector now.
*/
if (zram->table[index].handle ||
zram_test_flag(zram, 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);
else
uncmem = user_mem;
if (page_zero_filled(uncmem)) {
kunmap_atomic(user_mem);
if (is_partial_io(bvec))
kfree(uncmem);
zram_stat_inc(&zram->stats.pages_zero);
zram_set_flag(zram, index, ZRAM_ZERO);
ret = 0;
goto out;
}
ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen,
zram->compress_workmem);
kunmap_atomic(user_mem);
if (is_partial_io(bvec))
kfree(uncmem);
if (unlikely(ret != LZO_E_OK)) {
pr_err("Compression failed! err=%d\n", ret);
goto out;
}
if (unlikely(clen > max_zpage_size)) {
zram_stat_inc(&zram->stats.bad_compress);
src = uncmem;
clen = PAGE_SIZE;
}
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
handle = zs_malloc(zram->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(zram->mem_pool, handle, ZS_MM_WO);
memcpy(cmem, src, clen);
zs_unmap_object(zram->mem_pool, handle);
zram->table[index].handle = handle;
zram->table[index].size = clen;
/* 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);
return 0;
out:
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);
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
}
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)
{
if (unlikely(
(bio->bi_sector >= (zram->disksize >> SECTOR_SHIFT)) ||
(bio->bi_sector & (ZRAM_SECTOR_PER_LOGICAL_BLOCK - 1)) ||
(bio->bi_size & (ZRAM_LOGICAL_BLOCK_SIZE - 1)))) {
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 */
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;
if (unlikely(!zram->init_done) && zram_init_device(zram))
goto error;
down_read(&zram->init_lock);
if (unlikely(!zram->init_done))
goto error_unlock;
if (!valid_io_request(zram, bio)) {
zram_stat64_inc(zram, &zram->stats.invalid_io);
goto error_unlock;
}
__zram_make_request(zram, bio, bio_data_dir(bio));
up_read(&zram->init_lock);
Merge branch 'for-3.2/core' of git://git.kernel.dk/linux-block * 'for-3.2/core' of git://git.kernel.dk/linux-block: (29 commits) block: don't call blk_drain_queue() if elevator is not up blk-throttle: use queue_is_locked() instead of lockdep_is_held() blk-throttle: Take blkcg->lock while traversing blkcg->policy_list blk-throttle: Free up policy node associated with deleted rule block: warn if tag is greater than real_max_depth. block: make gendisk hold a reference to its queue blk-flush: move the queue kick into blk-flush: fix invalid BUG_ON in blk_insert_flush block: Remove the control of complete cpu from bio. block: fix a typo in the blk-cgroup.h file block: initialize the bounce pool if high memory may be added later block: fix request_queue lifetime handling by making blk_queue_cleanup() properly shutdown block: drop @tsk from attempt_plug_merge() and explain sync rules block: make get_request[_wait]() fail if queue is dead block: reorganize throtl_get_tg() and blk_throtl_bio() block: reorganize queue draining block: drop unnecessary blk_get/put_queue() in scsi_cmd_ioctl() and blk_get_tg() block: pass around REQ_* flags instead of broken down booleans during request alloc/free block: move blk_throtl prototypes to block/blk.h block: fix genhd refcounting in blkio_policy_parse_and_set() ... Fix up trivial conflicts due to "mddev_t" -> "struct mddev" conversion and making the request functions be of type "void" instead of "int" in - drivers/md/{faulty.c,linear.c,md.c,md.h,multipath.c,raid0.c,raid1.c,raid10.c,raid5.c} - drivers/staging/zram/zram_drv.c
2011-11-05 00:06:58 +00:00
return;
error_unlock:
up_read(&zram->init_lock);
error:
bio_io_error(bio);
}
void __zram_reset_device(struct zram *zram)
{
size_t index;
zram->init_done = 0;
/* Free various per-device buffers */
kfree(zram->compress_workmem);
free_pages((unsigned long)zram->compress_buffer, 1);
zram->compress_workmem = NULL;
zram->compress_buffer = NULL;
/* Free all pages that are still in this zram device */
for (index = 0; index < zram->disksize >> PAGE_SHIFT; index++) {
unsigned long handle = zram->table[index].handle;
if (!handle)
continue;
zs_free(zram->mem_pool, handle);
}
vfree(zram->table);
zram->table = NULL;
zs_destroy_pool(zram->mem_pool);
zram->mem_pool = NULL;
/* Reset stats */
memset(&zram->stats, 0, sizeof(zram->stats));
zram->disksize = 0;
}
void zram_reset_device(struct zram *zram)
{
down_write(&zram->init_lock);
__zram_reset_device(zram);
up_write(&zram->init_lock);
}
int zram_init_device(struct zram *zram)
{
int ret;
size_t num_pages;
down_write(&zram->init_lock);
if (zram->init_done) {
up_write(&zram->init_lock);
return 0;
}
zram_set_disksize(zram, totalram_pages << PAGE_SHIFT);
zram->compress_workmem = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
if (!zram->compress_workmem) {
pr_err("Error allocating compressor working memory!\n");
ret = -ENOMEM;
goto fail_no_table;
}
zram->compress_buffer =
(void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
if (!zram->compress_buffer) {
pr_err("Error allocating compressor buffer space\n");
ret = -ENOMEM;
goto fail_no_table;
}
num_pages = zram->disksize >> PAGE_SHIFT;
zram->table = vzalloc(num_pages * sizeof(*zram->table));
if (!zram->table) {
pr_err("Error allocating zram address table\n");
ret = -ENOMEM;
goto fail_no_table;
}
set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT);
/* zram devices sort of resembles non-rotational disks */
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue);
zram->mem_pool = zs_create_pool("zram", GFP_NOIO | __GFP_HIGHMEM);
if (!zram->mem_pool) {
pr_err("Error creating memory pool\n");
ret = -ENOMEM;
goto fail;
}
zram->init_done = 1;
up_write(&zram->init_lock);
pr_debug("Initialization done!\n");
return 0;
fail_no_table:
/* To prevent accessing table entries during cleanup */
zram->disksize = 0;
fail:
__zram_reset_device(zram);
up_write(&zram->init_lock);
pr_err("Initialization failed: err=%d\n", ret);
return ret;
}
static void zram_slot_free_notify(struct block_device *bdev,
unsigned long index)
{
struct zram *zram;
zram = bdev->bd_disk->private_data;
zram_free_page(zram, index);
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 = 0;
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);
ret = -ENOMEM;
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) {
blk_cleanup_queue(zram->queue);
pr_warn("Error allocating disk structure for device %d\n",
device_id);
ret = -ENOMEM;
goto out;
}
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);
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.
*/
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;
}
zram->init_done = 0;
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;
}
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);
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;
}
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];
destroy_device(zram);
if (zram->init_done)
zram_reset_device(zram);
}
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");