Skip to content

Commit

Permalink
btrfs: Allow to add new compression algorithm
Browse files Browse the repository at this point in the history
Make the code aware of compression type, instead of always assuming
zlib compression.

Also make the zlib workspace function as common code for all
compression types.

Signed-off-by: Li Zefan <lizf@cn.fujitsu.com>
  • Loading branch information
Li Zefan committed Dec 22, 2010
1 parent 4b72029 commit 261507a
Show file tree
Hide file tree
Showing 15 changed files with 473 additions and 282 deletions.
2 changes: 1 addition & 1 deletion fs/btrfs/btrfs_inode.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ struct btrfs_inode {
/*
* always compress this one file
*/
unsigned force_compress:1;
unsigned force_compress:4;

struct inode vfs_inode;
};
Expand Down
236 changes: 231 additions & 5 deletions fs/btrfs/compression.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ struct compressed_bio {
/* number of bytes on disk */
unsigned long compressed_len;

/* the compression algorithm for this bio */
int compress_type;

/* number of compressed pages in the array */
unsigned long nr_pages;

Expand Down Expand Up @@ -173,11 +176,12 @@ static void end_compressed_bio_read(struct bio *bio, int err)
/* ok, we're the last bio for this extent, lets start
* the decompression.
*/
ret = btrfs_zlib_decompress_biovec(cb->compressed_pages,
cb->start,
cb->orig_bio->bi_io_vec,
cb->orig_bio->bi_vcnt,
cb->compressed_len);
ret = btrfs_decompress_biovec(cb->compress_type,
cb->compressed_pages,
cb->start,
cb->orig_bio->bi_io_vec,
cb->orig_bio->bi_vcnt,
cb->compressed_len);
csum_failed:
if (ret)
cb->errors = 1;
Expand Down Expand Up @@ -588,6 +592,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,

cb->len = uncompressed_len;
cb->compressed_len = compressed_len;
cb->compress_type = extent_compress_type(bio_flags);
cb->orig_bio = bio;

nr_pages = (compressed_len + PAGE_CACHE_SIZE - 1) /
Expand Down Expand Up @@ -677,3 +682,224 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
bio_put(comp_bio);
return 0;
}

static struct list_head comp_idle_workspace[BTRFS_COMPRESS_TYPES];
static spinlock_t comp_workspace_lock[BTRFS_COMPRESS_TYPES];
static int comp_num_workspace[BTRFS_COMPRESS_TYPES];
static atomic_t comp_alloc_workspace[BTRFS_COMPRESS_TYPES];
static wait_queue_head_t comp_workspace_wait[BTRFS_COMPRESS_TYPES];

struct btrfs_compress_op *btrfs_compress_op[] = {
&btrfs_zlib_compress,
};

int __init btrfs_init_compress(void)
{
int i;

for (i = 0; i < BTRFS_COMPRESS_TYPES; i++) {
INIT_LIST_HEAD(&comp_idle_workspace[i]);
spin_lock_init(&comp_workspace_lock[i]);
atomic_set(&comp_alloc_workspace[i], 0);
init_waitqueue_head(&comp_workspace_wait[i]);
}
return 0;
}

/*
* this finds an available workspace or allocates a new one
* ERR_PTR is returned if things go bad.
*/
static struct list_head *find_workspace(int type)
{
struct list_head *workspace;
int cpus = num_online_cpus();
int idx = type - 1;

struct list_head *idle_workspace = &comp_idle_workspace[idx];
spinlock_t *workspace_lock = &comp_workspace_lock[idx];
atomic_t *alloc_workspace = &comp_alloc_workspace[idx];
wait_queue_head_t *workspace_wait = &comp_workspace_wait[idx];
int *num_workspace = &comp_num_workspace[idx];
again:
spin_lock(workspace_lock);
if (!list_empty(idle_workspace)) {
workspace = idle_workspace->next;
list_del(workspace);
(*num_workspace)--;
spin_unlock(workspace_lock);
return workspace;

}
if (atomic_read(alloc_workspace) > cpus) {
DEFINE_WAIT(wait);

spin_unlock(workspace_lock);
prepare_to_wait(workspace_wait, &wait, TASK_UNINTERRUPTIBLE);
if (atomic_read(alloc_workspace) > cpus && !*num_workspace)
schedule();
finish_wait(workspace_wait, &wait);
goto again;
}
atomic_inc(alloc_workspace);
spin_unlock(workspace_lock);

workspace = btrfs_compress_op[idx]->alloc_workspace();
if (IS_ERR(workspace)) {
atomic_dec(alloc_workspace);
wake_up(workspace_wait);
}
return workspace;
}

/*
* put a workspace struct back on the list or free it if we have enough
* idle ones sitting around
*/
static void free_workspace(int type, struct list_head *workspace)
{
int idx = type - 1;
struct list_head *idle_workspace = &comp_idle_workspace[idx];
spinlock_t *workspace_lock = &comp_workspace_lock[idx];
atomic_t *alloc_workspace = &comp_alloc_workspace[idx];
wait_queue_head_t *workspace_wait = &comp_workspace_wait[idx];
int *num_workspace = &comp_num_workspace[idx];

spin_lock(workspace_lock);
if (*num_workspace < num_online_cpus()) {
list_add_tail(workspace, idle_workspace);
(*num_workspace)++;
spin_unlock(workspace_lock);
goto wake;
}
spin_unlock(workspace_lock);

btrfs_compress_op[idx]->free_workspace(workspace);
atomic_dec(alloc_workspace);
wake:
if (waitqueue_active(workspace_wait))
wake_up(workspace_wait);
}

/*
* cleanup function for module exit
*/
static void free_workspaces(void)
{
struct list_head *workspace;
int i;

for (i = 0; i < BTRFS_COMPRESS_TYPES; i++) {
while (!list_empty(&comp_idle_workspace[i])) {
workspace = comp_idle_workspace[i].next;
list_del(workspace);
btrfs_compress_op[i]->free_workspace(workspace);
atomic_dec(&comp_alloc_workspace[i]);
}
}
}

/*
* given an address space and start/len, compress the bytes.
*
* pages are allocated to hold the compressed result and stored
* in 'pages'
*
* out_pages is used to return the number of pages allocated. There
* may be pages allocated even if we return an error
*
* total_in is used to return the number of bytes actually read. It
* may be smaller then len if we had to exit early because we
* ran out of room in the pages array or because we cross the
* max_out threshold.
*
* total_out is used to return the total number of compressed bytes
*
* max_out tells us the max number of bytes that we're allowed to
* stuff into pages
*/
int btrfs_compress_pages(int type, struct address_space *mapping,
u64 start, unsigned long len,
struct page **pages,
unsigned long nr_dest_pages,
unsigned long *out_pages,
unsigned long *total_in,
unsigned long *total_out,
unsigned long max_out)
{
struct list_head *workspace;
int ret;

workspace = find_workspace(type);
if (IS_ERR(workspace))
return -1;

ret = btrfs_compress_op[type-1]->compress_pages(workspace, mapping,
start, len, pages,
nr_dest_pages, out_pages,
total_in, total_out,
max_out);
free_workspace(type, workspace);
return ret;
}

/*
* pages_in is an array of pages with compressed data.
*
* disk_start is the starting logical offset of this array in the file
*
* bvec is a bio_vec of pages from the file that we want to decompress into
*
* vcnt is the count of pages in the biovec
*
* srclen is the number of bytes in pages_in
*
* The basic idea is that we have a bio that was created by readpages.
* The pages in the bio are for the uncompressed data, and they may not
* be contiguous. They all correspond to the range of bytes covered by
* the compressed extent.
*/
int btrfs_decompress_biovec(int type, struct page **pages_in, u64 disk_start,
struct bio_vec *bvec, int vcnt, size_t srclen)
{
struct list_head *workspace;
int ret;

workspace = find_workspace(type);
if (IS_ERR(workspace))
return -ENOMEM;

ret = btrfs_compress_op[type-1]->decompress_biovec(workspace, pages_in,
disk_start,
bvec, vcnt, srclen);
free_workspace(type, workspace);
return ret;
}

/*
* a less complex decompression routine. Our compressed data fits in a
* single page, and we want to read a single page out of it.
* start_byte tells us the offset into the compressed data we're interested in
*/
int btrfs_decompress(int type, unsigned char *data_in, struct page *dest_page,
unsigned long start_byte, size_t srclen, size_t destlen)
{
struct list_head *workspace;
int ret;

workspace = find_workspace(type);
if (IS_ERR(workspace))
return -ENOMEM;

ret = btrfs_compress_op[type-1]->decompress(workspace, data_in,
dest_page, start_byte,
srclen, destlen);

free_workspace(type, workspace);
return ret;
}

