diff --git a/compat.h b/compat.h index 87a80ce..630b65a 100644 --- a/compat.h +++ b/compat.h @@ -2,7 +2,7 @@ #define _RAMZSWAP_COMPAT_H_ /* Uncomment this if you are using swap free notify patch */ -//#define CONFIG_SWAP_FREE_NOTIFY +#define CONFIG_SWAP_FREE_NOTIFY #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)) #define blk_queue_physical_block_size(q, size) \ diff --git a/patches/patch_swap_notify_core_support_2.6.33.diff b/patches/patch_swap_notify_core_support_2.6.33.diff index 17cb7fc..39edc9c 100644 --- a/patches/patch_swap_notify_core_support_2.6.33.diff +++ b/patches/patch_swap_notify_core_support_2.6.33.diff @@ -1,17 +1,13 @@ - include/linux/blkdev.h | 2 ++ - mm/swapfile.c | 3 +++ - 2 files changed, 5 insertions(+), 0 deletions(-) - diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h -index 5c80189..af027de 100644 +index 5c80189..2232051 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1301,6 +1301,8 @@ struct block_device_operations { unsigned long long); int (*revalidate_disk) (struct gendisk *); int (*getgeo)(struct block_device *, struct hd_geometry *); -+ /* this callback is with swap_lock and sometimes page table lock held */ -+ void (*swap_slot_free_notify) (struct block_device *, unsigned long); ++ /* this callback is with swap_lock and sometimes page table lock held */ ++ void (*swap_slot_free_notify) (struct block_device *, unsigned long); struct module *owner; }; diff --git a/ramzswap_drv.c b/ramzswap_drv.c index 21e58c4..71df554 100644 --- a/ramzswap_drv.c +++ b/ramzswap_drv.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -37,11 +36,20 @@ /* Module params (documentation at end) */ static unsigned int num_devices; static unsigned long disksize_kb; +static unsigned long memlimit_kb; +static char backing_swap[MAX_SWAP_NAME_LEN]; /* Globals */ static int ramzswap_major; static struct ramzswap *devices; +/* + * Pages that compress to larger than this size are + * forwarded to backing swap, if present or stored + * uncompressed in memory otherwise. + */ +static unsigned int max_zpage_size; + static int rzs_test_flag(struct ramzswap *rzs, u32 index, enum rzs_pageflags flag) { @@ -75,7 +83,53 @@ static int page_zero_filled(void *ptr) return 1; } -static void ramzswap_set_disksize(struct ramzswap *rzs, u64 totalram_bytes) +/* + * memlimit cannot be greater than backing disk size. + */ +static void ramzswap_set_memlimit(struct ramzswap *rzs, size_t totalram_bytes) +{ + int memlimit_valid = 1; + + if (!rzs->memlimit) { + pr_info("Memory limit not set.\n"); + memlimit_valid = 0; + } + + if (rzs->memlimit > rzs->disksize) { + pr_info("Memory limit cannot be greater than " + "disksize: limit=%zu, disksize=%zu\n", + rzs->memlimit, rzs->disksize); + memlimit_valid = 0; + } + + if (!memlimit_valid) { + size_t mempart, disksize; + pr_info("Using default: smaller of (%u%% of RAM) and " + "(backing disk size).\n", + default_memlimit_perc_ram); + mempart = default_memlimit_perc_ram * (totalram_bytes / 100); + disksize = rzs->disksize; + rzs->memlimit = mempart > disksize ? disksize : mempart; + } + + if (rzs->memlimit > totalram_bytes / 2) { + pr_info( + "Its not advisable setting limit more than half of " + "size of memory since we expect a 2:1 compression ratio. " + "Limit represents amount of *compressed* data we can keep " + "in memory!\n" + "\tMemory Size: %zu kB\n" + "\tLimit you selected: %zu kB\n" + "Continuing anyway ...\n", + totalram_bytes >> 10, rzs->memlimit >> 10 + ); + } + + rzs->memlimit &= PAGE_MASK; + BUG_ON(!rzs->memlimit); +} + +static void ramzswap_set_disksize(struct ramzswap *rzs, size_t totalram_bytes) { if (!rzs->disksize) { pr_info( @@ -94,8 +148,8 @@ static void ramzswap_set_disksize(struct ramzswap *rzs, u64 totalram_bytes) "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: %llu kB\n" - "\tSize you selected: %llu kB\n" + "\tMemory Size: %zu kB\n" + "\tSize you selected: %zu kB\n" "Continuing anyway ...\n", totalram_bytes >> 10, rzs->disksize ); @@ -106,17 +160,69 @@ static void ramzswap_set_disksize(struct ramzswap *rzs, u64 totalram_bytes) /* * Swap header (1st page of swap device) contains information - * about a swap file/partition. Prepare such a header for the - * given ramzswap device so that swapon can identify it as a - * swap partition. -*/ - -static void setup_swap_header(struct ramzswap *rzs, union swap_header *s) + * to indentify it as a swap partition. Prepare such a header + * for ramzswap device (ramzswap0) so that swapon can identify + * it as swap partition. In case backing swap device is provided, + * copy its swap header. + */ +static int setup_swap_header(struct ramzswap *rzs, union swap_header *s) { - s->info.version = 1; + int ret = 0; + struct page *page; + struct address_space *mapping; + union swap_header *backing_swap_header; + + /* + * There is no backing swap device. Create a swap header + * that is acceptable by swapon. + */ + if (!rzs->backing_swap) { + s->info.version = 1; + s->info.last_page = (rzs->disksize >> PAGE_SHIFT) - 1; + s->info.nr_badpages = 0; + memcpy(s->magic.magic, "SWAPSPACE2", 10); + return 0; + } + + /* + * We have a backing swap device. Copy its swap header + * to ramzswap device header. If this header contains + * invalid information (backing device not a swap + * partition, etc.), swapon will fail for ramzswap + * which is correct behavior - we don't want to swap + * over filesystem partition! + */ + + /* Read the backing swap header (code from sys_swapon) */ + mapping = rzs->swap_file->f_mapping; + if (!mapping->a_ops->readpage) { + ret = -EINVAL; + goto out; + } + + page = read_mapping_page(mapping, 0, rzs->swap_file); + if (IS_ERR(page)) { + ret = PTR_ERR(page); + goto out; + } + + backing_swap_header = kmap(page); + memcpy(s, backing_swap_header, sizeof(*s)); + if (s->info.nr_badpages) { + pr_info("Cannot use backing swap with bad pages (%u)\n", + s->info.nr_badpages); + ret = -EINVAL; + } + /* + * ramzswap disksize equals number of usable pages in backing + * swap. Set last_page in swap header to match this disksize + * ('last_page' means 0-based index of last usable swap page). + */ s->info.last_page = (rzs->disksize >> PAGE_SHIFT) - 1; - s->info.nr_badpages = 0; - memcpy(s->magic.magic, "SWAPSPACE2", 10); + kunmap(page); + +out: + return ret; } static void ramzswap_flush_dcache_page(struct page *page) @@ -144,7 +250,12 @@ static void ramzswap_flush_dcache_page(struct page *page) static void ramzswap_ioctl_get_stats(struct ramzswap *rzs, struct ramzswap_ioctl_stats *s) { + strncpy(s->backing_swap_name, rzs->backing_swap_name, + MAX_SWAP_NAME_LEN - 1); + s->backing_swap_name[MAX_SWAP_NAME_LEN - 1] = '\0'; + s->disksize = rzs->disksize; + s->memlimit = rzs->memlimit; #if defined(CONFIG_RAMZSWAP_STATS) { @@ -154,8 +265,8 @@ static void ramzswap_ioctl_get_stats(struct ramzswap *rzs, 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); + succ_writes = stat64_read(rzs, &rs->num_writes) - + stat64_read(rzs, &rs->failed_writes); if (succ_writes && rs->pages_stored) { good_compress_perc = rs->good_compress * 100 @@ -164,12 +275,12 @@ static void ramzswap_ioctl_get_stats(struct ramzswap *rzs, / 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->num_reads = stat64_read(rzs, &rs->num_reads); + s->num_writes = stat64_read(rzs, &rs->num_writes); + s->failed_reads = stat64_read(rzs, &rs->failed_reads); + s->failed_writes = stat64_read(rzs, &rs->failed_writes); + s->invalid_io = stat64_read(rzs, &rs->invalid_io); + s->notify_free = stat64_read(rzs, &rs->notify_free); s->pages_zero = rs->pages_zero; s->good_compress_pct = good_compress_perc; @@ -180,10 +291,330 @@ static void ramzswap_ioctl_get_stats(struct ramzswap *rzs, s->orig_data_size = rs->pages_stored << PAGE_SHIFT; s->compr_data_size = rs->compr_size; s->mem_used_total = mem_used; + + s->bdev_num_reads = stat64_read(rzs, &rs->bdev_num_reads); + s->bdev_num_writes = stat64_read(rzs, &rs->bdev_num_writes); } #endif /* CONFIG_RAMZSWAP_STATS */ } +static int add_backing_swap_extent(struct ramzswap *rzs, + pgoff_t phy_pagenum, + pgoff_t num_pages) +{ + unsigned int idx; + struct list_head *head; + struct page *curr_page, *new_page; + unsigned int extents_per_page = PAGE_SIZE / + sizeof(struct ramzswap_backing_extent); + + idx = rzs->num_extents % extents_per_page; + if (!idx) { + new_page = alloc_page(__GFP_ZERO); + if (!new_page) + return -ENOMEM; + + if (rzs->num_extents) { + curr_page = virt_to_page(rzs->curr_extent); + head = &curr_page->lru; + } else { + head = &rzs->backing_swap_extent_list; + } + + list_add(&new_page->lru, head); + rzs->curr_extent = page_address(new_page); + } + + rzs->curr_extent->phy_pagenum = phy_pagenum; + rzs->curr_extent->num_pages = num_pages; + + pr_debug("add_extent: idx=%u, phy_pgnum=%lu, num_pgs=%lu, " + "pg_last=%lu, curr_ext=%p\n", idx, phy_pagenum, num_pages, + phy_pagenum + num_pages - 1, rzs->curr_extent); + + if (idx != extents_per_page - 1) + rzs->curr_extent++; + + return 0; +} + +static int setup_backing_swap_extents(struct ramzswap *rzs, + struct inode *inode, unsigned long *num_pages) +{ + int ret = 0; + unsigned blkbits; + unsigned blocks_per_page; + pgoff_t contig_pages = 0, total_pages = 0; + pgoff_t pagenum = 0, prev_pagenum = 0; + sector_t probe_block = 0; + sector_t last_block; + + blkbits = inode->i_blkbits; + blocks_per_page = PAGE_SIZE >> blkbits; + + last_block = i_size_read(inode) >> blkbits; + while (probe_block + blocks_per_page <= last_block) { + unsigned block_in_page; + sector_t first_block; + + first_block = bmap(inode, probe_block); + if (first_block == 0) + goto bad_bmap; + + /* It must be PAGE_SIZE aligned on-disk */ + if (first_block & (blocks_per_page - 1)) { + probe_block++; + goto probe_next; + } + + /* All blocks within this page must be contiguous on disk */ + for (block_in_page = 1; block_in_page < blocks_per_page; + block_in_page++) { + sector_t block; + + block = bmap(inode, probe_block + block_in_page); + if (block == 0) + goto bad_bmap; + if (block != first_block + block_in_page) { + /* Discontiguity */ + probe_block++; + goto probe_next; + } + } + + /* + * We found a PAGE_SIZE length, PAGE_SIZE aligned + * run of blocks. + */ + pagenum = first_block >> (PAGE_SHIFT - blkbits); + + if (total_pages && (pagenum != prev_pagenum + 1)) { + ret = add_backing_swap_extent(rzs, prev_pagenum - + (contig_pages - 1), contig_pages); + if (ret < 0) + goto out; + rzs->num_extents++; + contig_pages = 0; + } + total_pages++; + contig_pages++; + prev_pagenum = pagenum; + probe_block += blocks_per_page; + +probe_next: + continue; + } + + if (contig_pages) { + pr_debug("adding last extent: pagenum=%lu, " + "contig_pages=%lu\n", pagenum, contig_pages); + ret = add_backing_swap_extent(rzs, + prev_pagenum - (contig_pages - 1), contig_pages); + if (ret < 0) + goto out; + rzs->num_extents++; + } + if (!rzs->num_extents) { + pr_err("No swap extents found!\n"); + ret = -EINVAL; + } + + if (!ret) { + *num_pages = total_pages; + pr_info("Found %lu extents containing %luk\n", + rzs->num_extents, *num_pages << (PAGE_SHIFT - 10)); + } + goto out; + +bad_bmap: + pr_err("Backing swapfile has holes\n"); + ret = -EINVAL; + +out: + while (ret && !list_empty(&rzs->backing_swap_extent_list)) { + struct page *page; + struct list_head *entry = rzs->backing_swap_extent_list.next; + page = list_entry(entry, struct page, lru); + list_del(entry); + __free_page(page); + } + return ret; +} + +static void map_backing_swap_extents(struct ramzswap *rzs) +{ + struct ramzswap_backing_extent *se; + struct page *table_page, *se_page; + unsigned long num_pages, num_table_pages, entry; + unsigned long se_idx, span; + unsigned entries_per_page = PAGE_SIZE / sizeof(*rzs->table); + unsigned extents_per_page = PAGE_SIZE / sizeof(*se); + + /* True for block device */ + if (!rzs->num_extents) + return; + + se_page = list_entry(rzs->backing_swap_extent_list.next, + struct page, lru); + se = page_address(se_page); + span = se->num_pages; + num_pages = rzs->disksize >> PAGE_SHIFT; + num_table_pages = DIV_ROUND_UP(num_pages * sizeof(*rzs->table), + PAGE_SIZE); + + entry = 0; + se_idx = 0; + while (num_table_pages--) { + table_page = vmalloc_to_page(&rzs->table[entry]); + while (span <= entry) { + se_idx++; + if (se_idx == rzs->num_extents) + BUG(); + + if (!(se_idx % extents_per_page)) { + se_page = list_entry(se_page->lru.next, + struct page, lru); + se = page_address(se_page); + } else + se++; + + span += se->num_pages; + } + table_page->mapping = (struct address_space *)se; + table_page->private = se->num_pages - (span - entry); + pr_debug("map_table: entry=%lu, span=%lu, map=%p, priv=%lu\n", + entry, span, table_page->mapping, table_page->private); + entry += entries_per_page; + } +} + +/* + * Check if value of backing_swap module param is sane. + * Claim this device and set ramzswap size equal to + * size of this block device. + */ +static int setup_backing_swap(struct ramzswap *rzs) +{ + int ret = 0; + size_t disksize; + unsigned long num_pages = 0; + struct inode *inode; + struct file *swap_file; + struct address_space *mapping; + struct block_device *bdev = NULL; + + if (!rzs->backing_swap_name[0]) { + pr_debug("backing_swap param not given\n"); + goto out; + } + + pr_debug("Using backing swap device: %s\n", rzs->backing_swap_name); + + swap_file = filp_open(rzs->backing_swap_name, + O_RDWR | O_LARGEFILE, 0); + if (IS_ERR(swap_file)) { + pr_err("Error opening backing device: %s\n", + rzs->backing_swap_name); + ret = -EINVAL; + goto out; + } + + mapping = swap_file->f_mapping; + inode = mapping->host; + + if (S_ISBLK(inode->i_mode)) { + bdev = I_BDEV(inode); + ret = bd_claim(bdev, setup_backing_swap); + if (ret < 0) { + bdev = NULL; + goto bad_param; + } + disksize = i_size_read(inode); + if (!disksize) { + pr_err("Error reading backing swap size.\n"); + goto bad_param; + } + } else if (S_ISREG(inode->i_mode)) { + bdev = inode->i_sb->s_bdev; + if (IS_SWAPFILE(inode)) { + ret = -EBUSY; + goto bad_param; + } + ret = setup_backing_swap_extents(rzs, inode, &num_pages); + if (ret < 0) + goto bad_param; + disksize = num_pages << PAGE_SHIFT; + } else { + goto bad_param; + } + + rzs->swap_file = swap_file; + rzs->backing_swap = bdev; + rzs->disksize = disksize; + + return 0; + +bad_param: + if (bdev) + bd_release(bdev); + filp_close(swap_file, NULL); + +out: + rzs->backing_swap = NULL; + return ret; +} + +/* + * Map logical page number 'pagenum' to physical page number + * on backing swap device. For block device, this is a nop. + */ +static u32 map_backing_swap_page(struct ramzswap *rzs, u32 pagenum) +{ + u32 skip_pages, entries_per_page; + size_t delta, se_offset, skipped; + struct page *table_page, *se_page; + struct ramzswap_backing_extent *se; + + if (!rzs->num_extents) + return pagenum; + + entries_per_page = PAGE_SIZE / sizeof(*rzs->table); + + table_page = vmalloc_to_page(&rzs->table[pagenum]); + se = (struct ramzswap_backing_extent *)table_page->mapping; + se_page = virt_to_page(se); + + skip_pages = pagenum - (pagenum / entries_per_page * entries_per_page); + se_offset = table_page->private + skip_pages; + + if (se_offset < se->num_pages) + return se->phy_pagenum + se_offset; + + skipped = se->num_pages - table_page->private; + do { + struct ramzswap_backing_extent *se_base; + u32 se_entries_per_page = PAGE_SIZE / sizeof(*se); + + /* Get next swap extent */ + se_base = (struct ramzswap_backing_extent *) + page_address(se_page); + if (se - se_base == se_entries_per_page - 1) { + se_page = list_entry(se_page->lru.next, + struct page, lru); + se = page_address(se_page); + } else { + se++; + } + + skipped += se->num_pages; + } while (skipped < skip_pages); + + delta = skipped - skip_pages; + se_offset = se->num_pages - delta; + + return se->phy_pagenum + se_offset; +} + static void ramzswap_free_page(struct ramzswap *rzs, size_t index) { u32 clen; @@ -199,7 +630,7 @@ static void ramzswap_free_page(struct ramzswap *rzs, size_t index) */ if (rzs_test_flag(rzs, index, RZS_ZERO)) { rzs_clear_flag(rzs, index, RZS_ZERO); - rzs_stat_dec(&rzs->stats.pages_zero); + stat_dec(&rzs->stats.pages_zero); } return; } @@ -208,7 +639,7 @@ static void ramzswap_free_page(struct ramzswap *rzs, size_t index) clen = PAGE_SIZE; __free_page(page); rzs_clear_flag(rzs, index, RZS_UNCOMPRESSED); - rzs_stat_dec(&rzs->stats.pages_expand); + stat_dec(&rzs->stats.pages_expand); goto out; } @@ -218,11 +649,11 @@ static void ramzswap_free_page(struct ramzswap *rzs, size_t index) xv_free(rzs->mem_pool, page, offset); if (clen <= PAGE_SIZE / 2) - rzs_stat_dec(&rzs->stats.good_compress); + stat_dec(&rzs->stats.good_compress); out: rzs->stats.compr_size -= clen; - rzs_stat_dec(&rzs->stats.pages_stored); + stat_dec(&rzs->stats.pages_stored); rzs->table[index].page = NULL; rzs->table[index].offset = 0; @@ -271,12 +702,38 @@ static int handle_uncompressed_page(struct ramzswap *rzs, struct bio *bio) /* * Called when request page is not present in ramzswap. - * This is an attempt to read before any previous write + * Its either in backing swap device (if present) or + * this is an attempt to read before any previous write * to this location - this happens due to readahead when * swap device is read from user-space (e.g. during swapon) */ static int handle_ramzswap_fault(struct ramzswap *rzs, struct bio *bio) { + /* + * Always forward such requests to backing swap + * device (if present) + */ + if (rzs->backing_swap) { + u32 pagenum; + stat64_dec(rzs, &rzs->stats.num_reads); + stat64_inc(rzs, &rzs->stats.bdev_num_reads); + bio->bi_bdev = rzs->backing_swap; + + /* + * In case backing swap is a file, find the right offset within + * the file corresponding to logical position 'index'. For block + * device, this is a nop. + */ + pagenum = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT; + bio->bi_sector = map_backing_swap_page(rzs, pagenum) + << SECTORS_PER_PAGE_SHIFT; + return 1; + } + + /* + * Its unlikely event in case backing dev is + * not present + */ pr_debug("Read before write on swap device: " "sector=%lu, size=%u, offset=%u\n", (ulong)(bio->bi_sector), bio->bi_size, @@ -297,7 +754,7 @@ static int ramzswap_read(struct ramzswap *rzs, struct bio *bio) struct zobj_header *zheader; unsigned char *user_mem, *cmem; - rzs_stat64_inc(rzs, &rzs->stats.num_reads); + stat64_inc(rzs, &rzs->stats.num_reads); page = bio->bi_io_vec[0].bv_page; index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT; @@ -331,7 +788,7 @@ static int ramzswap_read(struct ramzswap *rzs, struct bio *bio) 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); + stat64_inc(rzs, &rzs->stats.failed_reads); goto out; } @@ -348,14 +805,14 @@ static int ramzswap_read(struct ramzswap *rzs, struct bio *bio) static int ramzswap_write(struct ramzswap *rzs, struct bio *bio) { - int ret; + int ret, fwd_write_request = 0; u32 offset, index; size_t clen; struct zobj_header *zheader; struct page *page, *page_store; unsigned char *user_mem, *cmem, *src; - rzs_stat64_inc(rzs, &rzs->stats.num_writes); + stat64_inc(rzs, &rzs->stats.num_writes); page = bio->bi_io_vec[0].bv_page; index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT; @@ -379,13 +836,21 @@ static int ramzswap_write(struct ramzswap *rzs, struct bio *bio) kunmap_atomic(user_mem, KM_USER0); rzs_set_flag(rzs, index, RZS_ZERO); mutex_unlock(&rzs->lock); - rzs_stat_inc(&rzs->stats.pages_zero); + stat_inc(&rzs->stats.pages_zero); set_bit(BIO_UPTODATE, &bio->bi_flags); bio_endio(bio, 0); return 0; } + if (rzs->backing_swap && + (rzs->stats.compr_size > rzs->memlimit - PAGE_SIZE)) { + kunmap_atomic(user_mem, KM_USER0); + mutex_unlock(&rzs->lock); + fwd_write_request = 1; + goto out; + } + ret = lzo1x_1_compress(user_mem, PAGE_SIZE, src, &clen, rzs->compress_workmem); @@ -394,29 +859,36 @@ static int ramzswap_write(struct ramzswap *rzs, struct bio *bio) 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); + stat64_inc(rzs, &rzs->stats.failed_writes); goto out; } /* - * Page is incompressible. Store it as-is (uncompressed) + * Page is incompressible. Forward it to backing swap + * if present. Otherwise, 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)) { + if (rzs->backing_swap) { + mutex_unlock(&rzs->lock); + fwd_write_request = 1; + goto out; + } + 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); + 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); + stat_inc(&rzs->stats.pages_expand); rzs->table[index].page = page_store; src = kmap_atomic(page, KM_USER0); goto memstore; @@ -428,7 +900,9 @@ static int ramzswap_write(struct ramzswap *rzs, struct bio *bio) 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); + stat64_inc(rzs, &rzs->stats.failed_writes); + if (rzs->backing_swap) + fwd_write_request = 1; goto out; } @@ -455,9 +929,9 @@ static int ramzswap_write(struct ramzswap *rzs, struct bio *bio) /* Update stats */ rzs->stats.compr_size += clen; - rzs_stat_inc(&rzs->stats.pages_stored); + stat_inc(&rzs->stats.pages_stored); if (clen <= PAGE_SIZE / 2) - rzs_stat_inc(&rzs->stats.good_compress); + stat_inc(&rzs->stats.good_compress); mutex_unlock(&rzs->lock); @@ -466,6 +940,31 @@ static int ramzswap_write(struct ramzswap *rzs, struct bio *bio) return 0; out: + if (fwd_write_request) { + stat64_inc(rzs, &rzs->stats.bdev_num_writes); + bio->bi_bdev = rzs->backing_swap; +#if 0 + /* + * TODO: We currently have linear mapping of ramzswap and + * backing swap sectors. This is not desired since we want + * to optimize writes to backing swap to minimize disk seeks + * or have effective wear leveling (for SSDs). Also, a + * non-linear mapping is required to implement compressed + * on-disk swapping. + */ + bio->bi_sector = get_backing_swap_page() + << SECTORS_PER_PAGE_SHIFT; +#endif + /* + * In case backing swap is a file, find the right offset within + * the file corresponding to logical position 'index'. For block + * device, this is a nop. + */ + bio->bi_sector = map_backing_swap_page(rzs, index) + << SECTORS_PER_PAGE_SHIFT; + return 1; + } + bio_io_error(bio); return 0; } @@ -504,7 +1003,7 @@ static int ramzswap_make_request(struct request_queue *queue, struct bio *bio) } if (!valid_swap_request(rzs, bio)) { - rzs_stat64_inc(rzs, &rzs->stats.invalid_io); + stat64_inc(rzs, &rzs->stats.invalid_io); bio_io_error(bio); return 0; } @@ -524,13 +1023,21 @@ static int ramzswap_make_request(struct request_queue *queue, struct bio *bio) static void reset_device(struct ramzswap *rzs, struct block_device *bdev) { - size_t index; + int is_backing_blkdev = 0; + size_t index, num_pages; + unsigned entries_per_page; + unsigned long num_table_pages, entry = 0; if (bdev) fsync_bdev(bdev); rzs->init_done = 0; + if (rzs->backing_swap && !rzs->num_extents) + is_backing_blkdev = 1; + + num_pages = rzs->disksize >> PAGE_SHIFT; + /* Free various per-device buffers */ kfree(rzs->compress_workmem); free_pages((unsigned long)rzs->compress_buffer, 1); @@ -539,7 +1046,7 @@ static void reset_device(struct ramzswap *rzs, struct block_device *bdev) rzs->compress_buffer = NULL; /* Free all pages that are still in this ramzswap device */ - for (index = 0; index < rzs->disksize >> PAGE_SHIFT; index++) { + for (index = 0; index < num_pages; index++) { struct page *page; u16 offset; @@ -555,16 +1062,51 @@ static void reset_device(struct ramzswap *rzs, struct block_device *bdev) xv_free(rzs->mem_pool, page, offset); } + entries_per_page = PAGE_SIZE / sizeof(*rzs->table); + num_table_pages = DIV_ROUND_UP(num_pages * sizeof(*rzs->table), + PAGE_SIZE); + /* + * Set page->mapping to NULL for every table page. + * Otherwise, we will hit bad_page() during free. + */ + while (rzs->num_extents && num_table_pages--) { + struct page *page; + page = vmalloc_to_page(&rzs->table[entry]); + page->mapping = NULL; + entry += entries_per_page; + } vfree(rzs->table); rzs->table = NULL; xv_destroy_pool(rzs->mem_pool); rzs->mem_pool = NULL; + /* Free all swap extent pages */ + while (!list_empty(&rzs->backing_swap_extent_list)) { + struct page *page; + struct list_head *entry; + entry = rzs->backing_swap_extent_list.next; + page = list_entry(entry, struct page, lru); + list_del(entry); + __free_page(page); + } + INIT_LIST_HEAD(&rzs->backing_swap_extent_list); + rzs->num_extents = 0; + + /* Close backing swap device, if present */ + if (rzs->backing_swap) { + if (is_backing_blkdev) + bd_release(rzs->backing_swap); + filp_close(rzs->swap_file, NULL); + rzs->backing_swap = NULL; + memset(rzs->backing_swap_name, 0, MAX_SWAP_NAME_LEN); + } + /* Reset stats */ memset(&rzs->stats, 0, sizeof(rzs->stats)); rzs->disksize = 0; + rzs->memlimit = 0; } static int ramzswap_ioctl_init_device(struct ramzswap *rzs) @@ -581,7 +1123,14 @@ static int ramzswap_ioctl_init_device(struct ramzswap *rzs) dev_id = rzs - devices; - ramzswap_set_disksize(rzs, totalram_pages << PAGE_SHIFT); + ret = setup_backing_swap(rzs); + if (ret) + goto fail; + + if (rzs->backing_swap) + ramzswap_set_memlimit(rzs, totalram_pages << PAGE_SHIFT); + else + ramzswap_set_disksize(rzs, totalram_pages << PAGE_SHIFT); rzs->compress_workmem = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL); if (!rzs->compress_workmem) { @@ -608,6 +1157,8 @@ static int ramzswap_ioctl_init_device(struct ramzswap *rzs) } memset(rzs->table, 0, num_pages * sizeof(*rzs->table)); + map_backing_swap_extents(rzs); + page = alloc_page(__GFP_ZERO); if (!page) { pr_err("Error allocating swap header page\n"); @@ -618,13 +1169,23 @@ static int ramzswap_ioctl_init_device(struct ramzswap *rzs) rzs_set_flag(rzs, 0, RZS_UNCOMPRESSED); swap_header = kmap(page); - setup_swap_header(rzs, swap_header); + ret = setup_swap_header(rzs, swap_header); kunmap(page); + if (ret) { + pr_err("Error setting swap header\n"); + goto fail; + } 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); + /* + * We have ident mapping of sectors for ramzswap and + * and the backing swap device. So, this queue flag + * should be according to backing dev. + */ + if (!rzs->backing_swap || + blk_queue_nonrot(rzs->backing_swap->bd_disk->queue)) + queue_flag_set_unlocked(QUEUE_FLAG_NONROT, rzs->disk->queue); rzs->mem_pool = xv_create_pool(); if (!rzs->mem_pool) { @@ -633,10 +1194,27 @@ static int ramzswap_ioctl_init_device(struct ramzswap *rzs) goto fail; } + /* + * Pages that compress to size greater than this are forwarded + * to physical swap disk (if backing dev is provided) + * TODO: make this configurable + */ + if (rzs->backing_swap) + max_zpage_size = max_zpage_size_bdev; + else + max_zpage_size = max_zpage_size_nobdev; + pr_debug("Max compressed page size: %u bytes\n", max_zpage_size); + rzs->init_done = 1; - pr_info("/dev/ramzswap%d initialized: " - "disksize_kb=%llu", dev_id, rzs->disksize >> 10); + if (rzs->backing_swap) { + pr_info("/dev/ramzswap%d initialized: " + "backing_swap=%s, memlimit_kb=%zu\n", + dev_id, rzs->backing_swap_name, rzs->memlimit >> 10); + } else { + pr_info("/dev/ramzswap%d initialized: " + "disksize_kb=%zu", dev_id, rzs->disksize >> 10); + } return 0; fail: @@ -659,7 +1237,7 @@ static int ramzswap_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { int ret = 0; - size_t disksize_kb; + size_t disksize_kb, memlimit_kb; struct ramzswap *rzs = bdev->bd_disk->private_data; @@ -678,6 +1256,36 @@ static int ramzswap_ioctl(struct block_device *bdev, fmode_t mode, pr_debug("Disk size set to %zu kB\n", disksize_kb); break; + case RZSIO_SET_MEMLIMIT_KB: + if (rzs->init_done) { + /* TODO: allow changing memlimit */ + ret = -EBUSY; + goto out; + } + if (copy_from_user(&memlimit_kb, (void *)arg, + _IOC_SIZE(cmd))) { + ret = -EFAULT; + goto out; + } + rzs->memlimit = memlimit_kb << 10; + pr_debug("Memory limit set to %zu kB\n", memlimit_kb); + break; + + case RZSIO_SET_BACKING_SWAP: + if (rzs->init_done) { + ret = -EBUSY; + goto out; + } + + if (copy_from_user(&rzs->backing_swap_name, (void *)arg, + _IOC_SIZE(cmd))) { + ret = -EFAULT; + goto out; + } + rzs->backing_swap_name[MAX_SWAP_NAME_LEN - 1] = '\0'; + pr_debug("Backing swap set to %s\n", rzs->backing_swap_name); + break; + case RZSIO_GET_STATS: { struct ramzswap_ioctl_stats *stats; @@ -709,7 +1317,6 @@ static int ramzswap_ioctl(struct block_device *bdev, fmode_t mode, ret = -EBUSY; goto out; } - ret = ramzswap_ioctl_reset_device(rzs, bdev); break; @@ -729,7 +1336,7 @@ void ramzswap_slot_free_notify(struct block_device *bdev, unsigned long index) rzs = bdev->bd_disk->private_data; ramzswap_free_page(rzs, index); - rzs_stat64_inc(rzs, &rzs->stats.notify_free); + stat64_inc(rzs, &rzs->stats.notify_free); return; } @@ -749,6 +1356,7 @@ static int create_device(struct ramzswap *rzs, int device_id) mutex_init(&rzs->lock); spin_lock_init(&rzs->stat64_lock); + INIT_LIST_HEAD(&rzs->backing_swap_extent_list); rzs->queue = blk_alloc_queue(GFP_KERNEL); if (!rzs->queue) { @@ -777,8 +1385,10 @@ static int create_device(struct ramzswap *rzs, int device_id) 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 */ + /* + * Actual capacity set using RZSIO_SET_DISKSIZE_KB ioctl + * or set equal to backing swap device (if provided) + */ set_capacity(rzs->disk, 0); blk_queue_physical_block_size(rzs->disk->queue, PAGE_SIZE); @@ -849,8 +1459,7 @@ static int __init ramzswap_init(void) rzs = &devices[0]; /* - * Set disksize now if user provided value for - * module parameter + * User specifies either or */ if (disksize_kb) { rzs->disksize = disksize_kb << 10; @@ -860,6 +1469,25 @@ static int __init ramzswap_init(void) goto out; } + if (backing_swap[0]) { + rzs->memlimit = memlimit_kb << 10; + strncpy(rzs->backing_swap_name, backing_swap, + MAX_SWAP_NAME_LEN); + rzs->backing_swap_name[MAX_SWAP_NAME_LEN - 1] = '\0'; + ret = ramzswap_ioctl_init_device(rzs); + if (ret) + goto free_devices; + goto out; + } + + /* User specified memlimit_kb but not backing_swap */ + if (memlimit_kb) { + pr_info("memlimit_kb parameter is valid only when " + "backing_swap is also specified. Aborting.\n"); + ret = -EINVAL; + goto free_devices; + } + return 0; free_devices: @@ -899,20 +1527,30 @@ module_param(num_devices, uint, 0); MODULE_PARM_DESC(num_devices, "Number of ramzswap devices"); /* - * disksize_kb parameter is used to initialize just the first - * (/dev/ramzswap0) device. To initialize additional devices, - * use rzscontrol utility. If this parameter is not provided, - * then the first device is also left in unitialized state. + * User specifies either or + * parameters. You must specify these parameters if the first device + * has to be initialized on module load without using rzscontrol utility. + * This is useful for embedded system, where shipping an additional binary + * (rzscontrol) might not be desirable. * - * This might be useful for small embedded devices where shipping - * an additional binary (rzscontrol) just to initialize a device - * might not be desirable. + * These parameters are used to initialize just the first (/dev/ramzswap0) + * device. To initialize additional devices, use rzscontrol utility. If + * these parameters are not provided, then the first device is also + * left in unitialized state. */ /* Optional: default = 25% of RAM */ module_param(disksize_kb, ulong, 0); MODULE_PARM_DESC(disksize_kb, "Disksize in KB"); +/* Optional: default = 15% of RAM */ +module_param(memlimit_kb, ulong, 0); +MODULE_PARM_DESC(memlimit_kb, "Memlimit in KB"); + +/* Optional: default = */ +module_param_string(backing_swap, backing_swap, sizeof(backing_swap), 0); +MODULE_PARM_DESC(backing_swap, "Backing swap name"); + module_init(ramzswap_init); module_exit(ramzswap_exit); diff --git a/ramzswap_drv.h b/ramzswap_drv.h index 4df9240..3d9cc57 100644 --- a/ramzswap_drv.h +++ b/ramzswap_drv.h @@ -31,7 +31,8 @@ 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. + * object. This is required to support memory defragmentation or + * migrating compressed pages to backing swap disk. */ struct zobj_header { #if 0 @@ -43,17 +44,27 @@ struct zobj_header { /* Default ramzswap disk size: 25% of total RAM */ static const unsigned default_disksize_perc_ram = 25; +static const unsigned default_memlimit_perc_ram = 15; /* + * Max compressed page size when backing device is provided. + * Pages that compress to size greater than this are sent to + * physical swap disk. + */ +static const unsigned max_zpage_size_bdev = PAGE_SIZE / 2; + +/* + * Max compressed page size when there is no backing dev. * Pages that compress to size greater than this are stored * uncompressed in memory. */ -static const unsigned max_zpage_size = PAGE_SIZE / 4 * 3; +static const unsigned max_zpage_size_nobdev = PAGE_SIZE / 4 * 3; /* - * NOTE: max_zpage_size must be less than or equal to: + * NOTE: max_zpage_size_{bdev,nobdev} sizes must be + * less than or equal to: * XV_MAX_ALLOC_SIZE - sizeof(struct zobj_header) - * otherwise, xv_malloc() would always return failure. + * since otherwise xv_malloc would always return failure. */ /*-- End of configurable params */ @@ -87,9 +98,19 @@ struct table { u8 flags; } __attribute__((aligned(4))); +/* + * Swap extent information in case backing swap is a regular + * file. These extent entries must fit exactly in a page. + */ +struct ramzswap_backing_extent { + pgoff_t phy_pagenum; + pgoff_t num_pages; +} __attribute__((aligned(4))); + struct ramzswap_stats { /* basic stats */ - size_t compr_size; /* compressed size of pages stored */ + 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 */ @@ -102,6 +123,8 @@ struct ramzswap_stats { u32 pages_stored; /* no. of pages currently stored */ u32 good_compress; /* % of pages with compression ratio<=50% */ u32 pages_expand; /* % of incompressible pages */ + u64 bdev_num_reads; /* no. of reads on backing dev */ + u64 bdev_num_writes; /* no. of writes on backing dev */ #endif }; @@ -115,37 +138,58 @@ struct ramzswap { struct request_queue *queue; struct gendisk *disk; int init_done; + /* + * This is limit on compressed data size (stats.compr_size) + * Its applicable only when backing swap device is present. + */ + size_t memlimit; /* bytes */ /* * This is limit on amount of *uncompressed* worth of data - * we can hold. + * we can hold. When backing swap device is provided, it is + * set equal to device size. */ - u64 disksize; /* bytes */ + size_t disksize; /* bytes */ struct ramzswap_stats stats; + + /* backing swap device info */ + struct ramzswap_backing_extent *curr_extent; + struct list_head backing_swap_extent_list; + unsigned long num_extents; + char backing_swap_name[MAX_SWAP_NAME_LEN]; + struct block_device *backing_swap; + struct file *swap_file; }; /*-- */ /* Debugging and Stats */ #if defined(CONFIG_RAMZSWAP_STATS) -static void rzs_stat_inc(u32 *v) +static void stat_inc(u32 *v) { *v = *v + 1; } -static void rzs_stat_dec(u32 *v) +static void stat_dec(u32 *v) { *v = *v - 1; } -static void rzs_stat64_inc(struct ramzswap *rzs, u64 *v) +static void 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) +static void stat64_dec(struct ramzswap *rzs, u64 *v) +{ + spin_lock(&rzs->stat64_lock); + *v = *v - 1; + spin_unlock(&rzs->stat64_lock); +} + +static u64 stat64_read(struct ramzswap *rzs, u64 *v) { u64 val; @@ -156,10 +200,11 @@ static u64 rzs_stat64_read(struct ramzswap *rzs, u64 *v) 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) +#define stat_inc(v) +#define stat_dec(v) +#define stat64_inc(r, v) +#define stat64_dec(r, v) +#define stat64_read(r, v) #endif /* CONFIG_RAMZSWAP_STATS */ #endif diff --git a/ramzswap_ioctl.h b/ramzswap_ioctl.h index db94bcb..d26076d 100644 --- a/ramzswap_ioctl.h +++ b/ramzswap_ioctl.h @@ -15,7 +15,11 @@ #ifndef _RAMZSWAP_IOCTL_H_ #define _RAMZSWAP_IOCTL_H_ +#define MAX_SWAP_NAME_LEN 128 + struct ramzswap_ioctl_stats { + char backing_swap_name[MAX_SWAP_NAME_LEN]; + u64 memlimit; /* only applicable if backing swap present */ u64 disksize; /* user specified or equal to backing swap * size (if present) */ u64 num_reads; /* failed + successful */ @@ -32,11 +36,15 @@ struct ramzswap_ioctl_stats { u64 orig_data_size; u64 compr_data_size; u64 mem_used_total; + u64 bdev_num_reads; /* no. of reads on backing dev */ + u64 bdev_num_writes; /* no. of writes on backing dev */ } __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) +#define RZSIO_SET_MEMLIMIT_KB _IOW('z', 1, size_t) +#define RZSIO_SET_BACKING_SWAP _IOW('z', 2, unsigned char[MAX_SWAP_NAME_LEN]) +#define RZSIO_GET_STATS _IOR('z', 3, struct ramzswap_ioctl_stats) +#define RZSIO_INIT _IO('z', 4) +#define RZSIO_RESET _IO('z', 5) #endif diff --git a/sub-projects/rzscontrol/man/rzscontrol.1 b/sub-projects/rzscontrol/man/rzscontrol.1 index a2f155c..bf82b3b 100644 --- a/sub-projects/rzscontrol/man/rzscontrol.1 +++ b/sub-projects/rzscontrol/man/rzscontrol.1 @@ -1,13 +1,13 @@ '\" t .\" Title: rzscontrol .\" Author: Nitin Gupta -.\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" Date: 05/19/2010 +.\" Generator: DocBook XSL Stylesheets v1.74.3 +.\" Date: 07/19/2009 .\" Manual: .\" Source: .\" Language: English .\" -.TH "RZSCONTROL" "1" "05/19/2010" "" "" +.TH "RZSCONTROL" "1" "07/19/2009" "" "" .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- @@ -22,27 +22,37 @@ rzscontrol \- control ramzswap devices .SH "SYNOPSIS" .HP \w'\fBrzscontrol\fR\ 'u -\fBrzscontrol\fR {ramzswap\ device} [\-d|\-\-disksize_kb=] [\-i|\-\-init] [\-r|\-\-reset] [\-s|\-\-stats] [\-v|\-\-verbose] [\-h|\-\-help] +\fBrzscontrol\fR {ramzswap\ device} [\-b|\-\-backing_swap=] [\-m|\-\-memlimit_kb=] [\-d|\-\-disksize_kb=] [\-i|\-\-init] [\-r|\-\-reset] [\-s|\-\-stats] [\-v|\-\-verbose] [\-h|\-\-help] .SH "DESCRIPTION" .PP -The ramzswap kernel module creates multiple ramzswap devices, equal to num_devices parameter\&. With rzscontrol, you can control each of these devices\&. +The ramzswap kernel module creates multiple ramzswap devices, equal to NUM_DEVICES parameter\&. With rzscontrol, you can control each of these devices\&. .PP This is how you use a ramzswap device (/dev/ramzswapX): .PP -\- Set [disksize] parameter (see OPTIONS and EXAMPLES)\&. +\- Set parameters for [backing swap, memory limit] or [disk size] (see OPTIONS and EXAMPLES)\&. .PP \- Issue init once these parameters are set: rzscontrol /dev/ramzswapX \-\-init\&. .PP -\- swapon /dev/ramzswapX (where X is ramzswap device id: 0, 1, 2\&.\&.\&. num_devices\-1)\&. +\- swapon /dev/ramzswapX (where X is ramzswap device id: 0, 1, 2\&.\&.\&. NUM_DEVICES\-1)\&. .PP \- When you are done with this swap: swapoff /dev/ramzswapX\&. NOTE: swapoff will not free any compressed pages\&. .PP \- After swapoff, issue reset: rzscontrol /dev/ramzswapX \-\-reset\&. This will free all metadata and compressed pages\&. .SH "OPTIONS" .PP +\fB\-b, \-\-backing_swap\fR +.RS 4 +Specify backing swap device\&. This can either be a swap block device or a swap file\&. Writes are forwarded to this device when memory limit is reached or when a page is incompressible\&. You must swapoff backing swap device before init is issued, otherwise init will fail with \-EBUSY error\&. Also, backing swap must be a valid swap device, otherwise \'swapon /dev/ramzswapX\' will fail as if swapon was issued directly on the backing swap device\&. +.RE +.PP +\fB\-m, \-\-memlimit_kb\fR +.RS 4 +Specify memory limit (in KB)\&. This is the limit on amount of memory allocated for compressed pages\&. When this memory limit is reached, all further writes are forwarded to backing swap device\&. This option is valid only if backing swap is specified\&. This must be smaller than or equal to backing swap size\&. Default: 15% of RAM or backing swap size, whichever is smaller\&. +.RE +.PP \fB\-d, \-\-disksize_kb\fR .RS 4 -Specify ramzswap disk size (in KB)\&. This is the limit on number of (uncompressed) pages that can be stored in this device\&. Default: 25% of RAM +Specify ramzswap disk size (in KB)\&. This is the limit on number of (uncompressed) pages that can be stored in this device\&. This parameter is valid only when backing swap is not present since in that case, it is implicitly equal to size of the backing swap device\&. Default: 25% of RAM .RE .PP \fB\-i, \-\-init\fR @@ -83,8 +93,7 @@ Load Module: .RS 4 .\} .nf - insmod ramzswap\&.ko num_devices=4 # creates 4 uninitialized devices: /dev/ramzswap{0,1,2,3} - +insmod ramzswap\&.ko NUM_DEVICES=4 # creates 4 uninitialized devices: /dev/ramzswap{0,1,2,3} .fi .if n \{\ .RE @@ -97,7 +106,10 @@ Initialize: .\} .nf rzscontrol /dev/ramzswap0 \-\-init # uses default value of disksize_kb - rzscontrol /dev/ramzswap1 \-\-disksize_kb=10240 \-\-init + rzscontrol /dev/ramzswap0 \-\-disksize_kb=10240 \-\-init + rzscontrol /dev/ramzswap1 \-\-backing_swap=/dev/sda2 \-\-init # uses default value of memlimit_kb + rzscontrol /dev/ramzswap1 \-\-backing_swap=/dev/sda2 \-\-memlimit_kb=10240 \-\-init + rzscontrol /dev/ramzswap2 \-\-backing_swap=/path/to/swap\&.file \-\-memlimit_kb=10240 \-\-init .fi .if n \{\ @@ -110,8 +122,7 @@ Activate: .RS 4 .\} .nf - swapon /dev/ramzswap1 # or any other initialized ramzswap device - +swapon /dev/ramzswap2 # or any other initialized ramzswap device .fi .if n \{\ .RE @@ -123,8 +134,7 @@ Stats: .RS 4 .\} .nf - rzscontrol /dev/ramzswap1 \-\-stats - +rzscontrol /dev/ramzswap2 \-\-stats .fi .if n \{\ .RE @@ -136,8 +146,7 @@ Deactivate: .RS 4 .\} .nf - swapoff /dev/ramzswap1 - +swapoff /dev/ramzswap2 .fi .if n \{\ .RE @@ -149,8 +158,7 @@ Reset: .RS 4 .\} .nf - rzscontrol /dev/ramzswap1 \-\-reset - +rzscontrol /dev/ramzswap2 \-\-reset .fi .if n \{\ .RE @@ -162,8 +170,7 @@ Unload Module: .RS 4 .\} .nf - rmmod ramzswap\&.ko - +rmmod ramzswap\&.ko .fi .if n \{\ .RE diff --git a/sub-projects/rzscontrol/man/rzscontrol.html b/sub-projects/rzscontrol/man/rzscontrol.html index 1a35bf4..8a56877 100644 --- a/sub-projects/rzscontrol/man/rzscontrol.html +++ b/sub-projects/rzscontrol/man/rzscontrol.html @@ -3,7 +3,7 @@ Manpage of RZSCONTROL

RZSCONTROL

-Section: (1)
Updated: 05/19/2010
Index +Section: (1)
Updated: 07/19/2009
Index Return to Main Contents
@@ -25,26 +25,26 @@

SYNOPSIS

-rzscontrol {ramzswap device} [-d|--disksize_kb=<value>] [-i|--init] [-r|--reset] [-s|--stats] [-v|--verbose] [-h|--help] +rzscontrol {ramzswap device} [-b|--backing_swap=<path>] [-m|--memlimit_kb=<value>] [-d|--disksize_kb=<value>] [-i|--init] [-r|--reset] [-s|--stats] [-v|--verbose] [-h|--help]
 

DESCRIPTION

-

The ramzswap kernel module creates multiple ramzswap devices, equal to num_devices parameter. With rzscontrol, you can control each of these devices. +
The ramzswap kernel module creates multiple ramzswap devices, equal to NUM_DEVICES parameter. With rzscontrol, you can control each of these devices.

This is how you use a ramzswap device (/dev/ramzswapX):

-- Set [disksize] parameter (see OPTIONS and EXAMPLES). +- Set parameters for [backing swap, memory limit] or [disk size] (see OPTIONS and EXAMPLES).

- Issue init once these parameters are set: rzscontrol /dev/ramzswapX --init.

