Commit e9d0b13b authored by Chris Mason's avatar Chris Mason Committed by David Woodhouse

Btrfs: Btree defrag on the extent-mapping tree as well

Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 409eb95d
...@@ -99,7 +99,6 @@ static int __btrfs_cow_block(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -99,7 +99,6 @@ static int __btrfs_cow_block(struct btrfs_trans_handle *trans, struct btrfs_root
if (ret) if (ret)
return ret; return ret;
} else { } else {
WARN_ON(!root->ref_cows);
clean_tree_block(trans, root, buf); clean_tree_block(trans, root, buf);
} }
...@@ -162,13 +161,14 @@ static int close_blocks(u64 blocknr, u64 other) ...@@ -162,13 +161,14 @@ static int close_blocks(u64 blocknr, u64 other)
int btrfs_realloc_node(struct btrfs_trans_handle *trans, int btrfs_realloc_node(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct buffer_head *parent, struct btrfs_root *root, struct buffer_head *parent,
int cache_only) int cache_only, u64 *last_ret)
{ {
struct btrfs_node *parent_node; struct btrfs_node *parent_node;
struct buffer_head *cur_bh; struct buffer_head *cur_bh;
struct buffer_head *tmp_bh; struct buffer_head *tmp_bh;
u64 blocknr; u64 blocknr;
u64 search_start = 0; u64 search_start = *last_ret;
u64 last_block = 0;
u64 other; u64 other;
u32 parent_nritems; u32 parent_nritems;
int start_slot; int start_slot;
...@@ -198,6 +198,8 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans, ...@@ -198,6 +198,8 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
for (i = start_slot; i < end_slot; i++) { for (i = start_slot; i < end_slot; i++) {
int close = 1; int close = 1;
blocknr = btrfs_node_blockptr(parent_node, i); blocknr = btrfs_node_blockptr(parent_node, i);
if (last_block == 0)
last_block = blocknr;
if (i > 0) { if (i > 0) {
other = btrfs_node_blockptr(parent_node, i - 1); other = btrfs_node_blockptr(parent_node, i - 1);
close = close_blocks(blocknr, other); close = close_blocks(blocknr, other);
...@@ -206,8 +208,10 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans, ...@@ -206,8 +208,10 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
other = btrfs_node_blockptr(parent_node, i + 1); other = btrfs_node_blockptr(parent_node, i + 1);
close = close_blocks(blocknr, other); close = close_blocks(blocknr, other);
} }
if (close) if (close) {
last_block = blocknr;
continue; continue;
}
cur_bh = btrfs_find_tree_block(root, blocknr); cur_bh = btrfs_find_tree_block(root, blocknr);
if (!cur_bh || !buffer_uptodate(cur_bh) || if (!cur_bh || !buffer_uptodate(cur_bh) ||
...@@ -219,9 +223,9 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans, ...@@ -219,9 +223,9 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
brelse(cur_bh); brelse(cur_bh);
cur_bh = read_tree_block(root, blocknr); cur_bh = read_tree_block(root, blocknr);
} }
if (search_start == 0) { if (search_start == 0)
search_start = bh_blocknr(cur_bh) & ~((u64)65535); search_start = last_block & ~((u64)65535);
}
err = __btrfs_cow_block(trans, root, cur_bh, parent, i, err = __btrfs_cow_block(trans, root, cur_bh, parent, i,
&tmp_bh, search_start, &tmp_bh, search_start,
min(8, end_slot - i)); min(8, end_slot - i));
......
...@@ -1019,6 +1019,8 @@ static inline void btrfs_memmove(struct btrfs_root *root, ...@@ -1019,6 +1019,8 @@ static inline void btrfs_memmove(struct btrfs_root *root,
btrfs_item_offset((leaf)->items + (slot)))) btrfs_item_offset((leaf)->items + (slot))))
/* extent-tree.c */ /* extent-tree.c */
int btrfs_extent_post_op(struct btrfs_trans_handle *trans,
struct btrfs_root *root);
int btrfs_copy_pinned(struct btrfs_root *root, struct radix_tree_root *copy); int btrfs_copy_pinned(struct btrfs_root *root, struct radix_tree_root *copy);
struct btrfs_block_group_cache *btrfs_lookup_block_group(struct struct btrfs_block_group_cache *btrfs_lookup_block_group(struct
btrfs_fs_info *info, btrfs_fs_info *info,
...@@ -1066,7 +1068,7 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -1066,7 +1068,7 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
ins_len, int cow); ins_len, int cow);
int btrfs_realloc_node(struct btrfs_trans_handle *trans, int btrfs_realloc_node(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct buffer_head *parent, struct btrfs_root *root, struct buffer_head *parent,
int cache_only); int cache_only, u64 *last_ret);
void btrfs_release_path(struct btrfs_root *root, struct btrfs_path *p); void btrfs_release_path(struct btrfs_root *root, struct btrfs_path *p);
struct btrfs_path *btrfs_alloc_path(void); struct btrfs_path *btrfs_alloc_path(void);
void btrfs_free_path(struct btrfs_path *p); void btrfs_free_path(struct btrfs_path *p);
......
...@@ -396,6 +396,14 @@ int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, ...@@ -396,6 +396,14 @@ int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
return 0; return 0;
} }
int btrfs_extent_post_op(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
finish_current_insert(trans, root->fs_info->extent_root);
del_pending_extents(trans, root->fs_info->extent_root);
return 0;
}
static int lookup_extent_ref(struct btrfs_trans_handle *trans, static int lookup_extent_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 blocknr, struct btrfs_root *root, u64 blocknr,
u64 num_blocks, u32 *refs) u64 num_blocks, u32 *refs)
...@@ -1374,7 +1382,25 @@ static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -1374,7 +1382,25 @@ static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root
BUG_ON(ret); BUG_ON(ret);
continue; continue;
} }
next = read_tree_block(root, blocknr); next = btrfs_find_tree_block(root, blocknr);
if (!next || !buffer_uptodate(next)) {
brelse(next);
mutex_unlock(&root->fs_info->fs_mutex);
next = read_tree_block(root, blocknr);
mutex_lock(&root->fs_info->fs_mutex);
/* we dropped the lock, check one more time */
ret = lookup_extent_ref(trans, root, blocknr, 1, &refs);
BUG_ON(ret);
if (refs != 1) {
path->slots[*level]++;
brelse(next);
ret = btrfs_free_extent(trans, root,
blocknr, 1, 1);
BUG_ON(ret);
continue;
}
}
WARN_ON(*level <= 0); WARN_ON(*level <= 0);
if (path->nodes[*level-1]) if (path->nodes[*level-1])
btrfs_block_release(root, path->nodes[*level-1]); btrfs_block_release(root, path->nodes[*level-1]);
......
...@@ -2159,9 +2159,7 @@ int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int ...@@ -2159,9 +2159,7 @@ int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int
{ {
struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_ioctl_vol_args vol_args; struct btrfs_ioctl_vol_args vol_args;
struct btrfs_trans_handle *trans;
int ret = 0; int ret = 0;
int err;
struct btrfs_dir_item *di; struct btrfs_dir_item *di;
int namelen; int namelen;
struct btrfs_path *path; struct btrfs_path *path;
...@@ -2201,25 +2199,8 @@ int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int ...@@ -2201,25 +2199,8 @@ int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int
case BTRFS_IOC_DEFRAG: case BTRFS_IOC_DEFRAG:
mutex_lock(&root->fs_info->fs_mutex); mutex_lock(&root->fs_info->fs_mutex);
trans = btrfs_start_transaction(root, 1); btrfs_defrag_root(root, 0);
memset(&root->defrag_progress, 0, btrfs_defrag_root(root->fs_info->extent_root, 0);
sizeof(root->defrag_progress));
while (1) {
root->defrag_running = 1;
err = btrfs_defrag_leaves(trans, root, 0);
btrfs_end_transaction(trans, root);
mutex_unlock(&root->fs_info->fs_mutex);
btrfs_btree_balance_dirty(root);
mutex_lock(&root->fs_info->fs_mutex);
trans = btrfs_start_transaction(root, 1);
if (err != -EAGAIN)
break;
}
root->defrag_running = 0;
btrfs_end_transaction(trans, root);
mutex_unlock(&root->fs_info->fs_mutex); mutex_unlock(&root->fs_info->fs_mutex);
ret = 0; ret = 0;
break; break;
......
...@@ -121,7 +121,9 @@ static int btrfs_sync_fs(struct super_block *sb, int wait) ...@@ -121,7 +121,9 @@ static int btrfs_sync_fs(struct super_block *sb, int wait)
filemap_flush(root->fs_info->btree_inode->i_mapping); filemap_flush(root->fs_info->btree_inode->i_mapping);
return 0; return 0;
} }
btrfs_clean_old_snapshots(root);
mutex_lock(&root->fs_info->fs_mutex); mutex_lock(&root->fs_info->fs_mutex);
btrfs_defrag_dirty_roots(root->fs_info);
trans = btrfs_start_transaction(root, 1); trans = btrfs_start_transaction(root, 1);
ret = btrfs_commit_transaction(trans, root); ret = btrfs_commit_transaction(trans, root);
sb->s_dirt = 0; sb->s_dirt = 0;
......
...@@ -317,18 +317,47 @@ static int add_dirty_roots(struct btrfs_trans_handle *trans, ...@@ -317,18 +317,47 @@ static int add_dirty_roots(struct btrfs_trans_handle *trans,
return err; return err;
} }
int btrfs_defrag_root(struct btrfs_root *root, int cacheonly)
{
struct btrfs_fs_info *info = root->fs_info;
int ret;
struct btrfs_trans_handle *trans;
if (root->defrag_running)
return 0;
trans = btrfs_start_transaction(root, 1);
while (1) {
root->defrag_running = 1;
ret = btrfs_defrag_leaves(trans, root, cacheonly);
btrfs_end_transaction(trans, root);
mutex_unlock(&info->fs_mutex);
btrfs_btree_balance_dirty(root);
cond_resched();
mutex_lock(&info->fs_mutex);
trans = btrfs_start_transaction(root, 1);
if (ret != -EAGAIN)
break;
}
root->defrag_running = 0;
radix_tree_tag_clear(&info->fs_roots_radix,
(unsigned long)root->root_key.objectid,
BTRFS_ROOT_DEFRAG_TAG);
btrfs_end_transaction(trans, root);
return 0;
}
int btrfs_defrag_dirty_roots(struct btrfs_fs_info *info) int btrfs_defrag_dirty_roots(struct btrfs_fs_info *info)
{ {
struct btrfs_root *gang[1]; struct btrfs_root *gang[1];
struct btrfs_root *root; struct btrfs_root *root;
struct btrfs_root *tree_root = info->tree_root;
struct btrfs_trans_handle *trans;
int i; int i;
int ret; int ret;
int err = 0; int err = 0;
u64 last = 0; u64 last = 0;
trans = btrfs_start_transaction(tree_root, 1);
while(1) { while(1) {
ret = radix_tree_gang_lookup_tag(&info->fs_roots_radix, ret = radix_tree_gang_lookup_tag(&info->fs_roots_radix,
(void **)gang, last, (void **)gang, last,
...@@ -339,37 +368,10 @@ int btrfs_defrag_dirty_roots(struct btrfs_fs_info *info) ...@@ -339,37 +368,10 @@ int btrfs_defrag_dirty_roots(struct btrfs_fs_info *info)
for (i = 0; i < ret; i++) { for (i = 0; i < ret; i++) {
root = gang[i]; root = gang[i];
last = root->root_key.objectid + 1; last = root->root_key.objectid + 1;
radix_tree_tag_clear(&info->fs_roots_radix, btrfs_defrag_root(root, 1);
(unsigned long)root->root_key.objectid,
BTRFS_ROOT_DEFRAG_TAG);
if (root->defrag_running)
continue;
while (1) {
mutex_lock(&root->fs_info->trans_mutex);
record_root_in_trans(root);
mutex_unlock(&root->fs_info->trans_mutex);
root->defrag_running = 1;
err = btrfs_defrag_leaves(trans, root, 1);
btrfs_end_transaction(trans, tree_root);
mutex_unlock(&info->fs_mutex);
btrfs_btree_balance_dirty(root);
cond_resched();
mutex_lock(&info->fs_mutex);
trans = btrfs_start_transaction(tree_root, 1);
if (err != -EAGAIN)
break;
}
root->defrag_running = 0;
radix_tree_tag_clear(&info->fs_roots_radix,
(unsigned long)root->root_key.objectid,
BTRFS_ROOT_DEFRAG_TAG);
} }
} }
btrfs_end_transaction(trans, tree_root); btrfs_defrag_root(info->extent_root, 1);
return err; return err;
} }
...@@ -527,6 +529,20 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, ...@@ -527,6 +529,20 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
return ret; return ret;
} }
int btrfs_clean_old_snapshots(struct btrfs_root *root)
{
struct list_head dirty_roots;
INIT_LIST_HEAD(&dirty_roots);
mutex_lock(&root->fs_info->trans_mutex);
list_splice_init(&root->fs_info->dead_roots, &dirty_roots);
mutex_unlock(&root->fs_info->trans_mutex);
if (!list_empty(&dirty_roots)) {
drop_dirty_roots(root, &dirty_roots);
}
return 0;
}
void btrfs_transaction_cleaner(struct work_struct *work) void btrfs_transaction_cleaner(struct work_struct *work)
{ {
struct btrfs_fs_info *fs_info = container_of(work, struct btrfs_fs_info *fs_info = container_of(work,
...@@ -536,12 +552,10 @@ void btrfs_transaction_cleaner(struct work_struct *work) ...@@ -536,12 +552,10 @@ void btrfs_transaction_cleaner(struct work_struct *work)
struct btrfs_root *root = fs_info->tree_root; struct btrfs_root *root = fs_info->tree_root;
struct btrfs_transaction *cur; struct btrfs_transaction *cur;
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans;
struct list_head dirty_roots;
unsigned long now; unsigned long now;
unsigned long delay = HZ * 30; unsigned long delay = HZ * 30;
int ret; int ret;
INIT_LIST_HEAD(&dirty_roots);
mutex_lock(&root->fs_info->fs_mutex); mutex_lock(&root->fs_info->fs_mutex);
mutex_lock(&root->fs_info->trans_mutex); mutex_lock(&root->fs_info->trans_mutex);
cur = root->fs_info->running_transaction; cur = root->fs_info->running_transaction;
...@@ -561,14 +575,7 @@ void btrfs_transaction_cleaner(struct work_struct *work) ...@@ -561,14 +575,7 @@ void btrfs_transaction_cleaner(struct work_struct *work)
ret = btrfs_commit_transaction(trans, root); ret = btrfs_commit_transaction(trans, root);
out: out:
mutex_unlock(&root->fs_info->fs_mutex); mutex_unlock(&root->fs_info->fs_mutex);
btrfs_clean_old_snapshots(root);
mutex_lock(&root->fs_info->trans_mutex);
list_splice_init(&root->fs_info->dead_roots, &dirty_roots);
mutex_unlock(&root->fs_info->trans_mutex);
if (!list_empty(&dirty_roots)) {
drop_dirty_roots(root, &dirty_roots);
}
btrfs_transaction_queue_work(root, delay); btrfs_transaction_queue_work(root, delay);
} }
......
...@@ -73,5 +73,7 @@ void btrfs_init_transaction_sys(void); ...@@ -73,5 +73,7 @@ void btrfs_init_transaction_sys(void);
void btrfs_exit_transaction_sys(void); void btrfs_exit_transaction_sys(void);
int btrfs_add_dead_root(struct btrfs_root *root, struct list_head *dead_list); int btrfs_add_dead_root(struct btrfs_root *root, struct list_head *dead_list);
int btrfs_defrag_dirty_roots(struct btrfs_fs_info *info); int btrfs_defrag_dirty_roots(struct btrfs_fs_info *info);
int btrfs_defrag_root(struct btrfs_root *root, int cacheonly);
int btrfs_clean_old_snapshots(struct btrfs_root *root);
#endif #endif
...@@ -42,16 +42,20 @@ static void reada_defrag(struct btrfs_root *root, ...@@ -42,16 +42,20 @@ static void reada_defrag(struct btrfs_root *root,
static int defrag_walk_down(struct btrfs_trans_handle *trans, static int defrag_walk_down(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct btrfs_path *path, int *level, struct btrfs_path *path, int *level,
int cache_only) int cache_only, u64 *last_ret)
{ {
struct buffer_head *next; struct buffer_head *next;
struct buffer_head *cur; struct buffer_head *cur;
u64 blocknr; u64 blocknr;
int ret = 0; int ret = 0;
int is_extent = 0;
WARN_ON(*level < 0); WARN_ON(*level < 0);
WARN_ON(*level >= BTRFS_MAX_LEVEL); WARN_ON(*level >= BTRFS_MAX_LEVEL);
if (root->fs_info->extent_root == root)
is_extent = 1;
while(*level > 0) { while(*level > 0) {
WARN_ON(*level < 0); WARN_ON(*level < 0);
WARN_ON(*level >= BTRFS_MAX_LEVEL); WARN_ON(*level >= BTRFS_MAX_LEVEL);
...@@ -70,7 +74,10 @@ static int defrag_walk_down(struct btrfs_trans_handle *trans, ...@@ -70,7 +74,10 @@ static int defrag_walk_down(struct btrfs_trans_handle *trans,
if (*level == 1) { if (*level == 1) {
ret = btrfs_realloc_node(trans, root, ret = btrfs_realloc_node(trans, root,
path->nodes[*level], path->nodes[*level],
cache_only); cache_only, last_ret);
if (is_extent)
btrfs_extent_post_op(trans, root);
break; break;
} }
blocknr = btrfs_node_blockptr(btrfs_buffer_node(cur), blocknr = btrfs_node_blockptr(btrfs_buffer_node(cur),
...@@ -90,8 +97,13 @@ static int defrag_walk_down(struct btrfs_trans_handle *trans, ...@@ -90,8 +97,13 @@ static int defrag_walk_down(struct btrfs_trans_handle *trans,
ret = btrfs_cow_block(trans, root, next, path->nodes[*level], ret = btrfs_cow_block(trans, root, next, path->nodes[*level],
path->slots[*level], &next); path->slots[*level], &next);
BUG_ON(ret); BUG_ON(ret);
ret = btrfs_realloc_node(trans, root, next, cache_only); ret = btrfs_realloc_node(trans, root, next, cache_only,
last_ret);
BUG_ON(ret); BUG_ON(ret);
if (is_extent)
btrfs_extent_post_op(trans, root);
WARN_ON(*level <= 0); WARN_ON(*level <= 0);
if (path->nodes[*level-1]) if (path->nodes[*level-1])
btrfs_block_release(root, path->nodes[*level-1]); btrfs_block_release(root, path->nodes[*level-1]);
...@@ -148,10 +160,14 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, ...@@ -148,10 +160,14 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
int level; int level;
int orig_level; int orig_level;
int i; int i;
int is_extent = 0;
u64 last_ret = 0;
if (root->fs_info->extent_root == root)
is_extent = 1;
if (root->ref_cows == 0) { if (root->ref_cows == 0 && !is_extent)
goto out; goto out;
}
path = btrfs_alloc_path(); path = btrfs_alloc_path();
if (!path) if (!path)
return -ENOMEM; return -ENOMEM;
...@@ -165,16 +181,21 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, ...@@ -165,16 +181,21 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
get_bh(root->node); get_bh(root->node);
ret = btrfs_cow_block(trans, root, root->node, NULL, 0, &tmp); ret = btrfs_cow_block(trans, root, root->node, NULL, 0, &tmp);
BUG_ON(ret); BUG_ON(ret);
ret = btrfs_realloc_node(trans, root, root->node, cache_only); ret = btrfs_realloc_node(trans, root, root->node, cache_only,
&last_ret);
BUG_ON(ret); BUG_ON(ret);
path->nodes[level] = root->node; path->nodes[level] = root->node;
path->slots[level] = 0; path->slots[level] = 0;
if (is_extent)
btrfs_extent_post_op(trans, root);
} else { } else {
level = root->defrag_level; level = root->defrag_level;
path->lowest_level = level; path->lowest_level = level;
wret = btrfs_search_slot(trans, root, &root->defrag_progress, wret = btrfs_search_slot(trans, root, &root->defrag_progress,
path, 0, 1); path, 0, 1);
if (is_extent)
btrfs_extent_post_op(trans, root);
if (wret < 0) { if (wret < 0) {
ret = wret; ret = wret;
goto out; goto out;
...@@ -188,7 +209,8 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, ...@@ -188,7 +209,8 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
} }
while(1) { while(1) {
wret = defrag_walk_down(trans, root, path, &level, cache_only); wret = defrag_walk_down(trans, root, path, &level, cache_only,
&last_ret);
if (wret > 0) if (wret > 0)
break; break;
if (wret < 0) if (wret < 0)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment