Staging: Rename ramzswap files to zram
authorNitin Gupta <ngupta@vflare.org>
Tue, 1 Jun 2010 08:01:24 +0000 (13:31 +0530)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 18 Jun 2010 19:46:42 +0000 (12:46 -0700)
Related changes:
 - Modify revelant Kconfig and Makefile accordingly.
 - Change include filenames in code.
 - Remove dependency on CONFIG_SWAP in Kconfig as zram usage
is no longer limited to swap disks.

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>
20 files changed:
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/ramzswap/Kconfig [deleted file]
drivers/staging/ramzswap/Makefile [deleted file]
drivers/staging/ramzswap/ramzswap.txt [deleted file]
drivers/staging/ramzswap/ramzswap_drv.c [deleted file]
drivers/staging/ramzswap/ramzswap_drv.h [deleted file]
drivers/staging/ramzswap/ramzswap_ioctl.h [deleted file]
drivers/staging/ramzswap/xvmalloc.c [deleted file]
drivers/staging/ramzswap/xvmalloc.h [deleted file]
drivers/staging/ramzswap/xvmalloc_int.h [deleted file]
drivers/staging/zram/Kconfig [new file with mode: 0644]
drivers/staging/zram/Makefile [new file with mode: 0644]
drivers/staging/zram/xvmalloc.c [new file with mode: 0644]
drivers/staging/zram/xvmalloc.h [new file with mode: 0644]
drivers/staging/zram/xvmalloc_int.h [new file with mode: 0644]
drivers/staging/zram/zram.txt [new file with mode: 0644]
drivers/staging/zram/zram_drv.c [new file with mode: 0644]
drivers/staging/zram/zram_drv.h [new file with mode: 0644]
drivers/staging/zram/zram_ioctl.h [new file with mode: 0644]

index d5b827ccecf8d0a3e3a879871c5ef7e1910a82e7..cdd3ea348319f3ffff37a0956a1dfc164049d1d9 100644 (file)
@@ -119,7 +119,7 @@ source "drivers/staging/sep/Kconfig"
 
 source "drivers/staging/iio/Kconfig"
 
-source "drivers/staging/ramzswap/Kconfig"
+source "drivers/staging/zram/Kconfig"
 
 source "drivers/staging/wlags49_h2/Kconfig"
 
index 2c14f89387be6736a0557cd4248446f45887c202..beceaff3dfdf0a294feeb98682a38260c727d21b 100644 (file)
@@ -40,7 +40,7 @@ obj-$(CONFIG_RAR_REGISTER)    += rar_register/
 obj-$(CONFIG_MRST_RAR_HANDLER) += memrar/
 obj-$(CONFIG_DX_SEP)           += sep/
 obj-$(CONFIG_IIO)              += iio/
-obj-$(CONFIG_RAMZSWAP)         += ramzswap/
+obj-$(CONFIG_ZRAM)             += zram/
 obj-$(CONFIG_WLAGS49_H2)       += wlags49_h2/
 obj-$(CONFIG_WLAGS49_H25)      += wlags49_h25/
 obj-$(CONFIG_BATMAN_ADV)       += batman-adv/