-- swapon /dev/ramzswapX (where X is ramzswap device id: 0, 1, 2... num_devices-1). +- swapon /dev/ramzswapX (where X is ramzswap device id: 0, 1, 2... NUM_DEVICES-1).

- When you are done with this swap: swapoff /dev/ramzswapX. NOTE: swapoff will not free any compressed pages. @@ -56,9 +56,23 @@

OPTIONS

+-b, --backing_swap +

+Specify backing swap device. This can either be a swap block device or a swap file. Writes are forwarded to this device when memory limit is reached or when a page is incompressible. You must swapoff backing swap device before init is issued, otherwise init will fail with -EBUSY error. Also, backing swap must be a valid swap device, otherwise 'swapon /dev/ramzswapX' will fail as if swapon was issued directly on the backing swap device. +
+ +

+ +-m, --memlimit_kb +

+Specify memory limit (in KB). This is the limit on amount of memory allocated for compressed pages. When this memory limit is reached, all further writes are forwarded to backing swap device. This option is valid only if backing swap is specified. This must be smaller than or equal to backing swap size. Default: 15% of RAM or backing swap size, whichever is smaller. +
+ +

+ -d, --disksize_kb

-Specify ramzswap disk size (in KB). This is the limit on number of (uncompressed) pages that can be stored in this device. Default: 25% of RAM +Specify ramzswap disk size (in KB). This is the limit on number of (uncompressed) pages that can be stored in this device. This parameter is valid only when backing swap is not present since in that case, it is implicitly equal to size of the backing swap device. Default: 25% of RAM

@@ -115,8 +129,7 @@

EXAMPLES

-    insmod ramzswap.ko num_devices=4 # creates 4 uninitialized devices: /dev/ramzswap{0,1,2,3}
-    
+insmod ramzswap.ko NUM_DEVICES=4 # creates 4 uninitialized devices: /dev/ramzswap{0,1,2,3}
 
@@ -134,7 +147,10 @@

EXAMPLES

     rzscontrol /dev/ramzswap0 --init # uses default value of disksize_kb
-    rzscontrol /dev/ramzswap1 --disksize_kb=10240 --init
+    rzscontrol /dev/ramzswap0 --disksize_kb=10240 --init
+    rzscontrol /dev/ramzswap1 --backing_swap=/dev/sda2 --init # uses default value of memlimit_kb
+    rzscontrol /dev/ramzswap1 --backing_swap=/dev/sda2 --memlimit_kb=10240 --init
+    rzscontrol /dev/ramzswap2 --backing_swap=/path/to/swap.file --memlimit_kb=10240 --init
     
 
@@ -152,8 +168,7 @@

EXAMPLES

-    swapon /dev/ramzswap1 # or any other initialized ramzswap device
-    
+swapon /dev/ramzswap2 # or any other initialized ramzswap device
 
@@ -170,8 +185,7 @@

