mirror of
https://github.com/genesi/linux-legacy.git
synced 2026-07-08 17:56:11 +00:00
@@ -125,7 +125,5 @@ source "drivers/staging/pata_rdc/Kconfig"
|
||||
|
||||
source "drivers/staging/udlfb/Kconfig"
|
||||
|
||||
source "drivers/staging/ramzswap/Kconfig"
|
||||
|
||||
endif # !STAGING_EXCLUDE_BUILD
|
||||
endif # STAGING
|
||||
|
||||
@@ -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 be used (only) as a 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.
|
||||
@@ -1,3 +0,0 @@
|
||||
ramzswap-objs := ramzswap_drv.o xvmalloc.o
|
||||
|
||||
obj-$(CONFIG_RAMZSWAP) += ramzswap.o
|
||||
@@ -1,5 +0,0 @@
|
||||
TODO:
|
||||
- Add support for swap notifiers
|
||||
|
||||
Please send patches to Greg Kroah-Hartman <greg@kroah.com> and
|
||||
Nitin Gupta <ngupta@vflare.org>
|
||||
@@ -1,51 +0,0 @@
|
||||
ramzswap: Compressed RAM based swap device
|
||||
-------------------------------------------
|
||||
|
||||
Project home: http://compcache.googlecode.com/
|
||||
|
||||
* Introduction
|
||||
|
||||
It creates RAM based block devices which can be used (only) 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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Compressed RAM based swap device
|
||||
*
|
||||
* Copyright (C) 2008, 2009 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 "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 or
|
||||
* migrating compressed pages to backing swap disk.
|
||||
*/
|
||||
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;
|
||||
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_nobdev = PAGE_SIZE / 4 * 3;
|
||||
|
||||
/*
|
||||
* NOTE: max_zpage_size_{bdev,nobdev} sizes must be
|
||||
* less than or equal to:
|
||||
* XV_MAX_ALLOC_SIZE - sizeof(struct zobj_header)
|
||||
* since 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)
|
||||
|
||||
/* Debugging and Stats */
|
||||
#if defined(CONFIG_RAMZSWAP_STATS)
|
||||
#define stat_inc(stat) ((stat)++)
|
||||
#define stat_dec(stat) ((stat)--)
|
||||
#else
|
||||
#define stat_inc(x)
|
||||
#define stat_dec(x)
|
||||
#endif
|
||||
|
||||
/* 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)));;
|
||||
|
||||
/*
|
||||
* 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 -
|
||||
* needed to enforce memlimit */
|
||||
/* more stats */
|
||||
#if defined(CONFIG_RAMZSWAP_STATS)
|
||||
u64 num_reads; /* failed + successful */
|
||||
u64 num_writes; /* --do-- */
|
||||
u64 failed_reads; /* can happen when memory is too low */
|
||||
u64 failed_writes; /* should NEVER! happen */
|
||||
u64 invalid_io; /* non-swap I/O requests */
|
||||
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 */
|
||||
u64 bdev_num_reads; /* no. of reads on backing dev */
|
||||
u64 bdev_num_writes; /* no. of writes on backing dev */
|
||||
#endif
|
||||
};
|
||||
|
||||
struct ramzswap {
|
||||
struct xv_pool *mem_pool;
|
||||
void *compress_workmem;
|
||||
void *compress_buffer;
|
||||
struct table *table;
|
||||
struct mutex lock;
|
||||
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. When backing swap device is provided, it is
|
||||
* set equal to device size.
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
/*-- */
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Compressed RAM based swap device
|
||||
*
|
||||
* Copyright (C) 2008, 2009 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_
|
||||
|
||||
#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 */
|
||||
u64 num_writes; /* --do-- */
|
||||
u64 failed_reads; /* can happen when memory is too low */
|
||||
u64 failed_writes; /* should NEVER! happen */
|
||||
u64 invalid_io; /* non-swap I/O requests */
|
||||
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;
|
||||
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_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
|
||||
@@ -1,507 +0,0 @@
|
||||
/*
|
||||
* xvmalloc memory allocator
|
||||
*
|
||||
* Copyright (C) 2008, 2009 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 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;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* xvmalloc memory allocator
|
||||
*
|
||||
* Copyright (C) 2008, 2009 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
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* xvmalloc memory allocator
|
||||
*
|
||||
* Copyright (C) 2008, 2009 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 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
|
||||
Reference in New Issue
Block a user