void __exit btrfs_exit_compress(void)
{
free_workspaces();
}
66 changes: 48 additions & 18 deletions fs/btrfs/compression.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,59 @@
#ifndef __BTRFS_COMPRESSION_
#define __BTRFS_COMPRESSION_

int btrfs_zlib_decompress(unsigned char *data_in,
struct page *dest_page,
unsigned long start_byte,
size_t srclen, size_t destlen);
int btrfs_zlib_compress_pages(struct address_space *mapping,
u64 start, unsigned long len,
struct page **pages,
unsigned long nr_dest_pages,
unsigned long *out_pages,
unsigned long *total_in,
unsigned long *total_out,
unsigned long max_out);
int btrfs_zlib_decompress_biovec(struct page **pages_in,
u64 disk_start,
struct bio_vec *bvec,
int vcnt,
size_t srclen);
void btrfs_zlib_exit(void);
int btrfs_init_compress(void);
void btrfs_exit_compress(void);

int btrfs_compress_pages(int type, struct address_space *mapping,
u64 start, unsigned long len,
struct page **pages,
unsigned long nr_dest_pages,
unsigned long *out_pages,
unsigned long *total_in,
unsigned long *total_out,
unsigned long max_out);
int btrfs_decompress_biovec(int type, struct page **pages_in, u64 disk_start,
struct bio_vec *bvec, int vcnt, size_t srclen);
int btrfs_decompress(int type, unsigned char *data_in, struct page *dest_page,
unsigned long start_byte, size_t srclen, size_t destlen);

int btrfs_submit_compressed_write(struct inode *inode, u64 start,
unsigned long len, u64 disk_start,
unsigned long compressed_len,
struct page **compressed_pages,
unsigned long nr_pages);
int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
int mirror_num, unsigned long bio_flags);

struct btrfs_compress_op {
struct list_head *(*alloc_workspace)(void);

void (*free_workspace)(struct list_head *workspace);

int (*compress_pages)(struct list_head *workspace,
struct address_space *mapping,
u64 start, unsigned long len,
struct page **pages,
unsigned long nr_dest_pages,
unsigned long *out_pages,
unsigned long *total_in,
unsigned long *total_out,
unsigned long max_out);

int (*decompress_biovec)(struct list_head *workspace,
struct page **pages_in,
u64 disk_start,
struct bio_vec *bvec,
int vcnt,
size_t srclen);

int (*decompress)(struct list_head *workspace,
unsigned char *data_in,
struct page *dest_page,
unsigned long start_byte,
size_t srclen, size_t destlen);
};

extern struct btrfs_compress_op btrfs_zlib_compress;

#endif
10 changes: 6 additions & 4 deletions fs/btrfs/ctree.h
Original file line number Diff line number Diff line change
Expand Up @@ -551,9 +551,10 @@ struct btrfs_timespec {
} __attribute__ ((__packed__));

enum btrfs_compression_type {
BTRFS_COMPRESS_NONE = 0,
BTRFS_COMPRESS_ZLIB = 1,
BTRFS_COMPRESS_LAST = 2,
BTRFS_COMPRESS_NONE = 0,
BTRFS_COMPRESS_ZLIB = 1,
BTRFS_COMPRESS_TYPES = 1,
BTRFS_COMPRESS_LAST = 2,
};

struct btrfs_inode_item {
Expand Down Expand Up @@ -895,7 +896,8 @@ struct btrfs_fs_info {
*/
u64 last_trans_log_full_commit;
u64 open_ioctl_trans;
unsigned long mount_opt;
unsigned long mount_opt:20;
unsigned long compress_type:4;
u64 max_inline;
u64 alloc_start;
struct btrfs_transaction *running_transaction;
Expand Down
5 changes: 4 additions & 1 deletion fs/btrfs/extent_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -2028,8 +2028,11 @@ static int __extent_read_full_page(struct extent_io_tree *tree,
BUG_ON(extent_map_end(em) <= cur);
BUG_ON(end < cur);

if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags))
if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) {
this_bio_flag = EXTENT_BIO_COMPRESSED;
extent_set_compress_type(&this_bio_flag,
em->compress_type);
}

iosize = min(extent_map_end(em) - cur, end - cur + 1);
cur_end = min(extent_map_end(em) - 1, end);
Expand Down
Loading

0 comments on commit 261507a

Please sign in to comment.