EXAMPLES

-    rzscontrol /dev/ramzswap1 --stats
-    
+rzscontrol /dev/ramzswap2 --stats
 
@@ -188,8 +202,7 @@

EXAMPLES

-    swapoff /dev/ramzswap1
-    
+swapoff /dev/ramzswap2
 
@@ -206,8 +219,7 @@

EXAMPLES

-    rzscontrol /dev/ramzswap1 --reset
-    
+rzscontrol /dev/ramzswap2 --reset
 
@@ -224,8 +236,7 @@

EXAMPLES

-    rmmod ramzswap.ko
-    
+rmmod ramzswap.ko
 
@@ -272,6 +283,6 @@

AUTHOR

This document was created by man2html, using the manual pages.
-Time: 02:20:56 GMT, May 19, 2010 +Time: 04:50:42 GMT, July 19, 2009 diff --git a/sub-projects/rzscontrol/man/rzscontrol.xml b/sub-projects/rzscontrol/man/rzscontrol.xml index bb92ea6..5070913 100644 --- a/sub-projects/rzscontrol/man/rzscontrol.xml +++ b/sub-projects/rzscontrol/man/rzscontrol.xml @@ -35,6 +35,8 @@ Synopsis rzscontrol ramzswap device + -b|--backing_swap=<path> + -m|--memlimit_kb=<value> -d|--disksize_kb=<value> -i|--init -r|--reset @@ -47,13 +49,13 @@ Description - The ramzswap kernel module creates multiple ramzswap devices, equal to num_devices parameter. With rzscontrol, + The ramzswap kernel module creates multiple ramzswap devices, equal to NUM_DEVICES parameter. With rzscontrol, you can control each of these devices. This is how you use a ramzswap device (/dev/ramzswapX): - - Set [disksize] parameter (see OPTIONS and EXAMPLES). + - Set parameters for [backing swap, memory limit] or [disk size] (see OPTIONS and EXAMPLES). - Issue init once these parameters are set: rzscontrol /dev/ramzswapX --init. - - swapon /dev/ramzswapX (where X is ramzswap device id: 0, 1, 2... num_devices-1). + - swapon /dev/ramzswapX (where X is ramzswap device id: 0, 1, 2... NUM_DEVICES-1). - When you are done with this swap: swapoff /dev/ramzswapX. NOTE: swapoff will not free any compressed pages. - After swapoff, issue reset: rzscontrol /dev/ramzswapX --reset. This will free all metadata and compressed pages. @@ -61,11 +63,30 @@ Options + + + + Specify backing swap device. This can either be a swap block device or a swap file. Writes are forwarded to + this device when memory limit is reached or when a page is incompressible. You must swapoff backing swap device + before init is issued, otherwise init will fail with -EBUSY error. Also, backing swap must be a valid swap device, + otherwise 'swapon /dev/ramzswapX' will fail as if swapon was issued directly on the backing swap device. + + + + + + Specify memory limit (in KB). This is the limit on amount of memory allocated for compressed pages. + When this memory limit is reached, all further writes are forwarded to backing swap device. This option + is valid only if backing swap is specified. This must be smaller than or equal to backing swap size. + Default: 15% of RAM or backing swap size, whichever is smaller. + + Specify ramzswap disk size (in KB). This is the limit on number of (uncompressed) pages that can - be stored in this device. Default: 25% of RAM + be stored in this device. This parameter is valid only when backing swap is not present since in that + case, it is implicitly equal to size of the backing swap device. Default: 25% of RAM @@ -111,34 +132,25 @@ Following shows a typical sequence of steps for using ramzswap. Initialization can be done with many variations of configuration parameters as shown below. Load Module: - - insmod ramzswap.ko num_devices=4 # creates 4 uninitialized devices: /dev/ramzswap{0,1,2,3} - + insmod ramzswap.ko NUM_DEVICES=4 # creates 4 uninitialized devices: /dev/ramzswap{0,1,2,3} Initialize: rzscontrol /dev/ramzswap0 --init # uses default value of disksize_kb - rzscontrol /dev/ramzswap1 --disksize_kb=10240 --init + rzscontrol /dev/ramzswap0 --disksize_kb=10240 --init + rzscontrol /dev/ramzswap1 --backing_swap=/dev/sda2 --init # uses default value of memlimit_kb + rzscontrol /dev/ramzswap1 --backing_swap=/dev/sda2 --memlimit_kb=10240 --init + rzscontrol /dev/ramzswap2 --backing_swap=/path/to/swap.file --memlimit_kb=10240 --init Activate: - - swapon /dev/ramzswap1 # or any other initialized ramzswap device - + swapon /dev/ramzswap2 # or any other initialized ramzswap device Stats: - - rzscontrol /dev/ramzswap1 --stats - + rzscontrol /dev/ramzswap2 --stats Deactivate: - - swapoff /dev/ramzswap1 - + swapoff /dev/ramzswap2 Reset: - - rzscontrol /dev/ramzswap1 --reset - + rzscontrol /dev/ramzswap2 --reset Unload Module: - - rmmod ramzswap.ko - + rmmod ramzswap.ko NOTE: ramzswap parameters cannot be changed once the device is initialized. It has to be reset before new parameters can be used. diff --git a/sub-projects/rzscontrol/rzscontrol.c b/sub-projects/rzscontrol/rzscontrol.c index 6553d7c..ccde2fd 100644 --- a/sub-projects/rzscontrol/rzscontrol.c +++ b/sub-projects/rzscontrol/rzscontrol.c @@ -45,6 +45,8 @@ static int verbose_flag; } while (0) struct rzs_args { + char backing_swap[MAX_SWAP_NAME_LEN]; + u64 memlimit_kb; u64 disksize_kb; int init; int reset; @@ -60,6 +62,11 @@ void usage(void) void show_stats(struct ramzswap_ioctl_stats *s) { + int backing_swap_present = (s->backing_swap_name[0] != '\0'); + + if (backing_swap_present) + printf("BackingSwap: %s\n", s->backing_swap_name); + #define K(x) ((x) >> 10) /* Basic stats */ printf( @@ -67,6 +74,10 @@ void show_stats(struct ramzswap_ioctl_stats *s) K(s->disksize) ); + /* memlimit is valid only if backing swap is present */ + if (backing_swap_present) + printf("MemLimit: %8" PRIu64 " kB\n", K(s->memlimit)); + /* Extended stats */ printf( "NumReads: %8" PRIu64 "\n" @@ -98,6 +109,15 @@ void show_stats(struct ramzswap_ioctl_stats *s) K(s->compr_data_size), K(s->mem_used_total) ); + + if (backing_swap_present) { + printf( + "BDevNumReads: %8" PRIu64 "\n" + "BDevNumWrites: %8" PRIu64 "\n", + s->bdev_num_reads, + s->bdev_num_writes + ); + } } int do_ioctl(int fd, int argc, struct rzs_args *args) @@ -116,6 +136,20 @@ int do_ioctl(int fd, int argc, struct rzs_args *args) while (argc--) { + if (args->backing_swap[0] != '\0') { + VERBOSE("backing_swap: %s\n", args->backing_swap); + ret = ioctl(fd, RZSIO_SET_BACKING_SWAP, args->backing_swap); + args->backing_swap[0] = '\0'; + ON_ERR("backing_swap"); + } + + if (args->memlimit_kb) { + VERBOSE("memlimit_kb: %" PRIu64 "\n", args->memlimit_kb); + ret = ioctl(fd, RZSIO_SET_MEMLIMIT_KB, &args->memlimit_kb); + args->memlimit_kb = 0; + ON_ERR("memlimit_kb"); + } + if (args->disksize_kb) { VERBOSE("disksize_kb: %" PRIu64 "\n", args->disksize_kb); ret = ioctl(fd, RZSIO_SET_DISKSIZE_KB, &args->disksize_kb); @@ -180,6 +214,8 @@ int main(int argc, char *argv[]) char *endptr = NULL; static struct option long_options[] = { + { "backing_swap", required_argument, 0, 'b' }, + { "memlimit_kb", required_argument, 0, 'm' }, { "disksize_kb", required_argument, 0, 'd' }, { "init", no_argument, 0, 'i' }, { "reset", no_argument, 0, 'r' }, @@ -195,6 +231,21 @@ int main(int argc, char *argv[]) break; switch (opt) { + case 'b': + strncpy(args.backing_swap, optarg, MAX_SWAP_NAME_LEN - 1); + args.backing_swap[MAX_SWAP_NAME_LEN - 1] = '\0'; + break; + + case 'm': + if (strnlen(optarg, MAX_SIZE_LEN + 1) > MAX_SIZE_LEN) { + printf("memlimit_kb: %s\n", strerror(EOVERFLOW)); + ret = -EOVERFLOW; + goto out; + } + + args.memlimit_kb = strtoul(optarg, &endptr, 10); + break; + case 'd': if (strnlen(optarg, MAX_SIZE_LEN + 1) > MAX_SIZE_LEN) { printf("disksize_kb: %s\n", strerror(EOVERFLOW));