Add support for defragging files via btrfsctl -d. Avoid OOM on extent tree
defrag. Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
parent
8e21528f87
commit
86479a04ee
@ -217,6 +217,9 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
|
|||||||
root->fs_info->generation);
|
root->fs_info->generation);
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
}
|
}
|
||||||
|
if (buffer_defrag_done(parent))
|
||||||
|
return 0;
|
||||||
|
|
||||||
parent_node = btrfs_buffer_node(parent);
|
parent_node = btrfs_buffer_node(parent);
|
||||||
parent_nritems = btrfs_header_nritems(&parent_node->header);
|
parent_nritems = btrfs_header_nritems(&parent_node->header);
|
||||||
parent_level = btrfs_header_level(&parent_node->header);
|
parent_level = btrfs_header_level(&parent_node->header);
|
||||||
@ -274,6 +277,7 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
|
|||||||
*last_ret = search_start;
|
*last_ret = search_start;
|
||||||
if (parent_level == 1)
|
if (parent_level == 1)
|
||||||
clear_buffer_defrag(tmp_bh);
|
clear_buffer_defrag(tmp_bh);
|
||||||
|
set_buffer_defrag_done(tmp_bh);
|
||||||
brelse(tmp_bh);
|
brelse(tmp_bh);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
|
@ -26,9 +26,11 @@
|
|||||||
enum btrfs_bh_state_bits {
|
enum btrfs_bh_state_bits {
|
||||||
BH_Checked = BH_PrivateStart,
|
BH_Checked = BH_PrivateStart,
|
||||||
BH_Defrag,
|
BH_Defrag,
|
||||||
|
BH_DefragDone,
|
||||||
};
|
};
|
||||||
BUFFER_FNS(Checked, checked);
|
BUFFER_FNS(Checked, checked);
|
||||||
BUFFER_FNS(Defrag, defrag);
|
BUFFER_FNS(Defrag, defrag);
|
||||||
|
BUFFER_FNS(DefragDone, defrag_done);
|
||||||
|
|
||||||
static inline struct btrfs_node *btrfs_buffer_node(struct buffer_head *bh)
|
static inline struct btrfs_node *btrfs_buffer_node(struct buffer_head *bh)
|
||||||
{
|
{
|
||||||
|
@ -10,6 +10,12 @@
|
|||||||
#include <linux/blkdev.h>
|
#include <linux/blkdev.h>
|
||||||
#include "extent_map.h"
|
#include "extent_map.h"
|
||||||
|
|
||||||
|
/* temporary define until extent_map moves out of btrfs */
|
||||||
|
struct kmem_cache *btrfs_cache_create(const char *name, size_t size,
|
||||||
|
unsigned long extra_flags,
|
||||||
|
void (*ctor)(void *, struct kmem_cache *,
|
||||||
|
unsigned long));
|
||||||
|
|
||||||
static struct kmem_cache *extent_map_cache;
|
static struct kmem_cache *extent_map_cache;
|
||||||
static struct kmem_cache *extent_state_cache;
|
static struct kmem_cache *extent_state_cache;
|
||||||
|
|
||||||
@ -32,14 +38,12 @@ struct tree_entry {
|
|||||||
|
|
||||||
void __init extent_map_init(void)
|
void __init extent_map_init(void)
|
||||||
{
|
{
|
||||||
extent_map_cache = kmem_cache_create("extent_map",
|
extent_map_cache = btrfs_cache_create("extent_map",
|
||||||
sizeof(struct extent_map), 0,
|
sizeof(struct extent_map),
|
||||||
SLAB_RECLAIM_ACCOUNT |
|
|
||||||
SLAB_DESTROY_BY_RCU,
|
SLAB_DESTROY_BY_RCU,
|
||||||
NULL);
|
NULL);
|
||||||
extent_state_cache = kmem_cache_create("extent_state",
|
extent_state_cache = btrfs_cache_create("extent_state",
|
||||||
sizeof(struct extent_state), 0,
|
sizeof(struct extent_state),
|
||||||
SLAB_RECLAIM_ACCOUNT |
|
|
||||||
SLAB_DESTROY_BY_RCU,
|
SLAB_DESTROY_BY_RCU,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
@ -1904,6 +1904,70 @@ fail:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned long force_ra(struct address_space *mapping,
|
||||||
|
struct file_ra_state *ra, struct file *file,
|
||||||
|
pgoff_t offset, pgoff_t last_index)
|
||||||
|
{
|
||||||
|
pgoff_t req_size;
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
|
||||||
|
req_size = last_index - offset + 1;
|
||||||
|
offset = page_cache_readahead(mapping, ra, file, offset, req_size);
|
||||||
|
return offset;
|
||||||
|
#else
|
||||||
|
req_size = min(last_index - offset + 1, (pgoff_t)128);
|
||||||
|
page_cache_sync_readahead(mapping, ra, file, offset, req_size);
|
||||||
|
return offset + req_size;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int btrfs_defrag_file(struct file *file) {
|
||||||
|
struct inode *inode = file->f_path.dentry->d_inode;
|
||||||
|
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
|
||||||
|
struct page *page;
|
||||||
|
unsigned long last_index;
|
||||||
|
unsigned long ra_index = 0;
|
||||||
|
u64 page_start;
|
||||||
|
u64 page_end;
|
||||||
|
unsigned long i;
|
||||||
|
|
||||||
|
mutex_lock(&inode->i_mutex);
|
||||||
|
last_index = inode->i_size >> PAGE_CACHE_SHIFT;
|
||||||
|
for (i = 0; i <= last_index; i++) {
|
||||||
|
if (i == ra_index) {
|
||||||
|
ra_index = force_ra(inode->i_mapping, &file->f_ra,
|
||||||
|
file, ra_index, last_index);
|
||||||
|
}
|
||||||
|
page = grab_cache_page(inode->i_mapping, i);
|
||||||
|
if (!page)
|
||||||
|
goto out_unlock;
|
||||||
|
if (!PageUptodate(page)) {
|
||||||
|
btrfs_readpage(NULL, page);
|
||||||
|
lock_page(page);
|
||||||
|
if (!PageUptodate(page)) {
|
||||||
|
unlock_page(page);
|
||||||
|
page_cache_release(page);
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
page_start = page->index << PAGE_CACHE_SHIFT;
|
||||||
|
page_end = page_start + PAGE_CACHE_SIZE - 1;
|
||||||
|
|
||||||
|
lock_extent(em_tree, page_start, page_end, GFP_NOFS);
|
||||||
|
set_extent_delalloc(em_tree, page_start,
|
||||||
|
page_end, GFP_NOFS);
|
||||||
|
unlock_extent(em_tree, page_start, page_end, GFP_NOFS);
|
||||||
|
set_page_dirty(page);
|
||||||
|
unlock_page(page);
|
||||||
|
page_cache_release(page);
|
||||||
|
balance_dirty_pages_ratelimited_nr(inode->i_mapping, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
out_unlock:
|
||||||
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int
|
int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int
|
||||||
cmd, unsigned long arg)
|
cmd, unsigned long arg)
|
||||||
{
|
{
|
||||||
@ -1948,10 +2012,14 @@ int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case BTRFS_IOC_DEFRAG:
|
case BTRFS_IOC_DEFRAG:
|
||||||
mutex_lock(&root->fs_info->fs_mutex);
|
if (S_ISDIR(inode->i_mode)) {
|
||||||
btrfs_defrag_root(root, 0);
|
mutex_lock(&root->fs_info->fs_mutex);
|
||||||
btrfs_defrag_root(root->fs_info->extent_root, 0);
|
btrfs_defrag_root(root, 0);
|
||||||
mutex_unlock(&root->fs_info->fs_mutex);
|
btrfs_defrag_root(root->fs_info->extent_root, 0);
|
||||||
|
mutex_unlock(&root->fs_info->fs_mutex);
|
||||||
|
} else if (S_ISREG(inode->i_mode)) {
|
||||||
|
btrfs_defrag_file(filp);
|
||||||
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -2018,7 +2086,7 @@ void btrfs_destroy_cachep(void)
|
|||||||
kmem_cache_destroy(btrfs_path_cachep);
|
kmem_cache_destroy(btrfs_path_cachep);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct kmem_cache *cache_create(const char *name, size_t size,
|
struct kmem_cache *btrfs_cache_create(const char *name, size_t size,
|
||||||
unsigned long extra_flags,
|
unsigned long extra_flags,
|
||||||
void (*ctor)(void *, struct kmem_cache *,
|
void (*ctor)(void *, struct kmem_cache *,
|
||||||
unsigned long))
|
unsigned long))
|
||||||
@ -2033,27 +2101,28 @@ static struct kmem_cache *cache_create(const char *name, size_t size,
|
|||||||
|
|
||||||
int btrfs_init_cachep(void)
|
int btrfs_init_cachep(void)
|
||||||
{
|
{
|
||||||
btrfs_inode_cachep = cache_create("btrfs_inode_cache",
|
btrfs_inode_cachep = btrfs_cache_create("btrfs_inode_cache",
|
||||||
sizeof(struct btrfs_inode),
|
sizeof(struct btrfs_inode),
|
||||||
0, init_once);
|
0, init_once);
|
||||||
if (!btrfs_inode_cachep)
|
if (!btrfs_inode_cachep)
|
||||||
goto fail;
|
goto fail;
|
||||||
btrfs_trans_handle_cachep = cache_create("btrfs_trans_handle_cache",
|
btrfs_trans_handle_cachep =
|
||||||
sizeof(struct btrfs_trans_handle),
|
btrfs_cache_create("btrfs_trans_handle_cache",
|
||||||
0, NULL);
|
sizeof(struct btrfs_trans_handle),
|
||||||
|
0, NULL);
|
||||||
if (!btrfs_trans_handle_cachep)
|
if (!btrfs_trans_handle_cachep)
|
||||||
goto fail;
|
goto fail;
|
||||||
btrfs_transaction_cachep = cache_create("btrfs_transaction_cache",
|
btrfs_transaction_cachep = btrfs_cache_create("btrfs_transaction_cache",
|
||||||
sizeof(struct btrfs_transaction),
|
sizeof(struct btrfs_transaction),
|
||||||
0, NULL);
|
0, NULL);
|
||||||
if (!btrfs_transaction_cachep)
|
if (!btrfs_transaction_cachep)
|
||||||
goto fail;
|
goto fail;
|
||||||
btrfs_path_cachep = cache_create("btrfs_path_cache",
|
btrfs_path_cachep = btrfs_cache_create("btrfs_path_cache",
|
||||||
sizeof(struct btrfs_transaction),
|
sizeof(struct btrfs_transaction),
|
||||||
0, NULL);
|
0, NULL);
|
||||||
if (!btrfs_path_cachep)
|
if (!btrfs_path_cachep)
|
||||||
goto fail;
|
goto fail;
|
||||||
btrfs_bit_radix_cachep = cache_create("btrfs_radix", 256,
|
btrfs_bit_radix_cachep = btrfs_cache_create("btrfs_radix", 256,
|
||||||
SLAB_DESTROY_BY_RCU, NULL);
|
SLAB_DESTROY_BY_RCU, NULL);
|
||||||
if (!btrfs_bit_radix_cachep)
|
if (!btrfs_bit_radix_cachep)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -113,6 +113,8 @@ static int defrag_walk_down(struct btrfs_trans_handle *trans,
|
|||||||
}
|
}
|
||||||
WARN_ON(*level < 0);
|
WARN_ON(*level < 0);
|
||||||
WARN_ON(*level >= BTRFS_MAX_LEVEL);
|
WARN_ON(*level >= BTRFS_MAX_LEVEL);
|
||||||
|
clear_buffer_defrag(path->nodes[*level]);
|
||||||
|
clear_buffer_defrag_done(path->nodes[*level]);
|
||||||
btrfs_block_release(root, path->nodes[*level]);
|
btrfs_block_release(root, path->nodes[*level]);
|
||||||
path->nodes[*level] = NULL;
|
path->nodes[*level] = NULL;
|
||||||
*level += 1;
|
*level += 1;
|
||||||
@ -143,6 +145,7 @@ static int defrag_walk_up(struct btrfs_trans_handle *trans,
|
|||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
clear_buffer_defrag(path->nodes[*level]);
|
clear_buffer_defrag(path->nodes[*level]);
|
||||||
|
clear_buffer_defrag_done(path->nodes[*level]);
|
||||||
btrfs_block_release(root, path->nodes[*level]);
|
btrfs_block_release(root, path->nodes[*level]);
|
||||||
path->nodes[*level] = NULL;
|
path->nodes[*level] = NULL;
|
||||||
*level = i + 1;
|
*level = i + 1;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user