diff --git a/drivers/staging/ramzswap/Kconfig b/drivers/staging/ramzswap/Kconfig
deleted file mode 100644 (file)
index 127b3c6..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-config RAMZSWAP
-       tristate "Compressed in-memory swap device (ramzswap)"
-       depends on SWAP
-       select LZO_COMPRESS
-       select LZO_DECOMPRESS
-       default n
-       help
-         Creates virtual block devices which can (only) be used as swap
-         disks. Pages swapped to these disks are compressed and stored in
-         memory itself.
-
-         See ramzswap.txt for more information.
-         Project home: http://compcache.googlecode.com/
-
-config RAMZSWAP_STATS
-       bool "Enable ramzswap stats"
-       depends on RAMZSWAP
-       default y
-       help
-         Enable statistics collection for ramzswap. This adds only a minimal
-         overhead. In unsure, say Y.
diff --git a/drivers/staging/ramzswap/Makefile b/drivers/staging/ramzswap/Makefile
deleted file mode 100644 (file)
index 507d7dc..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-ramzswap-objs  :=      ramzswap_drv.o xvmalloc.o
-
-obj-$(CONFIG_RAMZSWAP) +=      ramzswap.o
diff --git a/drivers/staging/ramzswap/ramzswap.txt b/drivers/staging/ramzswap/ramzswap.txt
deleted file mode 100644 (file)
index 9694acf..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-ramzswap: Compressed RAM based swap device
--------------------------------------------
-
-Project home: http://compcache.googlecode.com/
-
-* Introduction
-
-The ramzswap module creates RAM based block devices which can (only) be used as
-swap disks. Pages swapped to these devices are compressed and stored in memory
-itself. See project home for use cases, performance numbers and a lot more.
-
-Individual ramzswap devices are configured and initialized using rzscontrol
-userspace utility as shown in examples below. See rzscontrol man page for more
-details.
-
-* Usage
-
-Following shows a typical sequence of steps for using ramzswap.
-
-1) Load Modules:
-       modprobe ramzswap num_devices=4
-       This creates 4 (uninitialized) devices: /dev/ramzswap{0,1,2,3}
-       (num_devices parameter is optional. Default: 1)
-
-2) Initialize:
-       Use rzscontrol utility to configure and initialize individual
-       ramzswap devices. Example:
-       rzscontrol /dev/ramzswap2 --init # uses default value of disksize_kb
-
-       *See rzscontrol man page for more details and examples*
-
-3) Activate:
-       swapon /dev/ramzswap2 # or any other initialized ramzswap device
-
-4) Stats:
-       rzscontrol /dev/ramzswap2 --stats
-
-5) Deactivate:
-       swapoff /dev/ramzswap2
-
-6) Reset:
-       rzscontrol /dev/ramzswap2 --reset
-       (This frees all the memory allocated for this 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/ramzswap/ramzswap_drv.c b/drivers/staging/ramzswap/ramzswap_drv.c
deleted file mode 100644 (file)
index 7b5cc61..0000000
+++ /dev/null
@@ -1,805 +0,0 @@
-/*
- * Compressed RAM based swap 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 "ramzswap"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.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/swap.h>
-#include <linux/swapops.h>
-#include <linux/vmalloc.h>
-
-#include "ramzswap_drv.h"
-
-/* Globals */
-static int ramzswap_major;
-static struct ramzswap *devices;
-
-/* Module params (documentation at end) */
-static unsigned int num_devices;
-
-static int rzs_test_flag(struct ramzswap *rzs, u32 index,
-                       enum rzs_pageflags flag)
-{
-       return rzs->table[index].flags & BIT(flag);
-}
-
-static void rzs_set_flag(struct ramzswap *rzs, u32 index,
-                       enum rzs_pageflags flag)
-{
-       rzs->table[index].flags |= BIT(flag);
-}
-
-static void rzs_clear_flag(struct ramzswap *rzs, u32 index,
-                       enum rzs_pageflags flag)
-{
-       rzs->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 ramzswap_set_disksize(struct ramzswap *rzs, size_t totalram_bytes)
-{
-       if (!rzs->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
-               );
-               rzs->disksize = default_disksize_perc_ram *
-                                       (totalram_bytes / 100);
-       }
-
-       if (rzs->disksize > 2 * (totalram_bytes)) {
-               pr_info(
-               "There is little point creating a ramzswap of greater than "
-               "twice the size of memory since we expect a 2:1 compression "
-               "ratio. Note that ramzswap uses about 0.1%% of the size of "
-               "the swap device when not in use so a huge ramzswap is "
-               "wasteful.\n"
-               "\tMemory Size: %zu kB\n"
-               "\tSize you selected: %zu kB\n"
-               "Continuing anyway ...\n",
-               totalram_bytes >> 10, rzs->disksize
-               );
-       }
-
-       rzs->disksize &= PAGE_MASK;
-}
-
-static void ramzswap_ioctl_get_stats(struct ramzswap *rzs,
-                       struct ramzswap_ioctl_stats *s)
-{
-       s->disksize = rzs->disksize;
-
-#if defined(CONFIG_RAMZSWAP_STATS)
-       {
-       struct ramzswap_stats *rs = &rzs->stats;
-       size_t succ_writes, mem_used;
-       unsigned int good_compress_perc = 0, no_compress_perc = 0;
-
-       mem_used = xv_get_total_size_bytes(rzs->mem_pool)
-                       + (rs->pages_expand << PAGE_SHIFT);
-       succ_writes = rzs_stat64_read(rzs, &rs->num_writes) -
-                       rzs_stat64_read(rzs, &rs->failed_writes);
-
-       if (succ_writes && rs->pages_stored) {
-               good_compress_perc = rs->good_compress * 100
-                                       / rs->pages_stored;
-               no_compress_perc = rs->pages_expand * 100
-                                       / rs->pages_stored;
-       }
-
-       s->num_reads = rzs_stat64_read(rzs, &rs->num_reads);
-       s->num_writes = rzs_stat64_read(rzs, &rs->num_writes);
-       s->failed_reads = rzs_stat64_read(rzs, &rs->failed_reads);
-       s->failed_writes = rzs_stat64_read(rzs, &rs->failed_writes);
-       s->invalid_io = rzs_stat64_read(rzs, &rs->invalid_io);
-       s->notify_free = rzs_stat64_read(rzs, &rs->notify_free);
-       s->pages_zero = rs->pages_zero;
-
-       s->good_compress_pct = good_compress_perc;
-       s->pages_expand_pct = no_compress_perc;
-
-       s->pages_stored = rs->pages_stored;
-       s->pages_used = mem_used >> PAGE_SHIFT;
-       s->orig_data_size = rs->pages_stored << PAGE_SHIFT;
-       s->compr_data_size = rs->compr_size;
-       s->mem_used_total = mem_used;
-       }
-#endif /* CONFIG_RAMZSWAP_STATS */
-}
-
-static void ramzswap_free_page(struct ramzswap *rzs, size_t index)
-{
-       u32 clen;
-       void *obj;
-
-       struct page *page = rzs->table[index].page;
-       u32 offset = rzs->table[index].offset;
-
-       if (unlikely(!page)) {
-               /*
-                * No memory is allocated for zero filled pages.
-                * Simply clear zero page flag.
-                */
-               if (rzs_test_flag(rzs, index, RZS_ZERO)) {
-                       rzs_clear_flag(rzs, index, RZS_ZERO);
-                       rzs_stat_dec(&rzs->stats.pages_zero);
-               }
-               return;
-       }
-
-       if (unlikely(rzs_test_flag(rzs, index, RZS_UNCOMPRESSED))) {
-               clen = PAGE_SIZE;
-               __free_page(page);
-               rzs_clear_flag(rzs, index, RZS_UNCOMPRESSED);
-               rzs_stat_dec(&rzs->stats.pages_expand);
-               goto out;
-       }
-
-       obj = kmap_atomic(page, KM_USER0) + offset;
-       clen = xv_get_object_size(obj) - sizeof(struct zobj_header);
-       kunmap_atomic(obj, KM_USER0);
-
-       xv_free(rzs->mem_pool, page, offset);
-       if (clen <= PAGE_SIZE / 2)
-               rzs_stat_dec(&rzs->stats.good_compress);
-
-out:
-       rzs->stats.compr_size -= clen;
-       rzs_stat_dec(&rzs->stats.pages_stored);
-
-       rzs->table[index].page = NULL;
-       rzs->table[index].offset = 0;
-}
-
-static void handle_zero_page(struct page *page)
-{
-       void *user_mem;
-
-       user_mem = kmap_atomic(page, KM_USER0);
-       memset(user_mem, 0, PAGE_SIZE);
-       kunmap_atomic(user_mem, KM_USER0);
-
-       flush_dcache_page(page);
-}
-
-static void handle_uncompressed_page(struct ramzswap *rzs,
-                               struct page *page, u32 index)
-{
-       unsigned char *user_mem, *cmem;
-
-       user_mem = kmap_atomic(page, KM_USER0);
-       cmem = kmap_atomic(rzs->table[index].page, KM_USER1) +
-                       rzs->table[index].offset;
-
-       memcpy(user_mem, cmem, PAGE_SIZE);
-       kunmap_atomic(user_mem, KM_USER0);
-       kunmap_atomic(cmem, KM_USER1);
-
-       flush_dcache_page(page);
-}
-
-static int ramzswap_read(struct ramzswap *rzs, struct bio *bio)
-{
-
-       int i;
-       u32 index;
-       struct bio_vec *bvec;
-
-       rzs_stat64_inc(rzs, &rzs->stats.num_reads);
-
-       index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT;
-       bio_for_each_segment(bvec, bio, i) {
-               int ret;
-               size_t clen;
-               struct page *page;
-               struct zobj_header *zheader;
-               unsigned char *user_mem, *cmem;
-
-               page = bvec->bv_page;
-
-               if (rzs_test_flag(rzs, index, RZS_ZERO)) {
-                       handle_zero_page(page);
-                       continue;
-               }
-
-               /* Requested page is not present in compressed area */
-               if (unlikely(!rzs->table[index].page)) {
-                       pr_debug("Read before write: sector=%lu, size=%u",
-                               (ulong)(bio->bi_sector), bio->bi_size);
-                       /* Do nothing */
-                       continue;
-               }
-
-               /* Page is stored uncompressed since it's incompressible */
-               if (unlikely(rzs_test_flag(rzs, index, RZS_UNCOMPRESSED))) {
-                       handle_uncompressed_page(rzs, page, index);
-                       continue;
-               }
-
-               user_mem = kmap_atomic(page, KM_USER0);
-               clen = PAGE_SIZE;
-
-               cmem = kmap_atomic(rzs->table[index].page, KM_USER1) +
-                               rzs->table[index].offset;
-
-               ret = lzo1x_decompress_safe(
-                       cmem + sizeof(*zheader),
-                       xv_get_object_size(cmem) - sizeof(*zheader),
-                       user_mem, &clen);
-
-               kunmap_atomic(user_mem, KM_USER0);
-               kunmap_atomic(cmem, KM_USER1);
-
-               /* 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);
-                       rzs_stat64_inc(rzs, &rzs->stats.failed_reads);
-                       goto out;
-               }
-
-               flush_dcache_page(page);
-               index++;
-       }
-
-       set_bit(BIO_UPTODATE, &bio->bi_flags);
-       bio_endio(bio, 0);
-       return 0;
-
-out:
-       bio_io_error(bio);
-       return 0;
-}
-
-static int ramzswap_write(struct ramzswap *rzs, struct bio *bio)
-{
-       int i;
-       u32 index;
-       struct bio_vec *bvec;
-
-       rzs_stat64_inc(rzs, &rzs->stats.num_writes);
-
-       index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT;
-
-       bio_for_each_segment(bvec, bio, i) {
-               int ret;
-               u32 offset;
-               size_t clen;
-               struct zobj_header *zheader;
-               struct page *page, *page_store;
-               unsigned char *user_mem, *cmem, *src;
-
-               page = bvec->bv_page;
-               src = rzs->compress_buffer;
-
-               /*
-                * System overwrites unused sectors. Free memory associated
-                * with this sector now.
-                */
-               if (rzs->table[index].page ||
-                               rzs_test_flag(rzs, index, RZS_ZERO))
-                       ramzswap_free_page(rzs, index);
-
-               mutex_lock(&rzs->lock);
-
-               user_mem = kmap_atomic(page, KM_USER0);
-               if (page_zero_filled(user_mem)) {
-                       kunmap_atomic(user_mem, KM_USER0);
-                       mutex_unlock(&rzs->lock);
-                       rzs_stat_inc(&rzs->stats.pages_zero);
-                       rzs_set_flag(rzs, index, RZS_ZERO);
-                       continue;
-               }
-
-               ret = lzo1x_1_compress(user_mem, PAGE_SIZE, src, &clen,
-                                       rzs->compress_workmem);
-
-               kunmap_atomic(user_mem, KM_USER0);
-
-               if (unlikely(ret != LZO_E_OK)) {
-                       mutex_unlock(&rzs->lock);
-                       pr_err("Compression failed! err=%d\n", ret);
-                       rzs_stat64_inc(rzs, &rzs->stats.failed_writes);
-                       goto out;
-               }
-
-               /*
-                * Page is incompressible. Store it as-is (uncompressed)
-                * since we do not want to return too many swap 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)) {
-                               mutex_unlock(&rzs->lock);
-                               pr_info("Error allocating memory for "
-                                       "incompressible page: %u\n", index);
-                               rzs_stat64_inc(rzs, &rzs->stats.failed_writes);
-                               goto out;
-                       }
-
-                       offset = 0;
-                       rzs_set_flag(rzs, index, RZS_UNCOMPRESSED);
-                       rzs_stat_inc(&rzs->stats.pages_expand);
-                       rzs->table[index].page = page_store;
-                       src = kmap_atomic(page, KM_USER0);
-                       goto memstore;
-               }
-
-               if (xv_malloc(rzs->mem_pool, clen + sizeof(*zheader),
-                               &rzs->table[index].page, &offset,
-                               GFP_NOIO | __GFP_HIGHMEM)) {
-                       mutex_unlock(&rzs->lock);
-                       pr_info("Error allocating memory for compressed "
-                               "page: %u, size=%zu\n", index, clen);
-                       rzs_stat64_inc(rzs, &rzs->stats.failed_writes);
-                       goto out;
-               }
-
-memstore:
-               rzs->table[index].offset = offset;
-
-               cmem = kmap_atomic(rzs->table[index].page, KM_USER1) +
-                               rzs->table[index].offset;
-
-#if 0
-               /* Back-reference needed for memory defragmentation */
-               if (!rzs_test_flag(rzs, index, RZS_UNCOMPRESSED)) {
-                       zheader = (struct zobj_header *)cmem;
-                       zheader->table_idx = index;
-                       cmem += sizeof(*zheader);
-               }
-#endif
-
-               memcpy(cmem, src, clen);
-
-               kunmap_atomic(cmem, KM_USER1);
-               if (unlikely(rzs_test_flag(rzs, index, RZS_UNCOMPRESSED)))
-                       kunmap_atomic(src, KM_USER0);
-
-               /* Update stats */
-               rzs->stats.compr_size += clen;
-               rzs_stat_inc(&rzs->stats.pages_stored);
-               if (clen <= PAGE_SIZE / 2)
-                       rzs_stat_inc(&rzs->stats.good_compress);
-
-               mutex_unlock(&rzs->lock);
-               index++;
-       }
-
-       set_bit(BIO_UPTODATE, &bio->bi_flags);
-       bio_endio(bio, 0);
-       return 0;
-
-out:
-       bio_io_error(bio);
-       return 0;
-}
-
-/*
- * Check if request is within bounds and page aligned.
- */
-static inline int valid_io_request(struct ramzswap *rzs, struct bio *bio)
-{
-       if (unlikely(
-               (bio->bi_sector >= (rzs->disksize >> SECTOR_SHIFT)) ||
-               (bio->bi_sector & (SECTORS_PER_PAGE - 1)) ||
-               (bio->bi_size & (PAGE_SIZE - 1)))) {
-
-               return 0;
-       }
-
-       /* I/O request is valid */
-       return 1;
-}
-
-/*
- * Handler function for all ramzswap I/O requests.
- */
-static int ramzswap_make_request(struct request_queue *queue, struct bio *bio)
-{
-       int ret = 0;
-       struct ramzswap *rzs = queue->queuedata;
-
-       if (unlikely(!rzs->init_done)) {
-               bio_io_error(bio);
-               return 0;
-       }
-
-       if (!valid_io_request(rzs, bio)) {
-               rzs_stat64_inc(rzs, &rzs->stats.invalid_io);
-               bio_io_error(bio);
-               return 0;
-       }
-
-       switch (bio_data_dir(bio)) {
-       case READ:
-               ret = ramzswap_read(rzs, bio);
-               break;
-
-       case WRITE:
-               ret = ramzswap_write(rzs, bio);
-               break;
-       }
-
-       return ret;
-}
-
-static void reset_device(struct ramzswap *rzs)
-{
-       size_t index;
-
-       /* Do not accept any new I/O request */
-       rzs->init_done = 0;
-
-       /* Free various per-device buffers */
-       kfree(rzs->compress_workmem);
-       free_pages((unsigned long)rzs->compress_buffer, 1);
-
-       rzs->compress_workmem = NULL;
-       rzs->compress_buffer = NULL;
-
-       /* Free all pages that are still in this ramzswap device */
-       for (index = 0; index < rzs->disksize >> PAGE_SHIFT; index++) {
-               struct page *page;
-               u16 offset;
-
-               page = rzs->table[index].page;
-               offset = rzs->table[index].offset;
-
-               if (!page)
-                       continue;
-
-               if (unlikely(rzs_test_flag(rzs, index, RZS_UNCOMPRESSED)))
-                       __free_page(page);
-               else
-                       xv_free(rzs->mem_pool, page, offset);
-       }
-
-       vfree(rzs->table);
-       rzs->table = NULL;
-
-       xv_destroy_pool(rzs->mem_pool);
-       rzs->mem_pool = NULL;
-
-       /* Reset stats */
-       memset(&rzs->stats, 0, sizeof(rzs->stats));
-
-       rzs->disksize = 0;
-}
-
-static int ramzswap_ioctl_init_device(struct ramzswap *rzs)
-{
-       int ret;
-       size_t num_pages;
-
-       if (rzs->init_done) {
-               pr_info("Device already initialized!\n");
-               return -EBUSY;
-       }
-
-       ramzswap_set_disksize(rzs, totalram_pages << PAGE_SHIFT);
-
-       rzs->compress_workmem = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
-       if (!rzs->compress_workmem) {
-               pr_err("Error allocating compressor working memory!\n");
-               ret = -ENOMEM;
-               goto fail;
-       }
-
-       rzs->compress_buffer = (void *)__get_free_pages(__GFP_ZERO, 1);
-       if (!rzs->compress_buffer) {
-               pr_err("Error allocating compressor buffer space\n");
-               ret = -ENOMEM;
-               goto fail;
-       }
-
-       num_pages = rzs->disksize >> PAGE_SHIFT;
-       rzs->table = vmalloc(num_pages * sizeof(*rzs->table));
-       if (!rzs->table) {
-               pr_err("Error allocating ramzswap address table\n");
-               /* To prevent accessing table entries during cleanup */
-               rzs->disksize = 0;
-               ret = -ENOMEM;
-               goto fail;
-       }
-       memset(rzs->table, 0, num_pages * sizeof(*rzs->table));
-
-       set_capacity(rzs->disk, rzs->disksize >> SECTOR_SHIFT);
-
-       /* ramzswap devices sort of resembles non-rotational disks */
-       queue_flag_set_unlocked(QUEUE_FLAG_NONROT, rzs->disk->queue);
-
-       rzs->mem_pool = xv_create_pool();
-       if (!rzs->mem_pool) {
-               pr_err("Error creating memory pool\n");
-               ret = -ENOMEM;
-               goto fail;
-       }
-
-       rzs->init_done = 1;
-
-       pr_debug("Initialization done!\n");
-       return 0;
-
-fail:
-       reset_device(rzs);
-
-       pr_err("Initialization failed: err=%d\n", ret);
-       return ret;
-}
-
-static int ramzswap_ioctl_reset_device(struct ramzswap *rzs)
-{
-       if (rzs->init_done)
-               reset_device(rzs);
-
-       return 0;
-}
-
-static int ramzswap_ioctl(struct block_device *bdev, fmode_t mode,
-                       unsigned int cmd, unsigned long arg)
-{
-       int ret = 0;
-       size_t disksize_kb;
-
-       struct ramzswap *rzs = bdev->bd_disk->private_data;
-
-       switch (cmd) {
-       case RZSIO_SET_DISKSIZE_KB:
-               if (rzs->init_done) {
-                       ret = -EBUSY;
-                       goto out;
-               }
-               if (copy_from_user(&disksize_kb, (void *)arg,
-                                               _IOC_SIZE(cmd))) {
-                       ret = -EFAULT;
-                       goto out;
-               }
-               rzs->disksize = disksize_kb << 10;
-               pr_info("Disk size set to %zu kB\n", disksize_kb);
-               break;
-
-       case RZSIO_GET_STATS:
-       {
-               struct ramzswap_ioctl_stats *stats;
-               if (!rzs->init_done) {
-                       ret = -ENOTTY;
-                       goto out;
-               }
-               stats = kzalloc(sizeof(*stats), GFP_KERNEL);
-               if (!stats) {
-                       ret = -ENOMEM;
-                       goto out;
-               }
-               ramzswap_ioctl_get_stats(rzs, stats);
-               if (copy_to_user((void *)arg, stats, sizeof(*stats))) {
-                       kfree(stats);
-                       ret = -EFAULT;
-                       goto out;
-               }
-               kfree(stats);
-               break;
-       }
-       case RZSIO_INIT:
-               ret = ramzswap_ioctl_init_device(rzs);
-               break;
-
-       case RZSIO_RESET:
-               /* Do not reset an active device! */
-               if (bdev->bd_holders) {
-                       ret = -EBUSY;
-                       goto out;
-               }
-
-               /* Make sure all pending I/O is finished */
-               if (bdev)
-                       fsync_bdev(bdev);
-
-               ret = ramzswap_ioctl_reset_device(rzs);
-               break;
-
-       default:
-               pr_info("Invalid ioctl %u\n", cmd);
-               ret = -ENOTTY;
-       }
-
-out:
-       return ret;
-}
-
-void ramzswap_slot_free_notify(struct block_device *bdev, unsigned long index)
-{
-       struct ramzswap *rzs;
-
-       rzs = bdev->bd_disk->private_data;
-       ramzswap_free_page(rzs, index);
-       rzs_stat64_inc(rzs, &rzs->stats.notify_free);
-}
-
-static const struct block_device_operations ramzswap_devops = {
-       .ioctl = ramzswap_ioctl,
-       .swap_slot_free_notify = ramzswap_slot_free_notify,
-       .owner = THIS_MODULE
-};
-
-static int create_device(struct ramzswap *rzs, int device_id)
-{
-       int ret = 0;
-
-       mutex_init(&rzs->lock);
-       spin_lock_init(&rzs->stat64_lock);
-
-       rzs->queue = blk_alloc_queue(GFP_KERNEL);
-       if (!rzs->queue) {
-               pr_err("Error allocating disk queue for device %d\n",
-                       device_id);
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       blk_queue_make_request(rzs->queue, ramzswap_make_request);
-       rzs->queue->queuedata = rzs;
-
-        /* gendisk structure */
-       rzs->disk = alloc_disk(1);
-       if (!rzs->disk) {
-               blk_cleanup_queue(rzs->queue);
-               pr_warning("Error allocating disk structure for device %d\n",
-                       device_id);
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       rzs->disk->major = ramzswap_major;
-       rzs->disk->first_minor = device_id;
-       rzs->disk->fops = &ramzswap_devops;
-       rzs->disk->queue = rzs->queue;
-       rzs->disk->private_data = rzs;
-       snprintf(rzs->disk->disk_name, 16, "ramzswap%d", device_id);
-
-       /* Actual capacity set using RZSIO_SET_DISKSIZE_KB ioctl */
-       set_capacity(rzs->disk, 0);
-
-       /*
-        * To ensure that we always get PAGE_SIZE aligned
-        * and n*PAGE_SIZED sized I/O requests.
-        */
-       blk_queue_physical_block_size(rzs->disk->queue, PAGE_SIZE);
-       blk_queue_logical_block_size(rzs->disk->queue, PAGE_SIZE);
-       blk_queue_io_min(rzs->disk->queue, PAGE_SIZE);
-       blk_queue_io_opt(rzs->disk->queue, PAGE_SIZE);
-
-       add_disk(rzs->disk);
-
-       rzs->init_done = 0;
-
-out:
-       return ret;
-}
-
-static void destroy_device(struct ramzswap *rzs)
-{
-       if (rzs->disk) {
-               del_gendisk(rzs->disk);
-               put_disk(rzs->disk);
-       }
-
-       if (rzs->queue)
-               blk_cleanup_queue(rzs->queue);
-}
-
-static int __init ramzswap_init(void)
-{
-       int ret, dev_id;
-
-       if (num_devices > max_num_devices) {
-               pr_warning("Invalid value for num_devices: %u\n",
-                               num_devices);
-               ret = -EINVAL;
-               goto out;
-       }
-
-       ramzswap_major = register_blkdev(0, "ramzswap");
-       if (ramzswap_major <= 0) {
-               pr_warning("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);
-       devices = kzalloc(num_devices * sizeof(struct ramzswap), GFP_KERNEL);
-       if (!devices) {
-               ret = -ENOMEM;
-               goto unregister;
-       }
-
-       for (dev_id = 0; dev_id < num_devices; dev_id++) {
-               ret = create_device(&devices[dev_id], dev_id);
-               if (ret)
-                       goto free_devices;
-       }
-
-       return 0;
-
-free_devices:
-       while (dev_id)
-               destroy_device(&devices[--dev_id]);
-unregister:
-       unregister_blkdev(ramzswap_major, "ramzswap");
-out:
-       return ret;
-}
-
-static void __exit ramzswap_exit(void)
-{
-       int i;
-       struct ramzswap *rzs;
-
-       for (i = 0; i < num_devices; i++) {
-               rzs = &devices[i];
-
-               destroy_device(rzs);
-               if (rzs->init_done)
-                       reset_device(rzs);
-       }
-
-       unregister_blkdev(ramzswap_major, "ramzswap");
-
-       kfree(devices);
-       pr_debug("Cleanup done!\n");
-}
-
-module_param(num_devices, uint, 0);
-MODULE_PARM_DESC(num_devices, "Number of ramzswap devices");
-
-module_init(ramzswap_init);
-module_exit(ramzswap_exit);
-
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_AUTHOR("Nitin Gupta <ngupta@vflare.org>");
-MODULE_DESCRIPTION("Compressed RAM Based Swap Device");
diff --git a/drivers/staging/ramzswap/ramzswap_drv.h b/drivers/staging/ramzswap/ramzswap_drv.h
deleted file mode 100644 (file)
index ad91726..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Compressed RAM based swap 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 _RAMZSWAP_DRV_H_
-#define _RAMZSWAP_DRV_H_
-
-#include <linux/spinlock.h>
-#include <linux/mutex.h>
-
-#include "ramzswap_ioctl.h"
-#include "xvmalloc.h"
-
-/*
- * Some arbitrary value. This is just to catch
- * invalid value for num_devices module parameter.
- */
-static const unsigned max_num_devices = 32;
-
-/*
- * Stored at beginning of each compressed object.
- *
- * It stores back-reference to table entry which points to this
- * object. This is required to support memory defragmentation.
- */
-struct zobj_header {
-#if 0
-       u32 table_idx;
-#endif
-};
-
-/*-- Configurable parameters */
-
-/* Default ramzswap disk size: 25% of total RAM */
-static const unsigned default_disksize_perc_ram = 25;
-
-/*
- * Pages that compress to size greater than this are stored
- * uncompressed in memory.
- */
-static const unsigned max_zpage_size = PAGE_SIZE / 4 * 3;
-
-/*
- * NOTE: max_zpage_size must be less than or equal to:
- *   XV_MAX_ALLOC_SIZE - sizeof(struct zobj_header)
- * otherwise, xv_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)
-
-/* Flags for ramzswap pages (table[page_no].flags) */
-enum rzs_pageflags {
-       /* Page is stored uncompressed */
-       RZS_UNCOMPRESSED,
-
-       /* Page consists entirely of zeros */
-       RZS_ZERO,
-
-       __NR_RZS_PAGEFLAGS,
-};
-
-/*-- Data structures */
-
-/*
- * Allocated for each swap slot, indexed by page no.
- * These table entries must fit exactly in a page.
- */
-struct table {
-       struct page *page;
-       u16 offset;
-       u8 count;       /* object ref count (not yet used) */
-       u8 flags;
-} __attribute__((aligned(4)));
-
-struct ramzswap_stats {
-       /* basic stats */
-       size_t compr_size;      /* compressed size of pages stored -
-                                * needed to enforce memlimit */
-       /* more stats */
-#if defined(CONFIG_RAMZSWAP_STATS)
-       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-swap 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 pages_expand;       /* % of incompressible pages */
-#endif
-};
-
-struct ramzswap {
-       struct xv_pool *mem_pool;
-       void *compress_workmem;
-       void *compress_buffer;
-       struct table *table;
-       spinlock_t stat64_lock; /* protect 64-bit stats */
-       struct mutex lock;      /* protect compression buffers against
-                                * concurrent writes */
-       struct request_queue *queue;
-       struct gendisk *disk;
-       int init_done;
-       /*
-        * This is limit on amount of *uncompressed* worth of data
-        * we can hold. When backing swap device is provided, it is
-        * set equal to device size.
-        */
-       size_t disksize;        /* bytes */
-
-       struct ramzswap_stats stats;
-};
-
-/*-- */
-
-/* Debugging and Stats */
-#if defined(CONFIG_RAMZSWAP_STATS)
-static void rzs_stat_inc(u32 *v)
-{
-       *v = *v + 1;
-}
-
-static void rzs_stat_dec(u32 *v)
-{
-       *v = *v - 1;
-}
-
-static void rzs_stat64_inc(struct ramzswap *rzs, u64 *v)
-{
-       spin_lock(&rzs->stat64_lock);
-       *v = *v + 1;
-       spin_unlock(&rzs->stat64_lock);
-}
-
-static u64 rzs_stat64_read(struct ramzswap *rzs, u64 *v)
-{
-       u64 val;
-
-       spin_lock(&rzs->stat64_lock);
-       val = *v;
-       spin_unlock(&rzs->stat64_lock);
-
-       return val;
-}
-#else
-#define rzs_stat_inc(v)
-#define rzs_stat_dec(v)
-#define rzs_stat64_inc(r, v)
-#define rzs_stat64_read(r, v)
-#endif /* CONFIG_RAMZSWAP_STATS */
-
-#endif
diff --git a/drivers/staging/ramzswap/ramzswap_ioctl.h b/drivers/staging/ramzswap/ramzswap_ioctl.h
deleted file mode 100644 (file)
index db94bcb..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Compressed RAM based swap 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 _RAMZSWAP_IOCTL_H_
-#define _RAMZSWAP_IOCTL_H_
-
-struct ramzswap_ioctl_stats {
-       u64 disksize;           /* user specified or equal to backing swap
-                                * size (if present) */
-       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-swap I/O requests */
-       u64 notify_free;        /* no. of swap slot free notifications */
-       u32 pages_zero;         /* no. of zero filled pages */
-       u32 good_compress_pct;  /* no. of pages with compression ratio<=50% */
-       u32 pages_expand_pct;   /* no. of incompressible pages */
-       u32 pages_stored;
-       u32 pages_used;
-       u64 orig_data_size;
-       u64 compr_data_size;
-       u64 mem_used_total;
-} __attribute__ ((packed, aligned(4)));
-
-#define RZSIO_SET_DISKSIZE_KB  _IOW('z', 0, size_t)
-#define RZSIO_GET_STATS                _IOR('z', 1, struct ramzswap_ioctl_stats)
-#define RZSIO_INIT             _IO('z', 2)
-#define RZSIO_RESET            _IO('z', 3)
-
-#endif
diff --git a/drivers/staging/ramzswap/xvmalloc.c b/drivers/staging/ramzswap/xvmalloc.c
deleted file mode 100644 (file)
index 3fdbb8a..0000000
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- * xvmalloc memory allocator
- *
- * 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
- */
-
-#include <linux/bitops.h>
-#include <linux/errno.h>
-#include <linux/highmem.h>
-#include <linux/init.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-
-#include "xvmalloc.h"
-#include "xvmalloc_int.h"
-
-static void stat_inc(u64 *value)
-{
-       *value = *value + 1;
-}
-
-static void stat_dec(u64 *value)
-{
-       *value = *value - 1;
-}
-
-static int test_flag(struct block_header *block, enum blockflags flag)
-{
-       return block->prev & BIT(flag);
-}
-
-static void set_flag(struct block_header *block, enum blockflags flag)
-{
-       block->prev |= BIT(flag);
-}
-
-static void clear_flag(struct block_header *block, enum blockflags flag)
-{
-       block->prev &= ~BIT(flag);
-}
-
-/*
- * Given <page, offset> pair, provide a derefrencable pointer.
- * This is called from xv_malloc/xv_free path, so it
- * needs to be fast.
- */
-static void *get_ptr_atomic(struct page *page, u16 offset, enum km_type type)
-{
-       unsigned char *base;
-
-       base = kmap_atomic(page, type);
-       return base + offset;
-}
-
-static void put_ptr_atomic(void *ptr, enum km_type type)
-{
-       kunmap_atomic(ptr, type);
-}
-
-static u32 get_blockprev(struct block_header *block)
-{
-       return block->prev & PREV_MASK;
-}
-
-static void set_blockprev(struct block_header *block, u16 new_offset)
-{
-       block->prev = new_offset | (block->prev & FLAGS_MASK);
-}
-
-static struct block_header *BLOCK_NEXT(struct block_header *block)
-{
-       return (struct block_header *)
-               ((char *)block + block->size + XV_ALIGN);
-}
-
-/*
- * Get index of free list containing blocks of maximum size
- * which is less than or equal to given size.
- */
-static u32 get_index_for_insert(u32 size)
-{
-       if (unlikely(size > XV_MAX_ALLOC_SIZE))
-               size = XV_MAX_ALLOC_SIZE;
-       size &= ~FL_DELTA_MASK;
-       return (size - XV_MIN_ALLOC_SIZE) >> FL_DELTA_SHIFT;
-}
-
-/*
- * Get index of free list having blocks of size greater than
- * or equal to requested size.
- */
-static u32 get_index(u32 size)
-{
-       if (unlikely(size < XV_MIN_ALLOC_SIZE))
-               size = XV_MIN_ALLOC_SIZE;
-       size = ALIGN(size, FL_DELTA);
-       return (size - XV_MIN_ALLOC_SIZE) >> FL_DELTA_SHIFT;
-}
-
-/**
- * find_block - find block of at least given size
- * @pool: memory pool to search from
- * @size: size of block required
- * @page: page containing required block
- * @offset: offset within the page where block is located.
- *
- * Searches two level bitmap to locate block of at least
- * the given size. If such a block is found, it provides
- * <page, offset> to identify this block and returns index
- * in freelist where we found this block.
- * Otherwise, returns 0 and <page, offset> params are not touched.
- */
-static u32 find_block(struct xv_pool *pool, u32 size,
-                       struct page **page, u32 *offset)
-{
-       ulong flbitmap, slbitmap;
-       u32 flindex, slindex, slbitstart;
-
-       /* There are no free blocks in this pool */
-       if (!pool->flbitmap)
-               return 0;
-
-       /* Get freelist index correspoding to this size */
-       slindex = get_index(size);
-       slbitmap = pool->slbitmap[slindex / BITS_PER_LONG];
-       slbitstart = slindex % BITS_PER_LONG;
-
-       /*
-        * If freelist is not empty at this index, we found the
-        * block - head of this list. This is approximate best-fit match.
-        */
-       if (test_bit(slbitstart, &slbitmap)) {
-               *page = pool->freelist[slindex].page;
-               *offset = pool->freelist[slindex].offset;
-               return slindex;
-       }
-
-       /*
-        * No best-fit found. Search a bit further in bitmap for a free block.
-        * Second level bitmap consists of series of 32-bit chunks. Search
-        * further in the chunk where we expected a best-fit, starting from
-        * index location found above.
-        */
-       slbitstart++;
-       slbitmap >>= slbitstart;
-
-       /* Skip this search if we were already at end of this bitmap chunk */
-       if ((slbitstart != BITS_PER_LONG) && slbitmap) {
-               slindex += __ffs(slbitmap) + 1;
-               *page = pool->freelist[slindex].page;
-               *offset = pool->freelist[slindex].offset;
-               return slindex;
-       }
-
-       /* Now do a full two-level bitmap search to find next nearest fit */
-       flindex = slindex / BITS_PER_LONG;
-
-       flbitmap = (pool->flbitmap) >> (flindex + 1);
-       if (!flbitmap)
-               return 0;
-
-       flindex += __ffs(flbitmap) + 1;
-       slbitmap = pool->slbitmap[flindex];
-       slindex = (flindex * BITS_PER_LONG) + __ffs(slbitmap);
-       *page = pool->freelist[slindex].page;
-       *offset = pool->freelist[slindex].offset;
-
-       return slindex;
-}
-
-/*
- * Insert block at <page, offset> in freelist of given pool.
- * freelist used depends on block size.
- */
-static void insert_block(struct xv_pool *pool, struct page *page, u32 offset,
-                       struct block_header *block)
-{
-       u32 flindex, slindex;
-       struct block_header *nextblock;
-
-       slindex = get_index_for_insert(block->size);
-       flindex = slindex / BITS_PER_LONG;
-
-       block->link.prev_page = 0;
-       block->link.prev_offset = 0;
-       block->link.next_page = pool->freelist[slindex].page;
-       block->link.next_offset = pool->freelist[slindex].offset;
-       pool->freelist[slindex].page = page;
-       pool->freelist[slindex].offset = offset;
-
-       if (block->link.next_page) {
-               nextblock = get_ptr_atomic(block->link.next_page,
-                                       block->link.next_offset, KM_USER1);
-               nextblock->link.prev_page = page;
-               nextblock->link.prev_offset = offset;
-               put_ptr_atomic(nextblock, KM_USER1);
-       }
-
-       __set_bit(slindex % BITS_PER_LONG, &pool->slbitmap[flindex]);
-       __set_bit(flindex, &pool->flbitmap);
-}
-
-/*
- * Remove block from head of freelist. Index 'slindex' identifies the freelist.
- */
-static void remove_block_head(struct xv_pool *pool,
-                       struct block_header *block, u32 slindex)
-{
-       struct block_header *tmpblock;
-       u32 flindex = slindex / BITS_PER_LONG;
-
-       pool->freelist[slindex].page = block->link.next_page;
-       pool->freelist[slindex].offset = block->link.next_offset;
-       block->link.prev_page = 0;
-       block->link.prev_offset = 0;
-
-       if (!pool->freelist[slindex].page) {
-               __clear_bit(slindex % BITS_PER_LONG, &pool->slbitmap[flindex]);
-               if (!pool->slbitmap[flindex])
-                       __clear_bit(flindex, &pool->flbitmap);
-       } else {
-               /*
-                * DEBUG ONLY: We need not reinitialize freelist head previous
-                * pointer to 0 - we never depend on its value. But just for
-                * sanity, lets do it.
-                */
-               tmpblock = get_ptr_atomic(pool->freelist[slindex].page,
-                               pool->freelist[slindex].offset, KM_USER1);
-               tmpblock->link.prev_page = 0;
-               tmpblock->link.prev_offset = 0;
-               put_ptr_atomic(tmpblock, KM_USER1);
-       }
-}
-
-/*
- * Remove block from freelist. Index 'slindex' identifies the freelist.
- */
-static void remove_block(struct xv_pool *pool, struct page *page, u32 offset,
-                       struct block_header *block, u32 slindex)
-{
-       u32 flindex;
-       struct block_header *tmpblock;
-
-       if (pool->freelist[slindex].page == page
-          && pool->freelist[slindex].offset == offset) {
-               remove_block_head(pool, block, slindex);
-               return;
-       }
-
-       flindex = slindex / BITS_PER_LONG;
-
-       if (block->link.prev_page) {
-               tmpblock = get_ptr_atomic(block->link.prev_page,
-                               block->link.prev_offset, KM_USER1);
-               tmpblock->link.next_page = block->link.next_page;
-               tmpblock->link.next_offset = block->link.next_offset;
-               put_ptr_atomic(tmpblock, KM_USER1);
-       }
-
-       if (block->link.next_page) {
-               tmpblock = get_ptr_atomic(block->link.next_page,
-                               block->link.next_offset, KM_USER1);
-               tmpblock->link.prev_page = block->link.prev_page;
-               tmpblock->link.prev_offset = block->link.prev_offset;
-               put_ptr_atomic(tmpblock, KM_USER1);
-       }
-}
-
-/*
- * Allocate a page and add it to freelist of given pool.
- */
-static int grow_pool(struct xv_pool *pool, gfp_t flags)
-{
-       struct page *page;
-       struct block_header *block;
-
-       page = alloc_page(flags);
-       if (unlikely(!page))
-               return -ENOMEM;
-
-       stat_inc(&pool->total_pages);
-
-       spin_lock(&pool->lock);
-       block = get_ptr_atomic(page, 0, KM_USER0);
-
-       block->size = PAGE_SIZE - XV_ALIGN;
-       set_flag(block, BLOCK_FREE);
-       clear_flag(block, PREV_FREE);
-       set_blockprev(block, 0);
-
-       insert_block(pool, page, 0, block);
-
-       put_ptr_atomic(block, KM_USER0);
-       spin_unlock(&pool->lock);
-
-       return 0;
-}
-
-/*
- * Create a memory pool. Allocates freelist, bitmaps and other
- * per-pool metadata.
- */
-struct xv_pool *xv_create_pool(void)
-{
-       u32 ovhd_size;
-       struct xv_pool *pool;
-
-       ovhd_size = roundup(sizeof(*pool), PAGE_SIZE);
-       pool = kzalloc(ovhd_size, GFP_KERNEL);
-       if (!pool)
-               return NULL;
-
-       spin_lock_init(&pool->lock);
-
-       return pool;
-}
-
-void xv_destroy_pool(struct xv_pool *pool)
-{
-       kfree(pool);
-}
-
-/**
- * xv_malloc - Allocate block of given size from pool.
- * @pool: pool to allocate from
- * @size: size of block to allocate
- * @page: page no. that holds the object
- * @offset: location of object within page
- *
- * On success, <page, offset> identifies block allocated
- * and 0 is returned. On failure, <page, offset> is set to
- * 0 and -ENOMEM is returned.
- *
- * Allocation requests with size > XV_MAX_ALLOC_SIZE will fail.
- */
-int xv_malloc(struct xv_pool *pool, u32 size, struct page **page,
-               u32 *offset, gfp_t flags)
-{
-       int error;
-       u32 index, tmpsize, origsize, tmpoffset;
-       struct block_header *block, *tmpblock;
-
-       *page = NULL;
-       *offset = 0;
-       origsize = size;
-
-       if (unlikely(!size || size > XV_MAX_ALLOC_SIZE))
-               return -ENOMEM;
-
-       size = ALIGN(size, XV_ALIGN);
-
-       spin_lock(&pool->lock);
-
-       index = find_block(pool, size, page, offset);
-
-       if (!*page) {
-               spin_unlock(&pool->lock);
-               if (flags & GFP_NOWAIT)
-                       return -ENOMEM;
-               error = grow_pool(pool, flags);
-               if (unlikely(error))
-                       return error;
-
-               spin_lock(&pool->lock);
-               index = find_block(pool, size, page, offset);
-       }
-
-       if (!*page) {
-               spin_unlock(&pool->lock);
-               return -ENOMEM;
-       }
-
-       block = get_ptr_atomic(*page, *offset, KM_USER0);
-
-       remove_block_head(pool, block, index);
-
-       /* Split the block if required */
-       tmpoffset = *offset + size + XV_ALIGN;
-       tmpsize = block->size - size;
-       tmpblock = (struct block_header *)((char *)block + size + XV_ALIGN);
-       if (tmpsize) {
-               tmpblock->size = tmpsize - XV_ALIGN;
-               set_flag(tmpblock, BLOCK_FREE);
-               clear_flag(tmpblock, PREV_FREE);
-
-               set_blockprev(tmpblock, *offset);
-               if (tmpblock->size >= XV_MIN_ALLOC_SIZE)
-                       insert_block(pool, *page, tmpoffset, tmpblock);
-
-               if (tmpoffset + XV_ALIGN + tmpblock->size != PAGE_SIZE) {
-                       tmpblock = BLOCK_NEXT(tmpblock);
-                       set_blockprev(tmpblock, tmpoffset);
-               }
-       } else {
-               /* This block is exact fit */
-               if (tmpoffset != PAGE_SIZE)
-                       clear_flag(tmpblock, PREV_FREE);
-       }
-
-       block->size = origsize;
-       clear_flag(block, BLOCK_FREE);
-
-       put_ptr_atomic(block, KM_USER0);
-       spin_unlock(&pool->lock);
-
-       *offset += XV_ALIGN;
-
-       return 0;
-}
-
-/*
- * Free block identified with <page, offset>
- */
-void xv_free(struct xv_pool *pool, struct page *page, u32 offset)
-{
-       void *page_start;
-       struct block_header *block, *tmpblock;
-
-       offset -= XV_ALIGN;
-
-       spin_lock(&pool->lock);
-
-       page_start = get_ptr_atomic(page, 0, KM_USER0);
-       block = (struct block_header *)((char *)page_start + offset);
-
-       /* Catch double free bugs */
-       BUG_ON(test_flag(block, BLOCK_FREE));
-
-       block->size = ALIGN(block->size, XV_ALIGN);
-
-       tmpblock = BLOCK_NEXT(block);
-       if (offset + block->size + XV_ALIGN == PAGE_SIZE)
-               tmpblock = NULL;
-
-       /* Merge next block if its free */
-       if (tmpblock && test_flag(tmpblock, BLOCK_FREE)) {
-               /*
-                * Blocks smaller than XV_MIN_ALLOC_SIZE
-                * are not inserted in any free list.
-                */
-               if (tmpblock->size >= XV_MIN_ALLOC_SIZE) {
-                       remove_block(pool, page,
-                                   offset + block->size + XV_ALIGN, tmpblock,
-                                   get_index_for_insert(tmpblock->size));
-               }
-               block->size += tmpblock->size + XV_ALIGN;
-       }
-
-       /* Merge previous block if its free */
-       if (test_flag(block, PREV_FREE)) {
-               tmpblock = (struct block_header *)((char *)(page_start) +
-                                               get_blockprev(block));
-               offset = offset - tmpblock->size - XV_ALIGN;
-
-               if (tmpblock->size >= XV_MIN_ALLOC_SIZE)
-                       remove_block(pool, page, offset, tmpblock,
-                                   get_index_for_insert(tmpblock->size));
-
-               tmpblock->size += block->size + XV_ALIGN;
-               block = tmpblock;
-       }
-
-       /* No used objects in this page. Free it. */
-       if (block->size == PAGE_SIZE - XV_ALIGN) {
-               put_ptr_atomic(page_start, KM_USER0);
-               spin_unlock(&pool->lock);
-
-               __free_page(page);
-               stat_dec(&pool->total_pages);
-               return;
-       }
-
-       set_flag(block, BLOCK_FREE);
-       if (block->size >= XV_MIN_ALLOC_SIZE)
-               insert_block(pool, page, offset, block);
-
-       if (offset + block->size + XV_ALIGN != PAGE_SIZE) {
-               tmpblock = BLOCK_NEXT(block);
-               set_flag(tmpblock, PREV_FREE);
-               set_blockprev(tmpblock, offset);
-       }
-
-       put_ptr_atomic(page_start, KM_USER0);
-       spin_unlock(&pool->lock);
-}
-
-u32 xv_get_object_size(void *obj)
-{
-       struct block_header *blk;
-
-       blk = (struct block_header *)((char *)(obj) - XV_ALIGN);
-       return blk->size;
-}
-
-/*
- * Returns total memory used by allocator (userdata + metadata)
- */
-u64 xv_get_total_size_bytes(struct xv_pool *pool)
-{
-       return pool->total_pages << PAGE_SHIFT;
-}
diff --git a/drivers/staging/ramzswap/xvmalloc.h b/drivers/staging/ramzswap/xvmalloc.h
deleted file mode 100644 (file)
index 5b1a81a..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * xvmalloc memory allocator
- *
- * 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
- */
-
-#ifndef _XV_MALLOC_H_
-#define _XV_MALLOC_H_
-
-#include <linux/types.h>
-
-struct xv_pool;
-
-struct xv_pool *xv_create_pool(void);
-void xv_destroy_pool(struct xv_pool *pool);
-
-int xv_malloc(struct xv_pool *pool, u32 size, struct page **page,
-                       u32 *offset, gfp_t flags);
-void xv_free(struct xv_pool *pool, struct page *page, u32 offset);
-
-u32 xv_get_object_size(void *obj);
-u64 xv_get_total_size_bytes(struct xv_pool *pool);
-
-#endif
diff --git a/drivers/staging/ramzswap/xvmalloc_int.h b/drivers/staging/ramzswap/xvmalloc_int.h
deleted file mode 100644 (file)
index e23ed5c..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * xvmalloc memory allocator
- *
- * 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
- */
-
-#ifndef _XV_MALLOC_INT_H_
-#define _XV_MALLOC_INT_H_
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-
-/* User configurable params */
-
-/* Must be power of two */
-#define XV_ALIGN_SHIFT 2
-#define XV_ALIGN       (1 << XV_ALIGN_SHIFT)
-#define XV_ALIGN_MASK  (XV_ALIGN - 1)
-
-/* This must be greater than sizeof(link_free) */
-#define XV_MIN_ALLOC_SIZE      32
-#define XV_MAX_ALLOC_SIZE      (PAGE_SIZE - XV_ALIGN)
-
-/* Free lists are separated by FL_DELTA bytes */
-#define FL_DELTA_SHIFT 3
-#define FL_DELTA       (1 << FL_DELTA_SHIFT)
-#define FL_DELTA_MASK  (FL_DELTA - 1)
-#define NUM_FREE_LISTS ((XV_MAX_ALLOC_SIZE - XV_MIN_ALLOC_SIZE) \
-                               / FL_DELTA + 1)
-
-#define MAX_FLI                DIV_ROUND_UP(NUM_FREE_LISTS, BITS_PER_LONG)
-
-/* End of user params */
-
-enum blockflags {
-       BLOCK_FREE,
-       PREV_FREE,
-       __NR_BLOCKFLAGS,
-};
-
-#define FLAGS_MASK     XV_ALIGN_MASK
-#define PREV_MASK      (~FLAGS_MASK)
-
-struct freelist_entry {
-       struct page *page;
-       u16 offset;
-       u16 pad;
-};
-
-struct link_free {
-       struct page *prev_page;
-       struct page *next_page;
-       u16 prev_offset;
-       u16 next_offset;
-};
-
-struct block_header {
-       union {
-               /* This common header must be XV_ALIGN bytes */
-               u8 common[XV_ALIGN];
-               struct {
-                       u16 size;
-                       u16 prev;
-               };
-       };
-       struct link_free link;
-};
-
-struct xv_pool {
-       ulong flbitmap;
-       ulong slbitmap[MAX_FLI];
-       spinlock_t lock;
-
-       struct freelist_entry freelist[NUM_FREE_LISTS];
-
-       /* stats */
-       u64 total_pages;
-};
-
-#endif
diff --git a/drivers/staging/zram/Kconfig b/drivers/staging/zram/Kconfig
new file mode 100644 (file)
index 0000000..5bcfe16
--- /dev/null
@@ -0,0 +1,28 @@
+config ZRAM
+       tristate "Compressed RAM block device support"
+       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: http://compcache.googlecode.com/
+
+config ZRAM_STATS
+       bool "Enable statistics for compressed RAM disks"
+       depends on ZRAM
+       default y
+       help
+         Enable statistics collection for compressed RAM devices. Statistics
+         are exported through ioctl interface, so you have to use zramconfig
+         program to get them. This adds only a minimal overhead.
+
+         If unsure, say Y.
+
diff --git a/drivers/staging/zram/Makefile b/drivers/staging/zram/Makefile
new file mode 100644 (file)
index 0000000..b2c087a
--- /dev/null
@@ -0,0 +1,3 @@
+zram-objs      :=      zram_drv.o xvmalloc.o
+
+obj-$(CONFIG_ZRAM)     +=      zram.o
diff --git a/drivers/staging/zram/xvmalloc.c b/drivers/staging/zram/xvmalloc.c
new file mode 100644 (file)
index 0000000..3fdbb8a
--- /dev/null
@@ -0,0 +1,507 @@
+/*
+ * xvmalloc memory allocator
+ *
+ * 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
+ */
+
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/highmem.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include "xvmalloc.h"
+#include "xvmalloc_int.h"
+
+static void stat_inc(u64 *value)
+{
+       *value = *value + 1;
+}
+
+static void stat_dec(u64 *value)
+{
+       *value = *value - 1;
+}
+
+static int test_flag(struct block_header *block, enum blockflags flag)
+{
+       return block->prev & BIT(flag);
+}
+
+static void set_flag(struct block_header *block, enum blockflags flag)
+{
+       block->prev |= BIT(flag);
+}
+
+static void clear_flag(struct block_header *block, enum blockflags flag)
+{
+       block->prev &= ~BIT(flag);
+}
+
+/*
+ * Given <page, offset> pair, provide a derefrencable pointer.
+ * This is called from xv_malloc/xv_free path, so it
+ * needs to be fast.
+ */
+static void *get_ptr_atomic(struct page *page, u16 offset, enum km_type type)
+{
+       unsigned char *base;
+
+       base = kmap_atomic(page, type);
+       return base + offset;
+}
+
+static void put_ptr_atomic(void *ptr, enum km_type type)
+{
+       kunmap_atomic(ptr, type);
+}
+
+static u32 get_blockprev(struct block_header *block)
+{
+       return block->prev & PREV_MASK;
+}
+
+static void set_blockprev(struct block_header *block, u16 new_offset)
+{
+       block->prev = new_offset | (block->prev & FLAGS_MASK);
+}
+
+static struct block_header *BLOCK_NEXT(struct block_header *block)
+{
+       return (struct block_header *)
+               ((char *)block + block->size + XV_ALIGN);
+}
+
+/*
+ * Get index of free list containing blocks of maximum size
+ * which is less than or equal to given size.
+ */
+static u32 get_index_for_insert(u32 size)
+{
+       if (unlikely(size > XV_MAX_ALLOC_SIZE))
+               size = XV_MAX_ALLOC_SIZE;
+       size &= ~FL_DELTA_MASK;
+       return (size - XV_MIN_ALLOC_SIZE) >> FL_DELTA_SHIFT;
+}
+
+/*
+ * Get index of free list having blocks of size greater than
+ * or equal to requested size.
+ */
+static u32 get_index(u32 size)
+{
+       if (unlikely(size < XV_MIN_ALLOC_SIZE))
+               size = XV_MIN_ALLOC_SIZE;
+       size = ALIGN(size, FL_DELTA);
+       return (size - XV_MIN_ALLOC_SIZE) >> FL_DELTA_SHIFT;
+}
+
+/**
+ * find_block - find block of at least given size
+ * @pool: memory pool to search from
+ * @size: size of block required
+ * @page: page containing required block
+ * @offset: offset within the page where block is located.
+ *
+ * Searches two level bitmap to locate block of at least
+ * the given size. If such a block is found, it provides
+ * <page, offset> to identify this block and returns index
+ * in freelist where we found this block.
+ * Otherwise, returns 0 and <page, offset> params are not touched.
+ */
+static u32 find_block(struct xv_pool *pool, u32 size,
+                       struct page **page, u32 *offset)
+{
+       ulong flbitmap, slbitmap;
+       u32 flindex, slindex, slbitstart;
+
+       /* There are no free blocks in this pool */
+       if (!pool->flbitmap)
+               return 0;
+
+       /* Get freelist index correspoding to this size */
+       slindex = get_index(size);
+       slbitmap = pool->slbitmap[slindex / BITS_PER_LONG];
+       slbitstart = slindex % BITS_PER_LONG;
+
+       /*
+        * If freelist is not empty at this index, we found the
+        * block - head of this list. This is approximate best-fit match.
+        */
+       if (test_bit(slbitstart, &slbitmap)) {
+               *page = pool->freelist[slindex].page;
+               *offset = pool->freelist[slindex].offset;
+               return slindex;
+       }
+
+       /*
+        * No best-fit found. Search a bit further in bitmap for a free block.
+        * Second level bitmap consists of series of 32-bit chunks. Search
+        * further in the chunk where we expected a best-fit, starting from
+        * index location found above.
+        */
+       slbitstart++;
+       slbitmap >>= slbitstart;
+
+       /* Skip this search if we were already at end of this bitmap chunk */
+       if ((slbitstart != BITS_PER_LONG) && slbitmap) {
+               slindex += __ffs(slbitmap) + 1;
+               *page = pool->freelist[slindex].page;
+               *offset = pool->freelist[slindex].offset;
+               return slindex;
+       }
+
+       /* Now do a full two-level bitmap search to find next nearest fit */
+       flindex = slindex / BITS_PER_LONG;
+
+       flbitmap = (pool->flbitmap) >> (flindex + 1);
+       if (!flbitmap)
+               return 0;
+
+       flindex += __ffs(flbitmap) + 1;
+       slbitmap = pool->slbitmap[flindex];
+       slindex = (flindex * BITS_PER_LONG) + __ffs(slbitmap);
+       *page = pool->freelist[slindex].page;
+       *offset = pool->freelist[slindex].offset;
+
+       return slindex;
+}
+
+/*
+ * Insert block at <page, offset> in freelist of given pool.
+ * freelist used depends on block size.
+ */
+static void insert_block(struct xv_pool *pool, struct page *page, u32 offset,
+                       struct block_header *block)
+{
+       u32 flindex, slindex;
+       struct block_header *nextblock;
+
+       slindex = get_index_for_insert(block->size);
+       flindex = slindex / BITS_PER_LONG;
+
+       block->link.prev_page = 0;
+       block->link.prev_offset = 0;
+       block->link.next_page = pool->freelist[slindex].page;
+       block->link.next_offset = pool->freelist[slindex].offset;
+       pool->freelist[slindex].page = page;
+       pool->freelist[slindex].offset = offset;
+
+       if (block->link.next_page) {
+               nextblock = get_ptr_atomic(block->link.next_page,
+                                       block->link.next_offset, KM_USER1);
+               nextblock->link.prev_page = page;
+               nextblock->link.prev_offset = offset;
+               put_ptr_atomic(nextblock, KM_USER1);
+       }
+
+       __set_bit(slindex % BITS_PER_LONG, &pool->slbitmap[flindex]);
+       __set_bit(flindex, &pool->flbitmap);
+}
+
+/*
+ * Remove block from head of freelist. Index 'slindex' identifies the freelist.
+ */
+static void remove_block_head(struct xv_pool *pool,
+                       struct block_header *block, u32 slindex)
+{
+       struct block_header *tmpblock;
+       u32 flindex = slindex / BITS_PER_LONG;
+
+       pool->freelist[slindex].page = block->link.next_page;
+       pool->freelist[slindex].offset = block->link.next_offset;
+       block->link.prev_page = 0;
+       block->link.prev_offset = 0;
+
+       if (!pool->freelist[slindex].page) {
+               __clear_bit(slindex % BITS_PER_LONG, &pool->slbitmap[flindex]);
+               if (!pool->slbitmap[flindex])
+                       __clear_bit(flindex, &pool->flbitmap);
+       } else {
+               /*
+                * DEBUG ONLY: We need not reinitialize freelist head previous
+                * pointer to 0 - we never depend on its value. But just for
+                * sanity, lets do it.
+                */
+               tmpblock = get_ptr_atomic(pool->freelist[slindex].page,
+                               pool->freelist[slindex].offset, KM_USER1);
+               tmpblock->link.prev_page = 0;
+               tmpblock->link.prev_offset = 0;
+               put_ptr_atomic(tmpblock, KM_USER1);
+       }
+}
+
+/*
+ * Remove block from freelist. Index 'slindex' identifies the freelist.
+ */
+static void remove_block(struct xv_pool *pool, struct page *page, u32 offset,
+                       struct block_header *block, u32 slindex)
+{
+       u32 flindex;
+       struct block_header *tmpblock;
+
+       if (pool->freelist[slindex].page == page
+          && pool->freelist[slindex].offset == offset) {
+               remove_block_head(pool, block, slindex);
+               return;
+       }
+
+       flindex = slindex / BITS_PER_LONG;
+
+       if (block->link.prev_page) {
+               tmpblock = get_ptr_atomic(block->link.prev_page,
+                               block->link.prev_offset, KM_USER1);
+               tmpblock->link.next_page = block->link.next_page;
+               tmpblock->link.next_offset = block->link.next_offset;
+               put_ptr_atomic(tmpblock, KM_USER1);
+       }
+
+       if (block->link.next_page) {
+               tmpblock = get_ptr_atomic(block->link.next_page,
+                               block->link.next_offset, KM_USER1);
+               tmpblock->link.prev_page = block->link.prev_page;
+               tmpblock->link.prev_offset = block->link.prev_offset;
+               put_ptr_atomic(tmpblock, KM_USER1);
+       }
+}
+
+/*
+ * Allocate a page and add it to freelist of given pool.
+ */
+static int grow_pool(struct xv_pool *pool, gfp_t flags)
+{
+       struct page *page;
+       struct block_header *block;
+
+       page = alloc_page(flags);
+       if (unlikely(!page))
+               return -ENOMEM;
+
+       stat_inc(&pool->total_pages);
+
+       spin_lock(&pool->lock);
+       block = get_ptr_atomic(page, 0, KM_USER0);
+
+       block->size = PAGE_SIZE - XV_ALIGN;
+       set_flag(block, BLOCK_FREE);
+       clear_flag(block, PREV_FREE);
+       set_blockprev(block, 0);
+
+       insert_block(pool, page, 0, block);
+
+       put_ptr_atomic(block, KM_USER0);
+       spin_unlock(&pool->lock);
+
+       return 0;
+}
+
+/*
+ * Create a memory pool. Allocates freelist, bitmaps and other
+ * per-pool metadata.
+ */
+struct xv_pool *xv_create_pool(void)
+{
+       u32 ovhd_size;
+       struct xv_pool *pool;
+
+       ovhd_size = roundup(sizeof(*pool), PAGE_SIZE);
+       pool = kzalloc(ovhd_size, GFP_KERNEL);
+       if (!pool)
+               return NULL;
+
+       spin_lock_init(&pool->lock);
+
+       return pool;
+}
+
+void xv_destroy_pool(struct xv_pool *pool)
+{
+       kfree(pool);
+}
+
+/**
+ * xv_malloc - Allocate block of given size from pool.
+ * @pool: pool to allocate from
+ * @size: size of block to allocate
+ * @page: page no. that holds the object
+ * @offset: location of object within page
+ *
+ * On success, <page, offset> identifies block allocated
+ * and 0 is returned. On failure, <page, offset> is set to
+ * 0 and -ENOMEM is returned.
+ *
+ * Allocation requests with size > XV_MAX_ALLOC_SIZE will fail.
+ */
+int xv_malloc(struct xv_pool *pool, u32 size, struct page **page,
+               u32 *offset, gfp_t flags)
+{
+       int error;
+       u32 index, tmpsize, origsize, tmpoffset;
+       struct block_header *block, *tmpblock;
+
+       *page = NULL;
+       *offset = 0;
+       origsize = size;
+
+       if (unlikely(!size || size > XV_MAX_ALLOC_SIZE))
+               return -ENOMEM;
+
+       size = ALIGN(size, XV_ALIGN);
+
+       spin_lock(&pool->lock);
+
+       index = find_block(pool, size, page, offset);
+
+       if (!*page) {
+               spin_unlock(&pool->lock);
+               if (flags & GFP_NOWAIT)
+                       return -ENOMEM;
+               error = grow_pool(pool, flags);
+               if (unlikely(error))
+                       return error;
+
+               spin_lock(&pool->lock);
+               index = find_block(pool, size, page, offset);
+       }
+
+       if (!*page) {
+               spin_unlock(&pool->lock);
+               return -ENOMEM;
+       }
+
+       block = get_ptr_atomic(*page, *offset, KM_USER0);
+
+       remove_block_head(pool, block, index);
+
+       /* Split the block if required */
+       tmpoffset = *offset + size + XV_ALIGN;
+       tmpsize = block->size - size;
+       tmpblock = (struct block_header *)((char *)block + size + XV_ALIGN);
+       if (tmpsize) {
+               tmpblock->size = tmpsize - XV_ALIGN;
+               set_flag(tmpblock, BLOCK_FREE);
+               clear_flag(tmpblock, PREV_FREE);
+
+               set_blockprev(tmpblock, *offset);
+               if (tmpblock->size >= XV_MIN_ALLOC_SIZE)
+                       insert_block(pool, *page, tmpoffset, tmpblock);
+
+               if (tmpoffset + XV_ALIGN + tmpblock->size != PAGE_SIZE) {
+                       tmpblock = BLOCK_NEXT(tmpblock);
+                       set_blockprev(tmpblock, tmpoffset);
+               }
+       } else {
+               /* This block is exact fit */
+               if (tmpoffset != PAGE_SIZE)
+                       clear_flag(tmpblock, PREV_FREE);
+       }
+
+       block->size = origsize;
+       clear_flag(block, BLOCK_FREE);
+
+       put_ptr_atomic(block, KM_USER0);
+       spin_unlock(&pool->lock);
+
+       *offset += XV_ALIGN;
+
+       return 0;
+}
+
+/*
+ * Free block identified with <page, offset>
+ */
+void xv_free(struct xv_pool *pool, struct page *page, u32 offset)
+{
+       void *page_start;
+       struct block_header *block, *tmpblock;
+
+       offset -= XV_ALIGN;
+
+       spin_lock(&pool->lock);
+
+       page_start = get_ptr_atomic(page, 0, KM_USER0);
+       block = (struct block_header *)((char *)page_start + offset);
+
+       /* Catch double free bugs */
+       BUG_ON(test_flag(block, BLOCK_FREE));
+
+       block->size = ALIGN(block->size, XV_ALIGN);
+
+       tmpblock = BLOCK_NEXT(block);
+       if (offset + block->size + XV_ALIGN == PAGE_SIZE)
+               tmpblock = NULL;
+
+       /* Merge next block if its free */
+       if (tmpblock && test_flag(tmpblock, BLOCK_FREE)) {
+               /*
+                * Blocks smaller than XV_MIN_ALLOC_SIZE
+                * are not inserted in any free list.
+                */
+               if (tmpblock->size >= XV_MIN_ALLOC_SIZE) {
+                       remove_block(pool, page,
+                                   offset + block->size + XV_ALIGN, tmpblock,
+                                   get_index_for_insert(tmpblock->size));
+               }
+               block->size += tmpblock->size + XV_ALIGN;
+       }
+
+       /* Merge previous block if its free */
+       if (test_flag(block, PREV_FREE)) {
+               tmpblock = (struct block_header *)((char *)(page_start) +
+                                               get_blockprev(block));
+               offset = offset - tmpblock->size - XV_ALIGN;
+
+               if (tmpblock->size >= XV_MIN_ALLOC_SIZE)
+                       remove_block(pool, page, offset, tmpblock,
+                                   get_index_for_insert(tmpblock->size));
+
+               tmpblock->size += block->size + XV_ALIGN;
+               block = tmpblock;
+       }
+
+       /* No used objects in this page. Free it. */
+       if (block->size == PAGE_SIZE - XV_ALIGN) {
+               put_ptr_atomic(page_start, KM_USER0);
+               spin_unlock(&pool->lock);
+
+               __free_page(page);
+               stat_dec(&pool->total_pages);
+               return;
+       }
+
+       set_flag(block, BLOCK_FREE);
+       if (block->size >= XV_MIN_ALLOC_SIZE)
+               insert_block(pool, page, offset, block);
+
+       if (offset + block->size + XV_ALIGN != PAGE_SIZE) {
+               tmpblock = BLOCK_NEXT(block);
+               set_flag(tmpblock, PREV_FREE);
+               set_blockprev(tmpblock, offset);
+       }
+
+       put_ptr_atomic(page_start, KM_USER0);
+       spin_unlock(&pool->lock);
+}
+
+u32 xv_get_object_size(void *obj)
+{
+       struct block_header *blk;
+
+       blk = (struct block_header *)((char *)(obj) - XV_ALIGN);
+       return blk->size;
+}
+
+/*
+ * Returns total memory used by allocator (userdata + metadata)
+ */
+u64 xv_get_total_size_bytes(struct xv_pool *pool)
+{
+       return pool->total_pages << PAGE_SHIFT;
+}
diff --git a/drivers/staging/zram/xvmalloc.h b/drivers/staging/zram/xvmalloc.h
new file mode 100644 (file)
index 0000000..5b1a81a
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * xvmalloc memory allocator
+ *
+ * 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
+ */
+
+#ifndef _XV_MALLOC_H_
+#define _XV_MALLOC_H_
+
+#include <linux/types.h>
+
+struct xv_pool;
+
+struct xv_pool *xv_create_pool(void);
+void xv_destroy_pool(struct xv_pool *pool);
+
+int xv_malloc(struct xv_pool *pool, u32 size, struct page **page,
+                       u32 *offset, gfp_t flags);
+void xv_free(struct xv_pool *pool, struct page *page, u32 offset);
+
+u32 xv_get_object_size(void *obj);
+u64 xv_get_total_size_bytes(struct xv_pool *pool);
+
+#endif
diff --git a/drivers/staging/zram/xvmalloc_int.h b/drivers/staging/zram/xvmalloc_int.h
new file mode 100644 (file)
index 0000000..e23ed5c
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * xvmalloc memory allocator
+ *
+ * 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
+ */
+
+#ifndef _XV_MALLOC_INT_H_
+#define _XV_MALLOC_INT_H_
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+/* User configurable params */
+
+/* Must be power of two */
+#define XV_ALIGN_SHIFT 2
+#define XV_ALIGN       (1 << XV_ALIGN_SHIFT)
+#define XV_ALIGN_MASK  (XV_ALIGN - 1)
+
+/* This must be greater than sizeof(link_free) */
+#define XV_MIN_ALLOC_SIZE      32
+#define XV_MAX_ALLOC_SIZE      (PAGE_SIZE - XV_ALIGN)
+
+/* Free lists are separated by FL_DELTA bytes */
+#define FL_DELTA_SHIFT 3
+#define FL_DELTA       (1 << FL_DELTA_SHIFT)
+#define FL_DELTA_MASK  (FL_DELTA - 1)
+#define NUM_FREE_LISTS ((XV_MAX_ALLOC_SIZE - XV_MIN_ALLOC_SIZE) \
+                               / FL_DELTA + 1)
+
+#define MAX_FLI                DIV_ROUND_UP(NUM_FREE_LISTS, BITS_PER_LONG)
+
+/* End of user params */
+
+enum blockflags {
+       BLOCK_FREE,
+       PREV_FREE,
+       __NR_BLOCKFLAGS,
+};
+
+#define FLAGS_MASK     XV_ALIGN_MASK
+#define PREV_MASK      (~FLAGS_MASK)
+
+struct freelist_entry {
+       struct page *page;
+       u16 offset;
+       u16 pad;
+};
+
+struct link_free {
+       struct page *prev_page;
+       struct page *next_page;
+       u16 prev_offset;
+       u16 next_offset;
+};
+
+struct block_header {
+       union {
+               /* This common header must be XV_ALIGN bytes */
+               u8 common[XV_ALIGN];
+               struct {
+                       u16 size;
+                       u16 prev;
+               };
+       };
+       struct link_free link;
+};
+
+struct xv_pool {
+       ulong flbitmap;
+       ulong slbitmap[MAX_FLI];
+       spinlock_t lock;
+
+       struct freelist_entry freelist[NUM_FREE_LISTS];
+
+       /* stats */
+       u64 total_pages;
+};
+
+#endif
diff --git a/drivers/staging/zram/zram.txt b/drivers/staging/zram/zram.txt
new file mode 100644 (file)
index 0000000..9694acf
--- /dev/null
@@ -0,0 +1,51 @@
+ramzswap: Compressed RAM based swap device
+-------------------------------------------
+
+Project home: http://compcache.googlecode.com/
+
+* Introduction
+
+The ramzswap module creates RAM based block devices which can (only) be used as
+swap disks. Pages swapped to these devices are compressed and stored in memory
+itself. See project home for use cases, performance numbers and a lot more.
+
+Individual ramzswap devices are configured and initialized using rzscontrol
+userspace utility as shown in examples below. See rzscontrol man page for more
+details.
+
+* Usage
+
+Following shows a typical sequence of steps for using ramzswap.
+
+1) Load Modules:
+       modprobe ramzswap num_devices=4
+       This creates 4 (uninitialized) devices: /dev/ramzswap{0,1,2,3}
+       (num_devices parameter is optional. Default: 1)
+
+2) Initialize:
+       Use rzscontrol utility to configure and initialize individual
+       ramzswap devices. Example:
+       rzscontrol /dev/ramzswap2 --init # uses default value of disksize_kb
+
+       *See rzscontrol man page for more details and examples*
+
+3) Activate:
+       swapon /dev/ramzswap2 # or any other initialized ramzswap device
+
+4) Stats:
+       rzscontrol /dev/ramzswap2 --stats
+
+5) Deactivate:
+       swapoff /dev/ramzswap2
+
+6) Reset:
+       rzscontrol /dev/ramzswap2 --reset
+       (This frees all the memory allocated for this 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
new file mode 100644 (file)
index 0000000..e9b064c
--- /dev/null
@@ -0,0 +1,805 @@
+/*
+ * Compressed RAM based swap 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 "ramzswap"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.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/swap.h>
+#include <linux/swapops.h>
+#include <linux/vmalloc.h>
+
+#include "zram_drv.h"
+
+/* Globals */
+static int ramzswap_major;
+static struct ramzswap *devices;
+
+/* Module params (documentation at end) */
+static unsigned int num_devices;
+
+static int rzs_test_flag(struct ramzswap *rzs, u32 index,
+                       enum rzs_pageflags flag)
+{
+       return rzs->table[index].flags & BIT(flag);
+}
+
+static void rzs_set_flag(struct ramzswap *rzs, u32 index,
+                       enum rzs_pageflags flag)
+{
+       rzs->table[index].flags |= BIT(flag);
+}
+
+static void rzs_clear_flag(struct ramzswap *rzs, u32 index,
+                       enum rzs_pageflags flag)
+{
+       rzs->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 ramzswap_set_disksize(struct ramzswap *rzs, size_t totalram_bytes)
+{
+       if (!rzs->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
+               );
+               rzs->disksize = default_disksize_perc_ram *
+                                       (totalram_bytes / 100);
+       }
+
+       if (rzs->disksize > 2 * (totalram_bytes)) {
+               pr_info(
+               "There is little point creating a ramzswap of greater than "
+               "twice the size of memory since we expect a 2:1 compression "
+               "ratio. Note that ramzswap uses about 0.1%% of the size of "
+               "the swap device when not in use so a huge ramzswap is "
+               "wasteful.\n"
+               "\tMemory Size: %zu kB\n"
+               "\tSize you selected: %zu kB\n"
+               "Continuing anyway ...\n",
+               totalram_bytes >> 10, rzs->disksize
+               );
+       }
+
+       rzs->disksize &= PAGE_MASK;
+}
+
+static void ramzswap_ioctl_get_stats(struct ramzswap *rzs,
+                       struct ramzswap_ioctl_stats *s)
+{
+       s->disksize = rzs->disksize;
+
+#if defined(CONFIG_RAMZSWAP_STATS)
+       {
+       struct ramzswap_stats *rs = &rzs->stats;
+       size_t succ_writes, mem_used;
+       unsigned int good_compress_perc = 0, no_compress_perc = 0;
+
+       mem_used = xv_get_total_size_bytes(rzs->mem_pool)
+                       + (rs->pages_expand << PAGE_SHIFT);
+       succ_writes = rzs_stat64_read(rzs, &rs->num_writes) -
+                       rzs_stat64_read(rzs, &rs->failed_writes);
+
+       if (succ_writes && rs->pages_stored) {
+               good_compress_perc = rs->good_compress * 100
+                                       / rs->pages_stored;
+               no_compress_perc = rs->pages_expand * 100
+                                       / rs->pages_stored;
+       }
+
+       s->num_reads = rzs_stat64_read(rzs, &rs->num_reads);
+       s->num_writes = rzs_stat64_read(rzs, &rs->num_writes);
+       s->failed_reads = rzs_stat64_read(rzs, &rs->failed_reads);
+       s->failed_writes = rzs_stat64_read(rzs, &rs->failed_writes);
+       s->invalid_io = rzs_stat64_read(rzs, &rs->invalid_io);
+       s->notify_free = rzs_stat64_read(rzs, &rs->notify_free);
+       s->pages_zero = rs->pages_zero;
+
+       s->good_compress_pct = good_compress_perc;
+       s->pages_expand_pct = no_compress_perc;
+
+       s->pages_stored = rs->pages_stored;
+       s->pages_used = mem_used >> PAGE_SHIFT;
+       s->orig_data_size = rs->pages_stored << PAGE_SHIFT;
+       s->compr_data_size = rs->compr_size;
+       s->mem_used_total = mem_used;
+       }
+#endif /* CONFIG_RAMZSWAP_STATS */
+}
+
+static void ramzswap_free_page(struct ramzswap *rzs, size_t index)
+{
+       u32 clen;
+       void *obj;
+
+       struct page *page = rzs->table[index].page;
+       u32 offset = rzs->table[index].offset;
+
+       if (unlikely(!page)) {
+               /*
+                * No memory is allocated for zero filled pages.
+                * Simply clear zero page flag.
+                */
+               if (rzs_test_flag(rzs, index, RZS_ZERO)) {
+                       rzs_clear_flag(rzs, index, RZS_ZERO);
+                       rzs_stat_dec(&rzs->stats.pages_zero);
+               }
+               return;
+       }
+
+       if (unlikely(rzs_test_flag(rzs, index, RZS_UNCOMPRESSED))) {
+               clen = PAGE_SIZE;
+               __free_page(page);
+               rzs_clear_flag(rzs, index, RZS_UNCOMPRESSED);
+               rzs_stat_dec(&rzs->stats.pages_expand);
+               goto out;
+       }
+
+       obj = kmap_atomic(page, KM_USER0) + offset;
+       clen = xv_get_object_size(obj) - sizeof(struct zobj_header);
+       kunmap_atomic(obj, KM_USER0);
+
+       xv_free(rzs->mem_pool, page, offset);
+       if (clen <= PAGE_SIZE / 2)
+               rzs_stat_dec(&rzs->stats.good_compress);
+
+out:
+       rzs->stats.compr_size -= clen;
+       rzs_stat_dec(&rzs->stats.pages_stored);
+
+       rzs->table[index].page = NULL;
+       rzs->table[index].offset = 0;
+}
+
+static void handle_zero_page(struct page *page)
+{
+       void *user_mem;
+
+       user_mem = kmap_atomic(page, KM_USER0);
+       memset(user_mem, 0, PAGE_SIZE);
+       kunmap_atomic(user_mem, KM_USER0);
+
+       flush_dcache_page(page);
+}
+
+static void handle_uncompressed_page(struct ramzswap *rzs,
+                               struct page *page, u32 index)
+{
+       unsigned char *user_mem, *cmem;
+
+       user_mem = kmap_atomic(page, KM_USER0);
+       cmem = kmap_atomic(rzs->table[index].page, KM_USER1) +
+                       rzs->table[index].offset;
+
+       memcpy(user_mem, cmem, PAGE_SIZE);
+       kunmap_atomic(user_mem, KM_USER0);
+       kunmap_atomic(cmem, KM_USER1);
+
+       flush_dcache_page(page);
+}
+
+static int ramzswap_read(struct ramzswap *rzs, struct bio *bio)
+{
+
+       int i;
+       u32 index;
+       struct bio_vec *bvec;
+
+       rzs_stat64_inc(rzs, &rzs->stats.num_reads);
+
+       index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT;
+       bio_for_each_segment(bvec, bio, i) {
+               int ret;
+               size_t clen;
+               struct page *page;
+               struct zobj_header *zheader;
+               unsigned char *user_mem, *cmem;
+
+               page = bvec->bv_page;
+
+               if (rzs_test_flag(rzs, index, RZS_ZERO)) {
+                       handle_zero_page(page);
+                       continue;
+               }
+
+               /* Requested page is not present in compressed area */
+               if (unlikely(!rzs->table[index].page)) {
+                       pr_debug("Read before write: sector=%lu, size=%u",
+                               (ulong)(bio->bi_sector), bio->bi_size);
+                       /* Do nothing */
+                       continue;
+               }
+
+               /* Page is stored uncompressed since it's incompressible */
+               if (unlikely(rzs_test_flag(rzs, index, RZS_UNCOMPRESSED))) {
+                       handle_uncompressed_page(rzs, page, index);
+                       continue;
+               }
+
+               user_mem = kmap_atomic(page, KM_USER0);
+               clen = PAGE_SIZE;
+
+               cmem = kmap_atomic(rzs->table[index].page, KM_USER1) +
+                               rzs->table[index].offset;
+
+               ret = lzo1x_decompress_safe(
+                       cmem + sizeof(*zheader),
+                       xv_get_object_size(cmem) - sizeof(*zheader),
+                       user_mem, &clen);
+
+               kunmap_atomic(user_mem, KM_USER0);
+               kunmap_atomic(cmem, KM_USER1);
+
+               /* 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);
+                       rzs_stat64_inc(rzs, &rzs->stats.failed_reads);
+                       goto out;
+               }
+
+               flush_dcache_page(page);
+               index++;
+       }
+
+       set_bit(BIO_UPTODATE, &bio->bi_flags);
+       bio_endio(bio, 0);
+       return 0;
+
+out:
+       bio_io_error(bio);
+       return 0;
+}
+
+static int ramzswap_write(struct ramzswap *rzs, struct bio *bio)
+{
+       int i;
+       u32 index;
+       struct bio_vec *bvec;
+
+       rzs_stat64_inc(rzs, &rzs->stats.num_writes);
+
+       index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT;
+
+       bio_for_each_segment(bvec, bio, i) {
+               int ret;
+               u32 offset;
+               size_t clen;
+               struct zobj_header *zheader;
+               struct page *page, *page_store;
+               unsigned char *user_mem, *cmem, *src;
+
+               page = bvec->bv_page;
+               src = rzs->compress_buffer;
+
+               /*
+                * System overwrites unused sectors. Free memory associated
+                * with this sector now.
+                */
+               if (rzs->table[index].page ||
+                               rzs_test_flag(rzs, index, RZS_ZERO))
+                       ramzswap_free_page(rzs, index);
+
+               mutex_lock(&rzs->lock);
+
+               user_mem = kmap_atomic(page, KM_USER0);
+               if (page_zero_filled(user_mem)) {
+                       kunmap_atomic(user_mem, KM_USER0);
+                       mutex_unlock(&rzs->lock);
+                       rzs_stat_inc(&rzs->stats.pages_zero);
+                       rzs_set_flag(rzs, index, RZS_ZERO);
+                       continue;
+               }
+
+               ret = lzo1x_1_compress(user_mem, PAGE_SIZE, src, &clen,
+                                       rzs->compress_workmem);
+
+               kunmap_atomic(user_mem, KM_USER0);
+
+               if (unlikely(ret != LZO_E_OK)) {
+                       mutex_unlock(&rzs->lock);
+                       pr_err("Compression failed! err=%d\n", ret);
+                       rzs_stat64_inc(rzs, &rzs->stats.failed_writes);
+                       goto out;
+               }
+
+               /*
+                * Page is incompressible. Store it as-is (uncompressed)
+                * since we do not want to return too many swap 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)) {
+                               mutex_unlock(&rzs->lock);
+                               pr_info("Error allocating memory for "
+                                       "incompressible page: %u\n", index);
+                               rzs_stat64_inc(rzs, &rzs->stats.failed_writes);
+                               goto out;
+                       }
+
+                       offset = 0;
+                       rzs_set_flag(rzs, index, RZS_UNCOMPRESSED);
+                       rzs_stat_inc(&rzs->stats.pages_expand);
+                       rzs->table[index].page = page_store;
+                       src = kmap_atomic(page, KM_USER0);
+                       goto memstore;
+               }
+
+               if (xv_malloc(rzs->mem_pool, clen + sizeof(*zheader),
+                               &rzs->table[index].page, &offset,
+                               GFP_NOIO | __GFP_HIGHMEM)) {
+                       mutex_unlock(&rzs->lock);
+                       pr_info("Error allocating memory for compressed "
+                               "page: %u, size=%zu\n", index, clen);
+                       rzs_stat64_inc(rzs, &rzs->stats.failed_writes);
+                       goto out;
+               }
+
+memstore:
+               rzs->table[index].offset = offset;
+
+               cmem = kmap_atomic(rzs->table[index].page, KM_USER1) +
+                               rzs->table[index].offset;
+
+#if 0
+               /* Back-reference needed for memory defragmentation */
+               if (!rzs_test_flag(rzs, index, RZS_UNCOMPRESSED)) {
+                       zheader = (struct zobj_header *)cmem;
+                       zheader->table_idx = index;
+                       cmem += sizeof(*zheader);
+               }
+#endif
+
+               memcpy(cmem, src, clen);
+
+               kunmap_atomic(cmem, KM_USER1);
+               if (unlikely(rzs_test_flag(rzs, index, RZS_UNCOMPRESSED)))
+                       kunmap_atomic(src, KM_USER0);
+
+               /* Update stats */
+               rzs->stats.compr_size += clen;
+               rzs_stat_inc(&rzs->stats.pages_stored);
+               if (clen <= PAGE_SIZE / 2)
+                       rzs_stat_inc(&rzs->stats.good_compress);
+
+               mutex_unlock(&rzs->lock);
+               index++;
+       }
+
+       set_bit(BIO_UPTODATE, &bio->bi_flags);
+       bio_endio(bio, 0);
+       return 0;
+
+out:
+       bio_io_error(bio);
+       return 0;
+}
+
+/*
+ * Check if request is within bounds and page aligned.
+ */
+static inline int valid_io_request(struct ramzswap *rzs, struct bio *bio)
+{
+       if (unlikely(
+               (bio->bi_sector >= (rzs->disksize >> SECTOR_SHIFT)) ||
+               (bio->bi_sector & (SECTORS_PER_PAGE - 1)) ||
+               (bio->bi_size & (PAGE_SIZE - 1)))) {
+
+               return 0;
+       }
+
+       /* I/O request is valid */
+       return 1;
+}
+
+/*
+ * Handler function for all ramzswap I/O requests.
+ */
+static int ramzswap_make_request(struct request_queue *queue, struct bio *bio)
+{
+       int ret = 0;
+       struct ramzswap *rzs = queue->queuedata;
+
+       if (unlikely(!rzs->init_done)) {
+               bio_io_error(bio);
+               return 0;
+       }
+
+       if (!valid_io_request(rzs, bio)) {
+               rzs_stat64_inc(rzs, &rzs->stats.invalid_io);
+               bio_io_error(bio);
+               return 0;
+       }
+
+       switch (bio_data_dir(bio)) {
+       case READ:
+               ret = ramzswap_read(rzs, bio);
+               break;
+
+       case WRITE:
+               ret = ramzswap_write(rzs, bio);
+               break;
+       }
+
+       return ret;
+}
+
+static void reset_device(struct ramzswap *rzs)
+{
+       size_t index;
+
+       /* Do not accept any new I/O request */
+       rzs->init_done = 0;
+
+       /* Free various per-device buffers */
+       kfree(rzs->compress_workmem);
+       free_pages((unsigned long)rzs->compress_buffer, 1);
+
+       rzs->compress_workmem = NULL;
+       rzs->compress_buffer = NULL;
+
+       /* Free all pages that are still in this ramzswap device */
+       for (index = 0; index < rzs->disksize >> PAGE_SHIFT; index++) {
+               struct page *page;
+               u16 offset;
+
+               page = rzs->table[index].page;
+               offset = rzs->table[index].offset;
+
+               if (!page)
+                       continue;
+
+               if (unlikely(rzs_test_flag(rzs, index, RZS_UNCOMPRESSED)))
+                       __free_page(page);
+               else
+                       xv_free(rzs->mem_pool, page, offset);
+       }
+
+       vfree(rzs->table);
+       rzs->table = NULL;
+
+       xv_destroy_pool(rzs->mem_pool);
+       rzs->mem_pool = NULL;
+
+       /* Reset stats */
+       memset(&rzs->stats, 0, sizeof(rzs->stats));
+
+       rzs->disksize = 0;
+}
+
+static int ramzswap_ioctl_init_device(struct ramzswap *rzs)
+{
+       int ret;
+       size_t num_pages;
+
+       if (rzs->init_done) {
+               pr_info("Device already initialized!\n");
+               return -EBUSY;
+       }
+
+       ramzswap_set_disksize(rzs, totalram_pages << PAGE_SHIFT);
+
+       rzs->compress_workmem = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
+       if (!rzs->compress_workmem) {
+               pr_err("Error allocating compressor working memory!\n");
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       rzs->compress_buffer = (void *)__get_free_pages(__GFP_ZERO, 1);
+       if (!rzs->compress_buffer) {
+               pr_err("Error allocating compressor buffer space\n");
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       num_pages = rzs->disksize >> PAGE_SHIFT;
+       rzs->table = vmalloc(num_pages * sizeof(*rzs->table));
+       if (!rzs->table) {
+               pr_err("Error allocating ramzswap address table\n");
+               /* To prevent accessing table entries during cleanup */
+               rzs->disksize = 0;
+               ret = -ENOMEM;
+               goto fail;
+       }
+       memset(rzs->table, 0, num_pages * sizeof(*rzs->table));
+
+       set_capacity(rzs->disk, rzs->disksize >> SECTOR_SHIFT);
+
+       /* ramzswap devices sort of resembles non-rotational disks */
+       queue_flag_set_unlocked(QUEUE_FLAG_NONROT, rzs->disk->queue);
+
+       rzs->mem_pool = xv_create_pool();
+       if (!rzs->mem_pool) {
+               pr_err("Error creating memory pool\n");
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       rzs->init_done = 1;
+
+       pr_debug("Initialization done!\n");
+       return 0;
+
+fail:
+       reset_device(rzs);
+
+       pr_err("Initialization failed: err=%d\n", ret);
+       return ret;
+}
+
+static int ramzswap_ioctl_reset_device(struct ramzswap *rzs)
+{
+       if (rzs->init_done)
+               reset_device(rzs);
+
+       return 0;
+}
+
+static int ramzswap_ioctl(struct block_device *bdev, fmode_t mode,
+                       unsigned int cmd, unsigned long arg)
+{
+       int ret = 0;
+       size_t disksize_kb;
+
+       struct ramzswap *rzs = bdev->bd_disk->private_data;
+
+       switch (cmd) {
+       case RZSIO_SET_DISKSIZE_KB:
+               if (rzs->init_done) {
+                       ret = -EBUSY;
+                       goto out;
+               }
+               if (copy_from_user(&disksize_kb, (void *)arg,
+                                               _IOC_SIZE(cmd))) {
+                       ret = -EFAULT;
+                       goto out;
+               }
+               rzs->disksize = disksize_kb << 10;
+               pr_info("Disk size set to %zu kB\n", disksize_kb);
+               break;
+
+       case RZSIO_GET_STATS:
+       {
+               struct ramzswap_ioctl_stats *stats;
+               if (!rzs->init_done) {
+                       ret = -ENOTTY;
+                       goto out;
+               }
+               stats = kzalloc(sizeof(*stats), GFP_KERNEL);
+               if (!stats) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               ramzswap_ioctl_get_stats(rzs, stats);
+               if (copy_to_user((void *)arg, stats, sizeof(*stats))) {
+                       kfree(stats);
+                       ret = -EFAULT;
+                       goto out;
+               }
+               kfree(stats);
+               break;
+       }
+       case RZSIO_INIT:
+               ret = ramzswap_ioctl_init_device(rzs);
+               break;
+
+       case RZSIO_RESET:
+               /* Do not reset an active device! */
+               if (bdev->bd_holders) {
+                       ret = -EBUSY;
+                       goto out;
+               }
+
+               /* Make sure all pending I/O is finished */
+               if (bdev)
+                       fsync_bdev(bdev);
+
+               ret = ramzswap_ioctl_reset_device(rzs);
+               break;
+
+       default:
+               pr_info("Invalid ioctl %u\n", cmd);
+               ret = -ENOTTY;
+       }
+
+out:
+       return ret;
+}
+
+void ramzswap_slot_free_notify(struct block_device *bdev, unsigned long index)
+{
+       struct ramzswap *rzs;
+
+       rzs = bdev->bd_disk->private_data;
+       ramzswap_free_page(rzs, index);
+       rzs_stat64_inc(rzs, &rzs->stats.notify_free);
+}
+
+static const struct block_device_operations ramzswap_devops = {
+       .ioctl = ramzswap_ioctl,
+       .swap_slot_free_notify = ramzswap_slot_free_notify,
+       .owner = THIS_MODULE
+};
+
+static int create_device(struct ramzswap *rzs, int device_id)
+{
+       int ret = 0;
+
+       mutex_init(&rzs->lock);
+       spin_lock_init(&rzs->stat64_lock);
+
+       rzs->queue = blk_alloc_queue(GFP_KERNEL);
+       if (!rzs->queue) {
+               pr_err("Error allocating disk queue for device %d\n",
+                       device_id);
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       blk_queue_make_request(rzs->queue, ramzswap_make_request);
+       rzs->queue->queuedata = rzs;
+
+        /* gendisk structure */
+       rzs->disk = alloc_disk(1);
+       if (!rzs->disk) {
+               blk_cleanup_queue(rzs->queue);
+               pr_warning("Error allocating disk structure for device %d\n",
+                       device_id);
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       rzs->disk->major = ramzswap_major;
+       rzs->disk->first_minor = device_id;
+       rzs->disk->fops = &ramzswap_devops;
+       rzs->disk->queue = rzs->queue;
+       rzs->disk->private_data = rzs;
+       snprintf(rzs->disk->disk_name, 16, "ramzswap%d", device_id);
+
+       /* Actual capacity set using RZSIO_SET_DISKSIZE_KB ioctl */
+       set_capacity(rzs->disk, 0);
+
+       /*
+        * To ensure that we always get PAGE_SIZE aligned
+        * and n*PAGE_SIZED sized I/O requests.
+        */
+       blk_queue_physical_block_size(rzs->disk->queue, PAGE_SIZE);
+       blk_queue_logical_block_size(rzs->disk->queue, PAGE_SIZE);
+       blk_queue_io_min(rzs->disk->queue, PAGE_SIZE);
+       blk_queue_io_opt(rzs->disk->queue, PAGE_SIZE);
+
+       add_disk(rzs->disk);
+
+       rzs->init_done = 0;
+
+out:
+       return ret;
+}
+
+static void destroy_device(struct ramzswap *rzs)
+{
+       if (rzs->disk) {
+               del_gendisk(rzs->disk);
+               put_disk(rzs->disk);
+       }
+
+       if (rzs->queue)
+               blk_cleanup_queue(rzs->queue);
+}
+
+static int __init ramzswap_init(void)
+{
+       int ret, dev_id;
+
+       if (num_devices > max_num_devices) {
+               pr_warning("Invalid value for num_devices: %u\n",
+                               num_devices);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ramzswap_major = register_blkdev(0, "ramzswap");
+       if (ramzswap_major <= 0) {
+               pr_warning("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);
+       devices = kzalloc(num_devices * sizeof(struct ramzswap), GFP_KERNEL);
+       if (!devices) {
+               ret = -ENOMEM;
+               goto unregister;
+       }
+
+       for (dev_id = 0; dev_id < num_devices; dev_id++) {
+               ret = create_device(&devices[dev_id], dev_id);
+               if (ret)
+                       goto free_devices;
+       }
+
+       return 0;
+
+free_devices:
+       while (dev_id)
+               destroy_device(&devices[--dev_id]);
+unregister:
+       unregister_blkdev(ramzswap_major, "ramzswap");
+out:
+       return ret;
+}
+
+static void __exit ramzswap_exit(void)
+{
+       int i;
+       struct ramzswap *rzs;
+
+       for (i = 0; i < num_devices; i++) {
+               rzs = &devices[i];
+
+               destroy_device(rzs);
+               if (rzs->init_done)
+                       reset_device(rzs);
+       }
+
+       unregister_blkdev(ramzswap_major, "ramzswap");
+
+       kfree(devices);
+       pr_debug("Cleanup done!\n");
+}
+
+module_param(num_devices, uint, 0);
+MODULE_PARM_DESC(num_devices, "Number of ramzswap devices");
+
+module_init(ramzswap_init);
+module_exit(ramzswap_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Nitin Gupta <ngupta@vflare.org>");
+MODULE_DESCRIPTION("Compressed RAM Based Swap Device");
diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h
new file mode 100644 (file)
index 0000000..4d2e48a
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Compressed RAM based swap 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 _RAMZSWAP_DRV_H_
+#define _RAMZSWAP_DRV_H_
+
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+
+#include "zram_ioctl.h"
+#include "xvmalloc.h"
+
+/*
+ * Some arbitrary value. This is just to catch
+ * invalid value for num_devices module parameter.
+ */
+static const unsigned max_num_devices = 32;
+
+/*
+ * Stored at beginning of each compressed object.
+ *
+ * It stores back-reference to table entry which points to this
+ * object. This is required to support memory defragmentation.
+ */
+struct zobj_header {
+#if 0
+       u32 table_idx;
+#endif
+};
+
+/*-- Configurable parameters */
+
+/* Default ramzswap disk size: 25% of total RAM */
+static const unsigned default_disksize_perc_ram = 25;
+
+/*
+ * Pages that compress to size greater than this are stored
+ * uncompressed in memory.
+ */
+static const unsigned max_zpage_size = PAGE_SIZE / 4 * 3;
+
+/*
+ * NOTE: max_zpage_size must be less than or equal to:
+ *   XV_MAX_ALLOC_SIZE - sizeof(struct zobj_header)
+ * otherwise, xv_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)
+
+/* Flags for ramzswap pages (table[page_no].flags) */
+enum rzs_pageflags {
+       /* Page is stored uncompressed */
+       RZS_UNCOMPRESSED,
+
+       /* Page consists entirely of zeros */
+       RZS_ZERO,
+
+       __NR_RZS_PAGEFLAGS,
+};
+
+/*-- Data structures */
+
+/*
+ * Allocated for each swap slot, indexed by page no.
+ * These table entries must fit exactly in a page.
+ */
+struct table {
+       struct page *page;
+       u16 offset;
+       u8 count;       /* object ref count (not yet used) */
+       u8 flags;
+} __attribute__((aligned(4)));
+
+struct ramzswap_stats {
+       /* basic stats */
+       size_t compr_size;      /* compressed size of pages stored -
+                                * needed to enforce memlimit */
+       /* more stats */
+#if defined(CONFIG_RAMZSWAP_STATS)
+       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-swap 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 pages_expand;       /* % of incompressible pages */
+#endif
+};
+
+struct ramzswap {
+       struct xv_pool *mem_pool;
+       void *compress_workmem;
+       void *compress_buffer;
+       struct table *table;
+       spinlock_t stat64_lock; /* protect 64-bit stats */
+       struct mutex lock;      /* protect compression buffers against
+                                * concurrent writes */
+       struct request_queue *queue;
+       struct gendisk *disk;
+       int init_done;
+       /*
+        * This is limit on amount of *uncompressed* worth of data
+        * we can hold. When backing swap device is provided, it is
+        * set equal to device size.
+        */
+       size_t disksize;        /* bytes */
+
+       struct ramzswap_stats stats;
+};
+
+/*-- */
+
+/* Debugging and Stats */
+#if defined(CONFIG_RAMZSWAP_STATS)
+static void rzs_stat_inc(u32 *v)
+{
+       *v = *v + 1;
+}
+
+static void rzs_stat_dec(u32 *v)
+{
+       *v = *v - 1;
+}
+
+static void rzs_stat64_inc(struct ramzswap *rzs, u64 *v)
+{
+       spin_lock(&rzs->stat64_lock);
+       *v = *v + 1;
+       spin_unlock(&rzs->stat64_lock);
+}
+
+static u64 rzs_stat64_read(struct ramzswap *rzs, u64 *v)
+{
+       u64 val;
+
+       spin_lock(&rzs->stat64_lock);
+       val = *v;
+       spin_unlock(&rzs->stat64_lock);
+
+       return val;
+}
+#else
+#define rzs_stat_inc(v)
+#define rzs_stat_dec(v)
+#define rzs_stat64_inc(r, v)
+#define rzs_stat64_read(r, v)
+#endif /* CONFIG_RAMZSWAP_STATS */
+
+#endif
diff --git a/drivers/staging/zram/zram_ioctl.h b/drivers/staging/zram/zram_ioctl.h
new file mode 100644 (file)
index 0000000..db94bcb
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Compressed RAM based swap 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 _RAMZSWAP_IOCTL_H_
+#define _RAMZSWAP_IOCTL_H_
+
+struct ramzswap_ioctl_stats {
+       u64 disksize;           /* user specified or equal to backing swap
+                                * size (if present) */
+       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-swap I/O requests */
+       u64 notify_free;        /* no. of swap slot free notifications */
+       u32 pages_zero;         /* no. of zero filled pages */
+       u32 good_compress_pct;  /* no. of pages with compression ratio<=50% */
+       u32 pages_expand_pct;   /* no. of incompressible pages */
+       u32 pages_stored;
+       u32 pages_used;
+       u64 orig_data_size;
+       u64 compr_data_size;
+       u64 mem_used_total;
+} __attribute__ ((packed, aligned(4)));
+
+#define RZSIO_SET_DISKSIZE_KB  _IOW('z', 0, size_t)
+#define RZSIO_GET_STATS                _IOR('z', 1, struct ramzswap_ioctl_stats)
+#define RZSIO_INIT             _IO('z', 2)
+#define RZSIO_RESET            _IO('z', 3)
+
+#endif