Commit a6b6e75e authored by Chris Mason's avatar Chris Mason
Browse files

Btrfs: Defrag only leaves, and only when the parent node has a single objectid



This allows us to defrag huge directories, but skip the expensive defrag
case in more common usage, where it does not help as much.

Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent ff79f819
Loading
Loading
Loading
Loading
+16 −6
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
 * Boston, MA 021110-1307, USA.
 */

#include <linux/sched.h>
#include "ctree.h"
#include "disk-io.h"
#include "transaction.h"
@@ -190,7 +191,8 @@ static int should_defrag_leaf(struct extent_buffer *leaf)

int btrfs_realloc_node(struct btrfs_trans_handle *trans,
		       struct btrfs_root *root, struct extent_buffer *parent,
		       int cache_only, u64 *last_ret)
		       int start_slot, int cache_only, u64 *last_ret,
		       struct btrfs_key *progress)
{
	struct extent_buffer *cur;
	struct extent_buffer *tmp;
@@ -199,7 +201,6 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
	u64 last_block = 0;
	u64 other;
	u32 parent_nritems;
	int start_slot;
	int end_slot;
	int i;
	int err = 0;
@@ -221,15 +222,24 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,

	parent_nritems = btrfs_header_nritems(parent);
	blocksize = btrfs_level_size(root, parent_level - 1);

	start_slot = 0;
	end_slot = parent_nritems;

	if (parent_nritems == 1)
		return 0;

	if (root != root->fs_info->extent_root) {
		struct btrfs_key first_key;
		struct btrfs_key last_key;

		btrfs_node_key_to_cpu(parent, &first_key, 0);
		btrfs_node_key_to_cpu(parent, &last_key, parent_nritems - 1);
		if (first_key.objectid != last_key.objectid)
			return 0;
	}

	for (i = start_slot; i < end_slot; i++) {
		int close = 1;

		blocknr = btrfs_node_blockptr(parent, i);
		if (last_block == 0)
			last_block = blocknr;
@@ -898,7 +908,7 @@ static void reada_for_search(struct btrfs_root *root, struct btrfs_path *path,
	u32 blocksize;
	u32 nscan = 0;

	if (level == 0)
	if (level != 1)
		return;

	if (!path->nodes[level])
@@ -2370,7 +2380,7 @@ int btrfs_del_item(struct btrfs_trans_handle *trans, struct btrfs_root *root,
		}

		/* delete the leaf if it is mostly empty */
		if (used < BTRFS_LEAF_DATA_SIZE(root) / 3) {
		if (0 && used < BTRFS_LEAF_DATA_SIZE(root) / 3) {
			/* push_leaf_left fixes the path.
			 * make sure the path still points to our leaf
			 * for possible call to del_ptr below
+2 −1
Original line number Diff line number Diff line
@@ -913,7 +913,8 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
		      ins_len, int cow);
int btrfs_realloc_node(struct btrfs_trans_handle *trans,
		       struct btrfs_root *root, struct extent_buffer *parent,
		       int cache_only, u64 *last_ret);
		       int start_slot, int cache_only, u64 *last_ret,
		       struct btrfs_key *progress);
void btrfs_release_path(struct btrfs_root *root, struct btrfs_path *p);
struct btrfs_path *btrfs_alloc_path(void);
void btrfs_free_path(struct btrfs_path *p);
+0 −2
Original line number Diff line number Diff line
@@ -353,7 +353,6 @@ int btrfs_defrag_root(struct btrfs_root *root, int cacheonly)

	if (root->defrag_running)
		return 0;

	trans = btrfs_start_transaction(root, 1);
	while (1) {
		root->defrag_running = 1;
@@ -361,7 +360,6 @@ int btrfs_defrag_root(struct btrfs_root *root, int cacheonly)
		nr = trans->blocks_used;
		btrfs_end_transaction(trans, root);
		mutex_unlock(&info->fs_mutex);

		btrfs_btree_balance_dirty(info->tree_root, nr);
		cond_resched();

+5 −31
Original line number Diff line number Diff line
@@ -76,7 +76,9 @@ static int defrag_walk_down(struct btrfs_trans_handle *trans,
		if (*level == 1) {
			ret = btrfs_realloc_node(trans, root,
						 path->nodes[*level],
						 cache_only, last_ret);
						 path->slots[*level],
						 cache_only, last_ret,
						 &root->defrag_progress);
			if (is_extent)
				btrfs_extent_post_op(trans, root);

@@ -100,10 +102,6 @@ static int defrag_walk_down(struct btrfs_trans_handle *trans,
		ret = btrfs_cow_block(trans, root, next, path->nodes[*level],
				      path->slots[*level], &next);
		BUG_ON(ret);
		ret = btrfs_realloc_node(trans, root, next, cache_only,
					 last_ret);
		BUG_ON(ret);

		if (is_extent)
			btrfs_extent_post_op(trans, root);

@@ -122,8 +120,8 @@ static int defrag_walk_down(struct btrfs_trans_handle *trans,
	free_extent_buffer(path->nodes[*level]);
	path->nodes[*level] = NULL;
	*level += 1;
	WARN_ON(ret);
	return 0;
	WARN_ON(ret && ret != -EAGAIN);
	return ret;
}

static int defrag_walk_up(struct btrfs_trans_handle *trans,
@@ -147,27 +145,6 @@ static int defrag_walk_up(struct btrfs_trans_handle *trans,
			root->defrag_level = i;
			return 0;
		} else {
			if (*level > 1 && path->nodes[*level] != root->node &&
			    btrfs_buffer_defrag(path->nodes[*level])) {
				struct extent_buffer *next;
				u64 last;
				int ret;
				ret = btrfs_cow_block(trans, root,
						      path->nodes[*level],
						      path->nodes[*level + 1],
						      path->slots[*level + 1],
						      &next);
				BUG_ON(ret);
				path->nodes[*level] = next;
				last = next->start;
				ret = btrfs_realloc_node(trans, root, next,
							 cache_only, &last);
				BUG_ON(ret);

				if (root == root->fs_info->extent_root)
					btrfs_extent_post_op(trans, root);
			}

			btrfs_clear_buffer_defrag(path->nodes[*level]);
			free_extent_buffer(path->nodes[*level]);
			path->nodes[*level] = NULL;
@@ -211,9 +188,6 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
		extent_buffer_get(root->node);
		ret = btrfs_cow_block(trans, root, root->node, NULL, 0, &tmp);
		BUG_ON(ret);
		ret = btrfs_realloc_node(trans, root, root->node, cache_only,
					 &last_ret);
		BUG_ON(ret);
		path->nodes[level] = root->node;
		path->slots[level] = 0;
		if (is_extent)