Btrfs: add new defrag-range ioctl.

The btrfs defrag ioctl was limited to doing the entire file.  This
commit adds a new interface that can defrag a specific range inside
the file.

It can also force compression on the file, allowing you to selectively
compress individual files after they were created, even when mount -o
compress isn't turned on.

Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
Chris Mason 2010-03-11 09:42:04 -05:00
Родитель 940100a4a7
Коммит 1e701a3292
5 изменённых файлов: 117 добавлений и 14 удалений

Просмотреть файл

@ -153,6 +153,11 @@ struct btrfs_inode {
unsigned ordered_data_close:1; unsigned ordered_data_close:1;
unsigned dummy_inode:1; unsigned dummy_inode:1;
/*
* always compress this one file
*/
unsigned force_compress:1;
struct inode vfs_inode; struct inode vfs_inode;
}; };

Просмотреть файл

@ -1184,7 +1184,6 @@ struct btrfs_root {
#define BTRFS_INODE_NOATIME (1 << 9) #define BTRFS_INODE_NOATIME (1 << 9)
#define BTRFS_INODE_DIRSYNC (1 << 10) #define BTRFS_INODE_DIRSYNC (1 << 10)
/* some macros to generate set/get funcs for the struct fields. This /* some macros to generate set/get funcs for the struct fields. This
* assumes there is a lefoo_to_cpu for every type, so lets make a simple * assumes there is a lefoo_to_cpu for every type, so lets make a simple
* one for u8: * one for u8:

Просмотреть файл

@ -379,7 +379,8 @@ again:
* change at any time if we discover bad compression ratios. * change at any time if we discover bad compression ratios.
*/ */
if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS) && if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS) &&
btrfs_test_opt(root, COMPRESS)) { (btrfs_test_opt(root, COMPRESS) ||
(BTRFS_I(inode)->force_compress))) {
WARN_ON(pages); WARN_ON(pages);
pages = kzalloc(sizeof(struct page *) * nr_pages, GFP_NOFS); pages = kzalloc(sizeof(struct page *) * nr_pages, GFP_NOFS);
@ -483,8 +484,10 @@ again:
nr_pages_ret = 0; nr_pages_ret = 0;
/* flag the file so we don't compress in the future */ /* flag the file so we don't compress in the future */
if (!btrfs_test_opt(root, FORCE_COMPRESS)) if (!btrfs_test_opt(root, FORCE_COMPRESS) &&
!(BTRFS_I(inode)->force_compress)) {
BTRFS_I(inode)->flags |= BTRFS_INODE_NOCOMPRESS; BTRFS_I(inode)->flags |= BTRFS_INODE_NOCOMPRESS;
}
} }
if (will_compress) { if (will_compress) {
*num_added += 1; *num_added += 1;
@ -1211,7 +1214,8 @@ static int run_delalloc_range(struct inode *inode, struct page *locked_page,
else if (BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC) else if (BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC)
ret = run_delalloc_nocow(inode, locked_page, start, end, ret = run_delalloc_nocow(inode, locked_page, start, end,
page_started, 0, nr_written); page_started, 0, nr_written);
else if (!btrfs_test_opt(root, COMPRESS)) else if (!btrfs_test_opt(root, COMPRESS) &&
!(BTRFS_I(inode)->force_compress))
ret = cow_file_range(inode, locked_page, start, end, ret = cow_file_range(inode, locked_page, start, end,
page_started, nr_written, 1); page_started, nr_written, 1);
else else
@ -3639,6 +3643,7 @@ static noinline void init_btrfs_i(struct inode *inode)
bi->index_cnt = (u64)-1; bi->index_cnt = (u64)-1;
bi->last_unlink_trans = 0; bi->last_unlink_trans = 0;
bi->ordered_data_close = 0; bi->ordered_data_close = 0;
bi->force_compress = 0;
extent_map_tree_init(&BTRFS_I(inode)->extent_tree, GFP_NOFS); extent_map_tree_init(&BTRFS_I(inode)->extent_tree, GFP_NOFS);
extent_io_tree_init(&BTRFS_I(inode)->io_tree, extent_io_tree_init(&BTRFS_I(inode)->io_tree,
inode->i_mapping, GFP_NOFS); inode->i_mapping, GFP_NOFS);

Просмотреть файл

@ -476,13 +476,18 @@ out_unlock:
} }
static int should_defrag_range(struct inode *inode, u64 start, u64 len, static int should_defrag_range(struct inode *inode, u64 start, u64 len,
u64 *last_len, u64 *skip, u64 *defrag_end) int thresh, u64 *last_len, u64 *skip,
u64 *defrag_end)
{ {
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
struct extent_map *em = NULL; struct extent_map *em = NULL;
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree; struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
int ret = 1; int ret = 1;
if (thresh == 0)
thresh = 256 * 1024;
/* /*
* make sure that once we start defragging and extent, we keep on * make sure that once we start defragging and extent, we keep on
* defragging it * defragging it
@ -517,8 +522,7 @@ static int should_defrag_range(struct inode *inode, u64 start, u64 len,
/* /*
* we hit a real extent, if it is big don't bother defragging it again * we hit a real extent, if it is big don't bother defragging it again
*/ */
if ((*last_len == 0 || *last_len >= 256 * 1024) && if ((*last_len == 0 || *last_len >= thresh) && em->len >= thresh)
em->len >= 256 * 1024)
ret = 0; ret = 0;
/* /*
@ -542,7 +546,8 @@ static int should_defrag_range(struct inode *inode, u64 start, u64 len,
return ret; return ret;
} }
static int btrfs_defrag_file(struct file *file) static int btrfs_defrag_file(struct file *file,
struct btrfs_ioctl_defrag_range_args *range)
{ {
struct inode *inode = fdentry(file)->d_inode; struct inode *inode = fdentry(file)->d_inode;
struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_root *root = BTRFS_I(inode)->root;
@ -563,11 +568,19 @@ static int btrfs_defrag_file(struct file *file)
if (inode->i_size == 0) if (inode->i_size == 0)
return 0; return 0;
last_index = (inode->i_size - 1) >> PAGE_CACHE_SHIFT; if (range->start + range->len > range->start) {
i = 0; last_index = min_t(u64, inode->i_size - 1,
range->start + range->len - 1) >> PAGE_CACHE_SHIFT;
} else {
last_index = (inode->i_size - 1) >> PAGE_CACHE_SHIFT;
}
i = range->start >> PAGE_CACHE_SHIFT;
while (i <= last_index) { while (i <= last_index) {
if (!should_defrag_range(inode, (u64)i << PAGE_CACHE_SHIFT, if (!should_defrag_range(inode, (u64)i << PAGE_CACHE_SHIFT,
PAGE_CACHE_SIZE, &last_len, &skip, PAGE_CACHE_SIZE,
range->extent_thresh,
&last_len, &skip,
&defrag_end)) { &defrag_end)) {
unsigned long next; unsigned long next;
/* /*
@ -585,6 +598,8 @@ static int btrfs_defrag_file(struct file *file)
} }
total_read++; total_read++;
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
if (range->flags & BTRFS_DEFRAG_RANGE_COMPRESS)
BTRFS_I(inode)->force_compress = 1;
ret = btrfs_check_data_free_space(root, inode, PAGE_CACHE_SIZE); ret = btrfs_check_data_free_space(root, inode, PAGE_CACHE_SIZE);
if (ret) { if (ret) {
@ -673,6 +688,28 @@ loop_unlock:
i++; i++;
} }
if ((range->flags & BTRFS_DEFRAG_RANGE_START_IO))
filemap_flush(inode->i_mapping);
if ((range->flags & BTRFS_DEFRAG_RANGE_COMPRESS)) {
/* the filemap_flush will queue IO into the worker threads, but
* we have to make sure the IO is actually started and that
* ordered extents get created before we return
*/
atomic_inc(&root->fs_info->async_submit_draining);
while (atomic_read(&root->fs_info->nr_async_submits) ||
atomic_read(&root->fs_info->async_delalloc_pages)) {
wait_event(root->fs_info->async_submit_wait,
(atomic_read(&root->fs_info->nr_async_submits) == 0 &&
atomic_read(&root->fs_info->async_delalloc_pages) == 0));
}
atomic_dec(&root->fs_info->async_submit_draining);
mutex_lock(&inode->i_mutex);
BTRFS_I(inode)->force_compress = 0;
mutex_unlock(&inode->i_mutex);
}
return 0; return 0;
err_reservations: err_reservations:
@ -1284,10 +1321,11 @@ out:
return err; return err;
} }
static int btrfs_ioctl_defrag(struct file *file) static int btrfs_ioctl_defrag(struct file *file, void __user *argp)
{ {
struct inode *inode = fdentry(file)->d_inode; struct inode *inode = fdentry(file)->d_inode;
struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_ioctl_defrag_range_args *range;
int ret; int ret;
ret = mnt_want_write(file->f_path.mnt); ret = mnt_want_write(file->f_path.mnt);
@ -1308,7 +1346,30 @@ static int btrfs_ioctl_defrag(struct file *file)
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
btrfs_defrag_file(file);
range = kzalloc(sizeof(*range), GFP_KERNEL);
if (!range) {
ret = -ENOMEM;
goto out;
}
if (argp) {
if (copy_from_user(range, argp,
sizeof(*range))) {
ret = -EFAULT;
kfree(range);
}
/* compression requires us to start the IO */
if ((range->flags & BTRFS_DEFRAG_RANGE_COMPRESS)) {
range->flags |= BTRFS_DEFRAG_RANGE_START_IO;
range->extent_thresh = (u32)-1;
}
} else {
/* the rest are all set to zero by kzalloc */
range->len = (u64)-1;
}
btrfs_defrag_file(file, range);
kfree(range);
break; break;
} }
out: out:
@ -1831,7 +1892,9 @@ long btrfs_ioctl(struct file *file, unsigned int
case BTRFS_IOC_DEFAULT_SUBVOL: case BTRFS_IOC_DEFAULT_SUBVOL:
return btrfs_ioctl_default_subvol(file, argp); return btrfs_ioctl_default_subvol(file, argp);
case BTRFS_IOC_DEFRAG: case BTRFS_IOC_DEFRAG:
return btrfs_ioctl_defrag(file); return btrfs_ioctl_defrag(file, NULL);
case BTRFS_IOC_DEFRAG_RANGE:
return btrfs_ioctl_defrag(file, argp);
case BTRFS_IOC_RESIZE: case BTRFS_IOC_RESIZE:
return btrfs_ioctl_resize(root, argp); return btrfs_ioctl_resize(root, argp);
case BTRFS_IOC_ADD_DEV: case BTRFS_IOC_ADD_DEV:

Просмотреть файл

@ -99,6 +99,35 @@ struct btrfs_ioctl_clone_range_args {
__u64 dest_offset; __u64 dest_offset;
}; };
/* flags for the defrag range ioctl */
#define BTRFS_DEFRAG_RANGE_COMPRESS 1
#define BTRFS_DEFRAG_RANGE_START_IO 2
struct btrfs_ioctl_defrag_range_args {
/* start of the defrag operation */
__u64 start;
/* number of bytes to defrag, use (u64)-1 to say all */
__u64 len;
/*
* flags for the operation, which can include turning
* on compression for this one defrag
*/
__u64 flags;
/*
* any extent bigger than this will be considered
* already defragged. Use 0 to take the kernel default
* Use 1 to say every single extent must be rewritten
*/
__u32 extent_thresh;
/* spare for later */
__u32 unused[5];
};
#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \ #define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
struct btrfs_ioctl_vol_args) struct btrfs_ioctl_vol_args)
#define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \ #define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \
@ -130,6 +159,8 @@ struct btrfs_ioctl_clone_range_args {
struct btrfs_ioctl_vol_args) struct btrfs_ioctl_vol_args)
#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \ #define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
struct btrfs_ioctl_vol_args) struct btrfs_ioctl_vol_args)
#define BTRFS_IOC_DEFRAG_RANGE _IOW(BTRFS_IOCTL_MAGIC, 16, \
struct btrfs_ioctl_defrag_range_args)
#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \ #define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
struct btrfs_ioctl_search_args) struct btrfs_ioctl_search_args)
#define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \ #define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \