Commit 3fd0a558 authored by Yan, Zheng's avatar Yan, Zheng Committed by Chris Mason

Btrfs: Metadata ENOSPC handling for balance

This patch adds metadata ENOSPC handling for the balance code.
It is consisted by following major changes:

1. Avoid COW tree leave in the phrase of merging tree.

2. Handle interaction with snapshot creation.

3. make the backref cache can live across transactions.
Signed-off-by: default avatarYan Zheng <zheng.yan@oracle.com>
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent efa56464
...@@ -447,6 +447,9 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, ...@@ -447,6 +447,9 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
update_ref_for_cow(trans, root, buf, cow, &last_ref); update_ref_for_cow(trans, root, buf, cow, &last_ref);
if (root->ref_cows)
btrfs_reloc_cow_block(trans, root, buf, cow);
if (buf == root->node) { if (buf == root->node) {
WARN_ON(parent && parent != buf); WARN_ON(parent && parent != buf);
if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID || if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID ||
......
...@@ -2205,7 +2205,8 @@ static inline int btrfs_insert_empty_item(struct btrfs_trans_handle *trans, ...@@ -2205,7 +2205,8 @@ static inline int btrfs_insert_empty_item(struct btrfs_trans_handle *trans,
int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path); int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path);
int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path); int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path);
int btrfs_leaf_free_space(struct btrfs_root *root, struct extent_buffer *leaf); int btrfs_leaf_free_space(struct btrfs_root *root, struct extent_buffer *leaf);
int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref); int btrfs_drop_snapshot(struct btrfs_root *root,
struct btrfs_block_rsv *block_rsv, int update_ref);
int btrfs_drop_subtree(struct btrfs_trans_handle *trans, int btrfs_drop_subtree(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct extent_buffer *node, struct extent_buffer *node,
...@@ -2479,4 +2480,12 @@ int btrfs_update_reloc_root(struct btrfs_trans_handle *trans, ...@@ -2479,4 +2480,12 @@ int btrfs_update_reloc_root(struct btrfs_trans_handle *trans,
struct btrfs_root *root); struct btrfs_root *root);
int btrfs_recover_relocation(struct btrfs_root *root); int btrfs_recover_relocation(struct btrfs_root *root);
int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len); int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len);
void btrfs_reloc_cow_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct extent_buffer *buf,
struct extent_buffer *cow);
void btrfs_reloc_pre_snapshot(struct btrfs_trans_handle *trans,
struct btrfs_pending_snapshot *pending,
u64 *bytes_to_reserve);
void btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans,
struct btrfs_pending_snapshot *pending);
#endif #endif
...@@ -5875,7 +5875,8 @@ static noinline int walk_up_tree(struct btrfs_trans_handle *trans, ...@@ -5875,7 +5875,8 @@ static noinline int walk_up_tree(struct btrfs_trans_handle *trans,
* also make sure backrefs for the shared block and all lower level * also make sure backrefs for the shared block and all lower level
* blocks are properly updated. * blocks are properly updated.
*/ */
int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref) int btrfs_drop_snapshot(struct btrfs_root *root,
struct btrfs_block_rsv *block_rsv, int update_ref)
{ {
struct btrfs_path *path; struct btrfs_path *path;
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans;
...@@ -5894,6 +5895,8 @@ int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref) ...@@ -5894,6 +5895,8 @@ int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref)
BUG_ON(!wc); BUG_ON(!wc);
trans = btrfs_start_transaction(tree_root, 0); trans = btrfs_start_transaction(tree_root, 0);
if (block_rsv)
trans->block_rsv = block_rsv;
if (btrfs_disk_key_objectid(&root_item->drop_progress) == 0) { if (btrfs_disk_key_objectid(&root_item->drop_progress) == 0) {
level = btrfs_header_level(root->node); level = btrfs_header_level(root->node);
...@@ -5981,24 +5984,16 @@ int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref) ...@@ -5981,24 +5984,16 @@ int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref)
} }
BUG_ON(wc->level == 0); BUG_ON(wc->level == 0);
if (trans->transaction->in_commit || if (btrfs_should_end_transaction(trans, tree_root)) {
trans->transaction->delayed_refs.flushing) {
ret = btrfs_update_root(trans, tree_root, ret = btrfs_update_root(trans, tree_root,
&root->root_key, &root->root_key,
root_item); root_item);
BUG_ON(ret); BUG_ON(ret);
btrfs_end_transaction(trans, tree_root); btrfs_end_transaction_throttle(trans, tree_root);
trans = btrfs_start_transaction(tree_root, 0); trans = btrfs_start_transaction(tree_root, 0);
if (IS_ERR(trans)) if (block_rsv)
return PTR_ERR(trans); trans->block_rsv = block_rsv;
} else {
unsigned long update;
update = trans->delayed_ref_updates;
trans->delayed_ref_updates = 0;
if (update)
btrfs_run_delayed_refs(trans, tree_root,
update);
} }
} }
btrfs_release_path(root, path); btrfs_release_path(root, path);
...@@ -6026,7 +6021,7 @@ int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref) ...@@ -6026,7 +6021,7 @@ int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref)
kfree(root); kfree(root);
} }
out: out:
btrfs_end_transaction(trans, tree_root); btrfs_end_transaction_throttle(trans, tree_root);
kfree(wc); kfree(wc);
btrfs_free_path(path); btrfs_free_path(path);
return err; return err;
......
...@@ -44,8 +44,12 @@ struct tree_entry { ...@@ -44,8 +44,12 @@ struct tree_entry {
struct backref_node { struct backref_node {
struct rb_node rb_node; struct rb_node rb_node;
u64 bytenr; u64 bytenr;
/* objectid tree block owner */
u64 new_bytenr;
/* objectid of tree block owner, can be not uptodate */
u64 owner; u64 owner;
/* link to pending, changed or detached list */
struct list_head list;
/* list of upper level blocks reference this block */ /* list of upper level blocks reference this block */
struct list_head upper; struct list_head upper;
/* list of child blocks in the cache */ /* list of child blocks in the cache */
...@@ -56,9 +60,9 @@ struct backref_node { ...@@ -56,9 +60,9 @@ struct backref_node {
struct extent_buffer *eb; struct extent_buffer *eb;
/* level of tree block */ /* level of tree block */
unsigned int level:8; unsigned int level:8;
/* 1 if the block is root of old snapshot */ /* is the block in non-reference counted tree */
unsigned int old_root:1; unsigned int cowonly:1;
/* 1 if no child blocks in the cache */ /* 1 if no child node in the cache */
unsigned int lowest:1; unsigned int lowest:1;
/* is the extent buffer locked */ /* is the extent buffer locked */
unsigned int locked:1; unsigned int locked:1;
...@@ -66,6 +70,16 @@ struct backref_node { ...@@ -66,6 +70,16 @@ struct backref_node {
unsigned int processed:1; unsigned int processed:1;
/* have backrefs of this block been checked */ /* have backrefs of this block been checked */
unsigned int checked:1; unsigned int checked:1;
/*
* 1 if corresponding block has been cowed but some upper
* level block pointers may not point to the new location
*/
unsigned int pending:1;
/*
* 1 if the backref node isn't connected to any other
* backref node.
*/
unsigned int detached:1;
}; };
/* /*
...@@ -74,7 +88,6 @@ struct backref_node { ...@@ -74,7 +88,6 @@ struct backref_node {
struct backref_edge { struct backref_edge {
struct list_head list[2]; struct list_head list[2];
struct backref_node *node[2]; struct backref_node *node[2];
u64 blockptr;
}; };
#define LOWER 0 #define LOWER 0
...@@ -83,9 +96,25 @@ struct backref_edge { ...@@ -83,9 +96,25 @@ struct backref_edge {
struct backref_cache { struct backref_cache {
/* red black tree of all backref nodes in the cache */ /* red black tree of all backref nodes in the cache */
struct rb_root rb_root; struct rb_root rb_root;
/* list of backref nodes with no child block in the cache */ /* for passing backref nodes to btrfs_reloc_cow_block */
struct backref_node *path[BTRFS_MAX_LEVEL];
/*
* list of blocks that have been cowed but some block
* pointers in upper level blocks may not reflect the
* new location
*/
struct list_head pending[BTRFS_MAX_LEVEL]; struct list_head pending[BTRFS_MAX_LEVEL];
spinlock_t lock; /* list of backref nodes with no child node */
struct list_head leaves;
/* list of blocks that have been cowed in current transaction */
struct list_head changed;
/* list of detached backref node. */
struct list_head detached;
u64 last_trans;
int nr_nodes;
int nr_edges;
}; };
/* /*
...@@ -113,15 +142,6 @@ struct tree_block { ...@@ -113,15 +142,6 @@ struct tree_block {
unsigned int key_ready:1; unsigned int key_ready:1;
}; };
/* inode vector */
#define INODEVEC_SIZE 16
struct inodevec {
struct list_head list;
struct inode *inode[INODEVEC_SIZE];
int nr;
};
#define MAX_EXTENTS 128 #define MAX_EXTENTS 128
struct file_extent_cluster { struct file_extent_cluster {
...@@ -138,36 +158,43 @@ struct reloc_control { ...@@ -138,36 +158,43 @@ struct reloc_control {
struct btrfs_root *extent_root; struct btrfs_root *extent_root;
/* inode for moving data */ /* inode for moving data */
struct inode *data_inode; struct inode *data_inode;
struct btrfs_workers workers;
struct btrfs_block_rsv *block_rsv;
struct backref_cache backref_cache;
struct file_extent_cluster cluster;
/* tree blocks have been processed */ /* tree blocks have been processed */
struct extent_io_tree processed_blocks; struct extent_io_tree processed_blocks;
/* map start of tree root to corresponding reloc tree */ /* map start of tree root to corresponding reloc tree */
struct mapping_tree reloc_root_tree; struct mapping_tree reloc_root_tree;
/* list of reloc trees */ /* list of reloc trees */
struct list_head reloc_roots; struct list_head reloc_roots;
/* size of metadata reservation for merging reloc trees */
u64 merging_rsv_size;
/* size of relocated tree nodes */
u64 nodes_relocated;
u64 search_start; u64 search_start;
u64 extents_found; u64 extents_found;
u64 extents_skipped;
int stage; int block_rsv_retries;
int create_reloc_root;
unsigned int stage:8;
unsigned int create_reloc_tree:1;
unsigned int merge_reloc_tree:1;
unsigned int found_file_extent:1; unsigned int found_file_extent:1;
unsigned int found_old_snapshot:1; unsigned int commit_transaction:1;
}; };
/* stages of data relocation */ /* stages of data relocation */
#define MOVE_DATA_EXTENTS 0 #define MOVE_DATA_EXTENTS 0
#define UPDATE_DATA_PTRS 1 #define UPDATE_DATA_PTRS 1
/* static void remove_backref_node(struct backref_cache *cache,
* merge reloc tree to corresponding fs tree in worker threads struct backref_node *node);
*/ static void __mark_block_processed(struct reloc_control *rc,
struct async_merge { struct backref_node *node);
struct btrfs_work work;
struct reloc_control *rc;
struct btrfs_root *root;
struct completion *done;
atomic_t *num_pending;
};
static void mapping_tree_init(struct mapping_tree *tree) static void mapping_tree_init(struct mapping_tree *tree)
{ {
...@@ -181,15 +208,80 @@ static void backref_cache_init(struct backref_cache *cache) ...@@ -181,15 +208,80 @@ static void backref_cache_init(struct backref_cache *cache)
cache->rb_root = RB_ROOT; cache->rb_root = RB_ROOT;
for (i = 0; i < BTRFS_MAX_LEVEL; i++) for (i = 0; i < BTRFS_MAX_LEVEL; i++)
INIT_LIST_HEAD(&cache->pending[i]); INIT_LIST_HEAD(&cache->pending[i]);
spin_lock_init(&cache->lock); INIT_LIST_HEAD(&cache->changed);
INIT_LIST_HEAD(&cache->detached);
INIT_LIST_HEAD(&cache->leaves);
}
static void backref_cache_cleanup(struct backref_cache *cache)
{
struct backref_node *node;
int i;
while (!list_empty(&cache->detached)) {
node = list_entry(cache->detached.next,
struct backref_node, list);
remove_backref_node(cache, node);
}
while (!list_empty(&cache->leaves)) {
node = list_entry(cache->leaves.next,
struct backref_node, lower);
remove_backref_node(cache, node);
}
cache->last_trans = 0;
for (i = 0; i < BTRFS_MAX_LEVEL; i++)
BUG_ON(!list_empty(&cache->pending[i]));
BUG_ON(!list_empty(&cache->changed));
BUG_ON(!list_empty(&cache->detached));
BUG_ON(!RB_EMPTY_ROOT(&cache->rb_root));
BUG_ON(cache->nr_nodes);
BUG_ON(cache->nr_edges);
}
static struct backref_node *alloc_backref_node(struct backref_cache *cache)
{
struct backref_node *node;
node = kzalloc(sizeof(*node), GFP_NOFS);
if (node) {
INIT_LIST_HEAD(&node->list);
INIT_LIST_HEAD(&node->upper);
INIT_LIST_HEAD(&node->lower);
RB_CLEAR_NODE(&node->rb_node);
cache->nr_nodes++;
}
return node;
}
static void free_backref_node(struct backref_cache *cache,
struct backref_node *node)
{
if (node) {
cache->nr_nodes--;
kfree(node);
}
}
static struct backref_edge *alloc_backref_edge(struct backref_cache *cache)
{
struct backref_edge *edge;
edge = kzalloc(sizeof(*edge), GFP_NOFS);
if (edge)
cache->nr_edges++;
return edge;
} }
static void backref_node_init(struct backref_node *node) static void free_backref_edge(struct backref_cache *cache,
struct backref_edge *edge)
{ {
memset(node, 0, sizeof(*node)); if (edge) {
INIT_LIST_HEAD(&node->upper); cache->nr_edges--;
INIT_LIST_HEAD(&node->lower); kfree(edge);
RB_CLEAR_NODE(&node->rb_node); }
} }
static struct rb_node *tree_insert(struct rb_root *root, u64 bytenr, static struct rb_node *tree_insert(struct rb_root *root, u64 bytenr,
...@@ -250,6 +342,7 @@ static struct backref_node *walk_up_backref(struct backref_node *node, ...@@ -250,6 +342,7 @@ static struct backref_node *walk_up_backref(struct backref_node *node,
edges[idx++] = edge; edges[idx++] = edge;
node = edge->node[UPPER]; node = edge->node[UPPER];
} }
BUG_ON(node->detached);
*index = idx; *index = idx;
return node; return node;
} }
...@@ -281,13 +374,18 @@ static struct backref_node *walk_down_backref(struct backref_edge *edges[], ...@@ -281,13 +374,18 @@ static struct backref_node *walk_down_backref(struct backref_edge *edges[],
return NULL; return NULL;
} }
static void unlock_node_buffer(struct backref_node *node)
{
if (node->locked) {
btrfs_tree_unlock(node->eb);
node->locked = 0;
}
}
static void drop_node_buffer(struct backref_node *node) static void drop_node_buffer(struct backref_node *node)
{ {
if (node->eb) { if (node->eb) {
if (node->locked) { unlock_node_buffer(node);
btrfs_tree_unlock(node->eb);
node->locked = 0;
}
free_extent_buffer(node->eb); free_extent_buffer(node->eb);
node->eb = NULL; node->eb = NULL;
} }
...@@ -296,14 +394,14 @@ static void drop_node_buffer(struct backref_node *node) ...@@ -296,14 +394,14 @@ static void drop_node_buffer(struct backref_node *node)
static void drop_backref_node(struct backref_cache *tree, static void drop_backref_node(struct backref_cache *tree,
struct backref_node *node) struct backref_node *node)
{ {
BUG_ON(!node->lowest);
BUG_ON(!list_empty(&node->upper)); BUG_ON(!list_empty(&node->upper));
drop_node_buffer(node); drop_node_buffer(node);
list_del(&node->list);
list_del(&node->lower); list_del(&node->lower);
if (!RB_EMPTY_NODE(&node->rb_node))
rb_erase(&node->rb_node, &tree->rb_root); rb_erase(&node->rb_node, &tree->rb_root);
kfree(node); free_backref_node(tree, node);
} }
/* /*
...@@ -318,27 +416,121 @@ static void remove_backref_node(struct backref_cache *cache, ...@@ -318,27 +416,121 @@ static void remove_backref_node(struct backref_cache *cache,
if (!node) if (!node)
return; return;
BUG_ON(!node->lowest); BUG_ON(!node->lowest && !node->detached);
while (!list_empty(&node->upper)) { while (!list_empty(&node->upper)) {
edge = list_entry(node->upper.next, struct backref_edge, edge = list_entry(node->upper.next, struct backref_edge,
list[LOWER]); list[LOWER]);
upper = edge->node[UPPER]; upper = edge->node[UPPER];
list_del(&edge->list[LOWER]); list_del(&edge->list[LOWER]);
list_del(&edge->list[UPPER]); list_del(&edge->list[UPPER]);
kfree(edge); free_backref_edge(cache, edge);
if (RB_EMPTY_NODE(&upper->rb_node)) {
BUG_ON(!list_empty(&node->upper));
drop_backref_node(cache, node);
node = upper;
node->lowest = 1;
continue;
}
/* /*
* add the node to pending list if no other * add the node to leaf node list if no other
* child block cached. * child block cached.
*/ */
if (list_empty(&upper->lower)) { if (list_empty(&upper->lower)) {
list_add_tail(&upper->lower, list_add_tail(&upper->lower, &cache->leaves);
&cache->pending[upper->level]);
upper->lowest = 1; upper->lowest = 1;
} }
} }
drop_backref_node(cache, node); drop_backref_node(cache, node);
} }
static void update_backref_node(struct backref_cache *cache,
struct backref_node *node, u64 bytenr)
{
struct rb_node *rb_node;
rb_erase(&node->rb_node, &cache->rb_root);
node->bytenr = bytenr;
rb_node = tree_insert(&cache->rb_root, node->bytenr, &node->rb_node);
BUG_ON(rb_node);
}
/*
* update backref cache after a transaction commit
*/
static int update_backref_cache(struct btrfs_trans_handle *trans,
struct backref_cache *cache)
{
struct backref_node *node;
int level = 0;
if (cache->last_trans == 0) {
cache->last_trans = trans->transid;
return 0;
}
if (cache->last_trans == trans->transid)
return 0;
/*
* detached nodes are used to avoid unnecessary backref
* lookup. transaction commit changes the extent tree.
* so the detached nodes are no longer useful.
*/
while (!list_empty(&cache->detached)) {
node = list_entry(cache->detached.next,
struct backref_node, list);
remove_backref_node(cache, node);
}
while (!list_empty(&cache->changed)) {
node = list_entry(cache->changed.next,
struct backref_node, list);
list_del_init(&node->list);
BUG_ON(node->pending);
update_backref_node(cache, node, node->new_bytenr);
}
/*
* some nodes can be left in the pending list if there were
* errors during processing the pending nodes.
*/
for (level = 0; level < BTRFS_MAX_LEVEL; level++) {
list_for_each_entry(node, &cache->pending[level], list) {
BUG_ON(!node->pending);
if (node->bytenr == node->new_bytenr)
continue;
update_backref_node(cache, node, node->new_bytenr);
}
}
cache->last_trans = 0;
return 1;
}
static int should_ignore_root(struct btrfs_root *root)
{
struct btrfs_root *reloc_root;
if (!root->ref_cows)
return 0;
reloc_root = root->reloc_root;
if (!reloc_root)
return 0;
if (btrfs_root_last_snapshot(&reloc_root->root_item) ==
root->fs_info->running_transaction->transid - 1)
return 0;
/*
* if there is reloc tree and it was created in previous
* transaction backref lookup can find the reloc tree,
* so backref node for the fs tree root is useless for
* relocation.
*/
return 1;
}
/* /*
* find reloc tree by address of tree root * find reloc tree by address of tree root
*/ */
...@@ -453,11 +645,12 @@ int find_inline_backref(struct extent_buffer *leaf, int slot, ...@@ -453,11 +645,12 @@ int find_inline_backref(struct extent_buffer *leaf, int slot,
* for all upper level blocks that directly/indirectly reference the * for all upper level blocks that directly/indirectly reference the
* block are also cached. * block are also cached.
*/ */
static struct backref_node *build_backref_tree(struct reloc_control *rc, static noinline_for_stack
struct backref_cache *cache, struct backref_node *build_backref_tree(struct reloc_control *rc,
struct btrfs_key *node_key, struct btrfs_key *node_key,
int level, u64 bytenr) int level, u64 bytenr)
{ {
struct backref_cache *cache = &rc->backref_cache;
struct btrfs_path *path1; struct btrfs_path *path1;
struct btrfs_path *path2; struct btrfs_path *path2;
struct extent_buffer *eb; struct extent_buffer *eb;
...@@ -473,6 +666,8 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc, ...@@ -473,6 +666,8 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc,
unsigned long end; unsigned long end;
unsigned long ptr; unsigned long ptr;
LIST_HEAD(list); LIST_HEAD(list);
LIST_HEAD(useless);
int cowonly;
int ret; int ret;
int err = 0; int err = 0;
...@@ -483,15 +678,13 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc, ...@@ -483,15 +678,13 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc,
goto out; goto out;
} }
node = kmalloc(sizeof(*node), GFP_NOFS); node = alloc_backref_node(cache);
if (!node) { if (!node) {
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
} }
backref_node_init(node);
node->bytenr = bytenr; node->bytenr = bytenr;
node->owner = 0;
node->level = level; node->level = level;
node->lowest = 1; node->lowest = 1;
cur = node; cur = node;
...@@ -587,17 +780,20 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc, ...@@ -587,17 +780,20 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc,
#ifdef BTRFS_COMPAT_EXTENT_TREE_V0 #ifdef BTRFS_COMPAT_EXTENT_TREE_V0
if (key.type == BTRFS_SHARED_BLOCK_REF_KEY || if (key.type == BTRFS_SHARED_BLOCK_REF_KEY ||
key.type == BTRFS_EXTENT_REF_V0_KEY) { key.type == BTRFS_EXTENT_REF_V0_KEY) {
if (key.objectid == key.offset && if (key.type == BTRFS_EXTENT_REF_V0_KEY) {
key.type == BTRFS_EXTENT_REF_V0_KEY) {
struct btrfs_extent_ref_v0 *ref0; struct btrfs_extent_ref_v0 *ref0;
ref0 = btrfs_item_ptr(eb, path1->slots[0], ref0 = btrfs_item_ptr(eb, path1->slots[0],
struct btrfs_extent_ref_v0); struct btrfs_extent_ref_v0);
root = find_tree_root(rc, eb, ref0); root = find_tree_root(rc, eb, ref0);
if (root) if (!root->ref_cows)
cur->root = root; cur->cowonly = 1;
else if (key.objectid == key.offset) {
cur->old_root = 1; if (root && !should_ignore_root(root))
break; cur->root = root;
else
list_add(&cur->list, &useless);
break;
}
} }
#else #else
BUG_ON(key.type == BTRFS_EXTENT_REF_V0_KEY); BUG_ON(key.type == BTRFS_EXTENT_REF_V0_KEY);
...@@ -614,22 +810,20 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc, ...@@ -614,22 +810,20 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc,
break; break;
} }
edge = kzalloc(sizeof(*edge), GFP_NOFS); edge = alloc_backref_edge(cache);
if (!edge) { if (!edge) {
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
} }
rb_node = tree_search(&cache->rb_root, key.offset); rb_node = tree_search(&cache->rb_root, key.offset);
if (!rb_node) { if (!rb_node) {
upper = kmalloc(sizeof(*upper), GFP_NOFS); upper = alloc_backref_node(cache);
if (!upper) { if (!upper) {
kfree(edge); free_backref_edge(cache, edge);
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
} }
backref_node_init(upper);
upper->bytenr = key.offset; upper->bytenr = key.offset;
upper->owner = 0;
upper->level = cur->level + 1; upper->level = cur->level + 1;
/* /*
* backrefs for the upper level block isn't * backrefs for the upper level block isn't
...@@ -639,11 +833,12 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc, ...@@ -639,11 +833,12 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc,
} else { } else {
upper = rb_entry(rb_node, struct backref_node, upper = rb_entry(rb_node, struct backref_node,
rb_node); rb_node);
BUG_ON(!upper->checked);
INIT_LIST_HEAD(&edge->list[UPPER]); INIT_LIST_HEAD(&edge->list[UPPER]);
} }
list_add(&edge->list[LOWER], &cur->upper); list_add_tail(&edge->list[LOWER], &cur->upper);
edge->node[UPPER] = upper;
edge->node[LOWER] = cur; edge->node[LOWER] = cur;
edge->node[UPPER] = upper;
goto next; goto next;
} else if (key.type != BTRFS_TREE_BLOCK_REF_KEY) { } else if (key.type != BTRFS_TREE_BLOCK_REF_KEY) {
...@@ -657,11 +852,17 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc, ...@@ -657,11 +852,17 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc,
goto out; goto out;
} }
if (!root->ref_cows)
cur->cowonly = 1;
if (btrfs_root_level(&root->root_item) == cur->level) { if (btrfs_root_level(&root->root_item) == cur->level) {
/* tree root */ /* tree root */
BUG_ON(btrfs_root_bytenr(&root->root_item) != BUG_ON(btrfs_root_bytenr(&root->root_item) !=
cur->bytenr); cur->bytenr);
cur->root = root; if (should_ignore_root(root))
list_add(&cur->list, &useless);
else
cur->root = root;
break; break;
} }
...@@ -692,11 +893,14 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc, ...@@ -692,11 +893,14 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc,
if (!path2->nodes[level]) { if (!path2->nodes[level]) {
BUG_ON(btrfs_root_bytenr(&root->root_item) != BUG_ON(btrfs_root_bytenr(&root->root_item) !=
lower->bytenr); lower->bytenr);
lower->root = root; if (should_ignore_root(root))
list_add(&lower->list, &useless);
else
lower->root = root;
break; break;
} }
edge = kzalloc(sizeof(*edge), GFP_NOFS); edge = alloc_backref_edge(cache);
if (!edge) { if (!edge) {
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
...@@ -705,16 +909,17 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc, ...@@ -705,16 +909,17 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc,
eb = path2->nodes[level]; eb = path2->nodes[level];
rb_node = tree_search(&cache->rb_root, eb->start); rb_node = tree_search(&cache->rb_root, eb->start);
if (!rb_node) { if (!rb_node) {
upper = kmalloc(sizeof(*upper), GFP_NOFS); upper = alloc_backref_node(cache);
if (!upper) { if (!upper) {
kfree(edge); free_backref_edge(cache, edge);
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
} }
backref_node_init(upper);
upper->bytenr = eb->start; upper->bytenr = eb->start;
upper->owner = btrfs_header_owner(eb); upper->owner = btrfs_header_owner(eb);
upper->level = lower->level + 1; upper->level = lower->level + 1;
if (!root->ref_cows)
upper->cowonly = 1;
/* /*
* if we know the block isn't shared * if we know the block isn't shared
...@@ -744,10 +949,12 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc, ...@@ -744,10 +949,12 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc,
rb_node); rb_node);
BUG_ON(!upper->checked); BUG_ON(!upper->checked);
INIT_LIST_HEAD(&edge->list[UPPER]); INIT_LIST_HEAD(&edge->list[UPPER]);
if (!upper->owner)
upper->owner = btrfs_header_owner(eb);
} }
list_add_tail(&edge->list[LOWER], &lower->upper); list_add_tail(&edge->list[LOWER], &lower->upper);
edge->node[UPPER] = upper;
edge->node[LOWER] = lower; edge->node[LOWER] = lower;
edge->node[UPPER] = upper;
if (rb_node) if (rb_node)
break; break;
...@@ -785,8 +992,13 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc, ...@@ -785,8 +992,13 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc,
* into the cache. * into the cache.
*/ */
BUG_ON(!node->checked); BUG_ON(!node->checked);
rb_node = tree_insert(&cache->rb_root, node->bytenr, &node->rb_node); cowonly = node->cowonly;
BUG_ON(rb_node); if (!cowonly) {
rb_node = tree_insert(&cache->rb_root, node->bytenr,
&node->rb_node);
BUG_ON(rb_node);
list_add_tail(&node->lower, &cache->leaves);
}
list_for_each_entry(edge, &node->upper, list[LOWER]) list_for_each_entry(edge, &node->upper, list[LOWER])
list_add_tail(&edge->list[UPPER], &list); list_add_tail(&edge->list[UPPER], &list);
...@@ -795,6 +1007,14 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc, ...@@ -795,6 +1007,14 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc,
edge = list_entry(list.next, struct backref_edge, list[UPPER]); edge = list_entry(list.next, struct backref_edge, list[UPPER]);
list_del_init(&edge->list[UPPER]); list_del_init(&edge->list[UPPER]);
upper = edge->node[UPPER]; upper = edge->node[UPPER];
if (upper->detached) {
list_del(&edge->list[LOWER]);
lower = edge->node[LOWER];
free_backref_edge(cache, edge);
if (list_empty(&lower->upper))
list_add(&lower->list, &useless);
continue;
}
if (!RB_EMPTY_NODE(&upper->rb_node)) { if (!RB_EMPTY_NODE(&upper->rb_node)) {
if (upper->lowest) { if (upper->lowest) {
...@@ -807,25 +1027,69 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc, ...@@ -807,25 +1027,69 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc,
} }
BUG_ON(!upper->checked); BUG_ON(!upper->checked);
rb_node = tree_insert(&cache->rb_root, upper->bytenr, BUG_ON(cowonly != upper->cowonly);
&upper->rb_node); if (!cowonly) {
BUG_ON(rb_node); rb_node = tree_insert(&cache->rb_root, upper->bytenr,
&upper->rb_node);
BUG_ON(rb_node);
}
list_add_tail(&edge->list[UPPER], &upper->lower); list_add_tail(&edge->list[UPPER], &upper->lower);
list_for_each_entry(edge, &upper->upper, list[LOWER]) list_for_each_entry(edge, &upper->upper, list[LOWER])
list_add_tail(&edge->list[UPPER], &list); list_add_tail(&edge->list[UPPER], &list);
} }
/*
* process useless backref nodes. backref nodes for tree leaves
* are deleted from the cache. backref nodes for upper level
* tree blocks are left in the cache to avoid unnecessary backref
* lookup.
*/
while (!list_empty(&useless)) {
upper = list_entry(useless.next, struct backref_node, list);
list_del_init(&upper->list);
BUG_ON(!list_empty(&upper->upper));
if (upper == node)
node = NULL;
if (upper->lowest) {
list_del_init(&upper->lower);
upper->lowest = 0;
}
while (!list_empty(&upper->lower)) {
edge = list_entry(upper->lower.next,
struct backref_edge, list[UPPER]);
list_del(&edge->list[UPPER]);
list_del(&edge->list[LOWER]);
lower = edge->node[LOWER];
free_backref_edge(cache, edge);
if (list_empty(&lower->upper))
list_add(&lower->list, &useless);
}
__mark_block_processed(rc, upper);
if (upper->level > 0) {
list_add(&upper->list, &cache->detached);
upper->detached = 1;
} else {
rb_erase(&upper->rb_node, &cache->rb_root);
free_backref_node(cache, upper);
}
}
out: out:
btrfs_free_path(path1); btrfs_free_path(path1);
btrfs_free_path(path2); btrfs_free_path(path2);
if (err) { if (err) {
INIT_LIST_HEAD(&list); while (!list_empty(&useless)) {
lower = list_entry(useless.next,
struct backref_node, upper);
list_del_init(&lower->upper);
}
upper = node; upper = node;
INIT_LIST_HEAD(&list);
while (upper) { while (upper) {
if (RB_EMPTY_NODE(&upper->rb_node)) { if (RB_EMPTY_NODE(&upper->rb_node)) {
list_splice_tail(&upper->upper, &list); list_splice_tail(&upper->upper, &list);
kfree(upper); free_backref_node(cache, upper);
} }
if (list_empty(&list)) if (list_empty(&list))
...@@ -833,14 +1097,103 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc, ...@@ -833,14 +1097,103 @@ static struct backref_node *build_backref_tree(struct reloc_control *rc,
edge = list_entry(list.next, struct backref_edge, edge = list_entry(list.next, struct backref_edge,
list[LOWER]); list[LOWER]);
list_del(&edge->list[LOWER]);
upper = edge->node[UPPER]; upper = edge->node[UPPER];
kfree(edge); free_backref_edge(cache, edge);
} }
return ERR_PTR(err); return ERR_PTR(err);
} }
BUG_ON(node && node->detached);
return node; return node;
} }
/*
* helper to add backref node for the newly created snapshot.
* the backref node is created by cloning backref node that
* corresponds to root of source tree
*/
static int clone_backref_node(struct btrfs_trans_handle *trans,
struct reloc_control *rc,
struct btrfs_root *src,
struct btrfs_root *dest)
{
struct btrfs_root *reloc_root = src->reloc_root;
struct backref_cache *cache = &rc->backref_cache;
struct backref_node *node = NULL;
struct backref_node *new_node;
struct backref_edge *edge;
struct backref_edge *new_edge;
struct rb_node *rb_node;
if (cache->last_trans > 0)
update_backref_cache(trans, cache);
rb_node = tree_search(&cache->rb_root, src->commit_root->start);
if (rb_node) {
node = rb_entry(rb_node, struct backref_node, rb_node);
if (node->detached)
node = NULL;
else
BUG_ON(node->new_bytenr != reloc_root->node->start);
}
if (!node) {
rb_node = tree_search(&cache->rb_root,
reloc_root->commit_root->start);
if (rb_node) {
node = rb_entry(rb_node, struct backref_node,
rb_node);
BUG_ON(node->detached);
}
}
if (!node)
return 0;
new_node = alloc_backref_node(cache);
if (!new_node)
return -ENOMEM;
new_node->bytenr = dest->node->start;
new_node->level = node->level;
new_node->lowest = node->lowest;
new_node->root = dest;
if (!node->lowest) {
list_for_each_entry(edge, &node->lower, list[UPPER]) {
new_edge = alloc_backref_edge(cache);
if (!new_edge)
goto fail;
new_edge->node[UPPER] = new_node;
new_edge->node[LOWER] = edge->node[LOWER];
list_add_tail(&new_edge->list[UPPER],
&new_node->lower);
}
}
rb_node = tree_insert(&cache->rb_root, new_node->bytenr,
&new_node->rb_node);
BUG_ON(rb_node);
if (!new_node->lowest) {
list_for_each_entry(new_edge, &new_node->lower, list[UPPER]) {
list_add_tail(&new_edge->list[LOWER],
&new_edge->node[LOWER]->upper);
}
}
return 0;
fail:
while (!list_empty(&new_node->lower)) {
new_edge = list_entry(new_node->lower.next,
struct backref_edge, list[UPPER]);
list_del(&new_edge->list[UPPER]);
free_backref_edge(cache, new_edge);
}
free_backref_node(cache, new_node);
return -ENOMEM;
}
/* /*
* helper to add 'address of tree root -> reloc tree' mapping * helper to add 'address of tree root -> reloc tree' mapping
*/ */
...@@ -901,12 +1254,8 @@ static int __update_reloc_root(struct btrfs_root *root, int del) ...@@ -901,12 +1254,8 @@ static int __update_reloc_root(struct btrfs_root *root, int del)
return 0; return 0;
} }
/* static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
* create reloc tree for a given fs tree. reloc tree is just a struct btrfs_root *root, u64 objectid)
* snapshot of the fs tree with special root objectid.
*/
int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{ {
struct btrfs_root *reloc_root; struct btrfs_root *reloc_root;
struct extent_buffer *eb; struct extent_buffer *eb;
...@@ -914,36 +1263,45 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans, ...@@ -914,36 +1263,45 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
struct btrfs_key root_key; struct btrfs_key root_key;
int ret; int ret;
if (root->reloc_root) {
reloc_root = root->reloc_root;
reloc_root->last_trans = trans->transid;
return 0;
}
if (!root->fs_info->reloc_ctl ||
!root->fs_info->reloc_ctl->create_reloc_root ||
root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID)
return 0;
root_item = kmalloc(sizeof(*root_item), GFP_NOFS); root_item = kmalloc(sizeof(*root_item), GFP_NOFS);
BUG_ON(!root_item); BUG_ON(!root_item);
root_key.objectid = BTRFS_TREE_RELOC_OBJECTID; root_key.objectid = BTRFS_TREE_RELOC_OBJECTID;
root_key.type = BTRFS_ROOT_ITEM_KEY; root_key.type = BTRFS_ROOT_ITEM_KEY;
root_key.offset = root->root_key.objectid; root_key.offset = objectid;
ret = btrfs_copy_root(trans, root, root->commit_root, &eb, if (root->root_key.objectid == objectid) {
BTRFS_TREE_RELOC_OBJECTID); /* called by btrfs_init_reloc_root */
BUG_ON(ret); ret = btrfs_copy_root(trans, root, root->commit_root, &eb,
BTRFS_TREE_RELOC_OBJECTID);
BUG_ON(ret);
btrfs_set_root_last_snapshot(&root->root_item,
trans->transid - 1);
} else {
/*
* called by btrfs_reloc_post_snapshot_hook.
* the source tree is a reloc tree, all tree blocks
* modified after it was created have RELOC flag
* set in their headers. so it's OK to not update
* the 'last_snapshot'.
*/
ret = btrfs_copy_root(trans, root, root->node, &eb,
BTRFS_TREE_RELOC_OBJECTID);
BUG_ON(ret);
}
btrfs_set_root_last_snapshot(&root->root_item, trans->transid - 1);
memcpy(root_item, &root->root_item, sizeof(*root_item)); memcpy(root_item, &root->root_item, sizeof(*root_item));
btrfs_set_root_refs(root_item, 1);
btrfs_set_root_bytenr(root_item, eb->start); btrfs_set_root_bytenr(root_item, eb->start);
btrfs_set_root_level(root_item, btrfs_header_level(eb)); btrfs_set_root_level(root_item, btrfs_header_level(eb));
btrfs_set_root_generation(root_item, trans->transid); btrfs_set_root_generation(root_item, trans->transid);
memset(&root_item->drop_progress, 0, sizeof(struct btrfs_disk_key));
root_item->drop_level = 0; if (root->root_key.objectid == objectid) {
btrfs_set_root_refs(root_item, 0);
memset(&root_item->drop_progress, 0,
sizeof(struct btrfs_disk_key));
root_item->drop_level = 0;
}
btrfs_tree_unlock(eb); btrfs_tree_unlock(eb);
free_extent_buffer(eb); free_extent_buffer(eb);
...@@ -957,6 +1315,37 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans, ...@@ -957,6 +1315,37 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
&root_key); &root_key);
BUG_ON(IS_ERR(reloc_root)); BUG_ON(IS_ERR(reloc_root));
reloc_root->last_trans = trans->transid; reloc_root->last_trans = trans->transid;
return reloc_root;
}
/*
* create reloc tree for a given fs tree. reloc tree is just a
* snapshot of the fs tree with special root objectid.
*/
int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
struct btrfs_root *reloc_root;
struct reloc_control *rc = root->fs_info->reloc_ctl;
int clear_rsv = 0;
if (root->reloc_root) {
reloc_root = root->reloc_root;
reloc_root->last_trans = trans->transid;
return 0;
}
if (!rc || !rc->create_reloc_tree ||
root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID)
return 0;
if (!trans->block_rsv) {
trans->block_rsv = rc->block_rsv;
clear_rsv = 1;
}
reloc_root = create_reloc_root(trans, root, root->root_key.objectid);
if (clear_rsv)
trans->block_rsv = NULL;
__add_reloc_root(reloc_root); __add_reloc_root(reloc_root);
root->reloc_root = reloc_root; root->reloc_root = reloc_root;
...@@ -980,7 +1369,8 @@ int btrfs_update_reloc_root(struct btrfs_trans_handle *trans, ...@@ -980,7 +1369,8 @@ int btrfs_update_reloc_root(struct btrfs_trans_handle *trans,
reloc_root = root->reloc_root; reloc_root = root->reloc_root;
root_item = &reloc_root->root_item; root_item = &reloc_root->root_item;
if (btrfs_root_refs(root_item) == 0) { if (root->fs_info->reloc_ctl->merge_reloc_tree &&
btrfs_root_refs(root_item) == 0) {
root->reloc_root = NULL; root->reloc_root = NULL;
del = 1; del = 1;
} }
...@@ -1102,8 +1492,7 @@ static int get_new_location(struct inode *reloc_inode, u64 *new_bytenr, ...@@ -1102,8 +1492,7 @@ static int get_new_location(struct inode *reloc_inode, u64 *new_bytenr,
goto out; goto out;
} }
if (new_bytenr) *new_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
*new_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
ret = 0; ret = 0;
out: out:
btrfs_free_path(path); btrfs_free_path(path);
...@@ -1114,19 +1503,18 @@ static int get_new_location(struct inode *reloc_inode, u64 *new_bytenr, ...@@ -1114,19 +1503,18 @@ static int get_new_location(struct inode *reloc_inode, u64 *new_bytenr,
* update file extent items in the tree leaf to point to * update file extent items in the tree leaf to point to
* the new locations. * the new locations.
*/ */
static int replace_file_extents(struct btrfs_trans_handle *trans, static noinline_for_stack
struct reloc_control *rc, int replace_file_extents(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct reloc_control *rc,
struct extent_buffer *leaf, struct btrfs_root *root,
struct list_head *inode_list) struct extent_buffer *leaf)
{ {
struct btrfs_key key; struct btrfs_key key;
struct btrfs_file_extent_item *fi; struct btrfs_file_extent_item *fi;
struct inode *inode = NULL; struct inode *inode = NULL;
struct inodevec *ivec = NULL;
u64 parent; u64 parent;
u64 bytenr; u64 bytenr;
u64 new_bytenr; u64 new_bytenr = 0;
u64 num_bytes; u64 num_bytes;
u64 end; u64 end;
u32 nritems; u32 nritems;
...@@ -1166,21 +1554,12 @@ static int replace_file_extents(struct btrfs_trans_handle *trans, ...@@ -1166,21 +1554,12 @@ static int replace_file_extents(struct btrfs_trans_handle *trans,
* to complete and drop the extent cache * to complete and drop the extent cache
*/ */
if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) { if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) {
if (!ivec || ivec->nr == INODEVEC_SIZE) {
ivec = kmalloc(sizeof(*ivec), GFP_NOFS);
BUG_ON(!ivec);
ivec->nr = 0;
list_add_tail(&ivec->list, inode_list);
}
if (first) { if (first) {
inode = find_next_inode(root, key.objectid); inode = find_next_inode(root, key.objectid);
if (inode)
ivec->inode[ivec->nr++] = inode;
first = 0; first = 0;
} else if (inode && inode->i_ino < key.objectid) { } else if (inode && inode->i_ino < key.objectid) {
btrfs_add_delayed_iput(inode);
inode = find_next_inode(root, key.objectid); inode = find_next_inode(root, key.objectid);
if (inode)
ivec->inode[ivec->nr++] = inode;
} }
if (inode && inode->i_ino == key.objectid) { if (inode && inode->i_ino == key.objectid) {
end = key.offset + end = key.offset +
...@@ -1204,8 +1583,10 @@ static int replace_file_extents(struct btrfs_trans_handle *trans, ...@@ -1204,8 +1583,10 @@ static int replace_file_extents(struct btrfs_trans_handle *trans,
ret = get_new_location(rc->data_inode, &new_bytenr, ret = get_new_location(rc->data_inode, &new_bytenr,
bytenr, num_bytes); bytenr, num_bytes);
if (ret > 0) if (ret > 0) {
WARN_ON(1);
continue; continue;
}
BUG_ON(ret < 0); BUG_ON(ret < 0);
btrfs_set_file_extent_disk_bytenr(leaf, fi, new_bytenr); btrfs_set_file_extent_disk_bytenr(leaf, fi, new_bytenr);
...@@ -1225,6 +1606,8 @@ static int replace_file_extents(struct btrfs_trans_handle *trans, ...@@ -1225,6 +1606,8 @@ static int replace_file_extents(struct btrfs_trans_handle *trans,
} }
if (dirty) if (dirty)
btrfs_mark_buffer_dirty(leaf); btrfs_mark_buffer_dirty(leaf);
if (inode)
btrfs_add_delayed_iput(inode);
return 0; return 0;
} }
...@@ -1248,11 +1631,11 @@ int memcmp_node_keys(struct extent_buffer *eb, int slot, ...@@ -1248,11 +1631,11 @@ int memcmp_node_keys(struct extent_buffer *eb, int slot,
* if no block got replaced, 0 is returned. if there are other * if no block got replaced, 0 is returned. if there are other
* errors, a negative error number is returned. * errors, a negative error number is returned.
*/ */
static int replace_path(struct btrfs_trans_handle *trans, static noinline_for_stack
struct btrfs_root *dest, struct btrfs_root *src, int replace_path(struct btrfs_trans_handle *trans,
struct btrfs_path *path, struct btrfs_key *next_key, struct btrfs_root *dest, struct btrfs_root *src,
struct extent_buffer **leaf, struct btrfs_path *path, struct btrfs_key *next_key,
int lowest_level, int max_level) int lowest_level, int max_level)
{ {
struct extent_buffer *eb; struct extent_buffer *eb;
struct extent_buffer *parent; struct extent_buffer *parent;
...@@ -1263,16 +1646,16 @@ static int replace_path(struct btrfs_trans_handle *trans, ...@@ -1263,16 +1646,16 @@ static int replace_path(struct btrfs_trans_handle *trans,
u64 new_ptr_gen; u64 new_ptr_gen;
u64 last_snapshot; u64 last_snapshot;
u32 blocksize; u32 blocksize;
int cow = 0;
int level; int level;
int ret; int ret;
int slot; int slot;
BUG_ON(src->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID); BUG_ON(src->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID);
BUG_ON(dest->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID); BUG_ON(dest->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID);
BUG_ON(lowest_level > 1 && leaf);
last_snapshot = btrfs_root_last_snapshot(&src->root_item); last_snapshot = btrfs_root_last_snapshot(&src->root_item);
again:
slot = path->slots[lowest_level]; slot = path->slots[lowest_level];
btrfs_node_key_to_cpu(path->nodes[lowest_level], &key, slot); btrfs_node_key_to_cpu(path->nodes[lowest_level], &key, slot);
...@@ -1286,8 +1669,10 @@ static int replace_path(struct btrfs_trans_handle *trans, ...@@ -1286,8 +1669,10 @@ static int replace_path(struct btrfs_trans_handle *trans,
return 0; return 0;
} }
ret = btrfs_cow_block(trans, dest, eb, NULL, 0, &eb); if (cow) {
BUG_ON(ret); ret = btrfs_cow_block(trans, dest, eb, NULL, 0, &eb);
BUG_ON(ret);
}
btrfs_set_lock_blocking(eb); btrfs_set_lock_blocking(eb);
if (next_key) { if (next_key) {
...@@ -1331,7 +1716,7 @@ static int replace_path(struct btrfs_trans_handle *trans, ...@@ -1331,7 +1716,7 @@ static int replace_path(struct btrfs_trans_handle *trans,
if (new_bytenr == 0 || old_ptr_gen > last_snapshot || if (new_bytenr == 0 || old_ptr_gen > last_snapshot ||
memcmp_node_keys(parent, slot, path, level)) { memcmp_node_keys(parent, slot, path, level)) {
if (level <= lowest_level && !leaf) { if (level <= lowest_level) {
ret = 0; ret = 0;
break; break;
} }
...@@ -1339,16 +1724,12 @@ static int replace_path(struct btrfs_trans_handle *trans, ...@@ -1339,16 +1724,12 @@ static int replace_path(struct btrfs_trans_handle *trans,
eb = read_tree_block(dest, old_bytenr, blocksize, eb = read_tree_block(dest, old_bytenr, blocksize,
old_ptr_gen); old_ptr_gen);
btrfs_tree_lock(eb); btrfs_tree_lock(eb);
ret = btrfs_cow_block(trans, dest, eb, parent, if (cow) {
slot, &eb); ret = btrfs_cow_block(trans, dest, eb, parent,
BUG_ON(ret); slot, &eb);
btrfs_set_lock_blocking(eb); BUG_ON(ret);
if (level <= lowest_level) {
*leaf = eb;
ret = 0;
break;
} }
btrfs_set_lock_blocking(eb);
btrfs_tree_unlock(parent); btrfs_tree_unlock(parent);
free_extent_buffer(parent); free_extent_buffer(parent);
...@@ -1357,6 +1738,13 @@ static int replace_path(struct btrfs_trans_handle *trans, ...@@ -1357,6 +1738,13 @@ static int replace_path(struct btrfs_trans_handle *trans,
continue; continue;
} }
if (!cow) {
btrfs_tree_unlock(parent);
free_extent_buffer(parent);
cow = 1;
goto again;
}
btrfs_node_key_to_cpu(path->nodes[level], &key, btrfs_node_key_to_cpu(path->nodes[level], &key,
path->slots[level]); path->slots[level]);
btrfs_release_path(src, path); btrfs_release_path(src, path);
...@@ -1562,20 +1950,6 @@ static int invalidate_extent_cache(struct btrfs_root *root, ...@@ -1562,20 +1950,6 @@ static int invalidate_extent_cache(struct btrfs_root *root,
return 0; return 0;
} }
static void put_inodes(struct list_head *list)
{
struct inodevec *ivec;
while (!list_empty(list)) {
ivec = list_entry(list->next, struct inodevec, list);
list_del(&ivec->list);
while (ivec->nr > 0) {
ivec->nr--;
iput(ivec->inode[ivec->nr]);
}
kfree(ivec);
}
}
static int find_next_key(struct btrfs_path *path, int level, static int find_next_key(struct btrfs_path *path, int level,
struct btrfs_key *key) struct btrfs_key *key)
...@@ -1608,13 +1982,14 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc, ...@@ -1608,13 +1982,14 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
struct btrfs_root *reloc_root; struct btrfs_root *reloc_root;
struct btrfs_root_item *root_item; struct btrfs_root_item *root_item;
struct btrfs_path *path; struct btrfs_path *path;
struct extent_buffer *leaf = NULL; struct extent_buffer *leaf;
unsigned long nr; unsigned long nr;
int level; int level;
int max_level; int max_level;
int replaced = 0; int replaced = 0;
int ret; int ret;
int err = 0; int err = 0;
u32 min_reserved;
path = btrfs_alloc_path(); path = btrfs_alloc_path();
if (!path) if (!path)
...@@ -1648,34 +2023,23 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc, ...@@ -1648,34 +2023,23 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
btrfs_unlock_up_safe(path, 0); btrfs_unlock_up_safe(path, 0);
} }
if (level == 0 && rc->stage == UPDATE_DATA_PTRS) { min_reserved = root->nodesize * (BTRFS_MAX_LEVEL - 1) * 2;
trans = btrfs_start_transaction(root, 0); memset(&next_key, 0, sizeof(next_key));
leaf = path->nodes[0]; while (1) {
btrfs_item_key_to_cpu(leaf, &key, 0); trans = btrfs_start_transaction(root, 0);
btrfs_release_path(reloc_root, path); trans->block_rsv = rc->block_rsv;
ret = btrfs_search_slot(trans, root, &key, path, 0, 1); ret = btrfs_block_rsv_check(trans, root, rc->block_rsv,
if (ret < 0) { min_reserved, 0);
err = ret; if (ret) {
goto out; BUG_ON(ret != -EAGAIN);
ret = btrfs_commit_transaction(trans, root);
BUG_ON(ret);
continue;
} }
leaf = path->nodes[0];
btrfs_unlock_up_safe(path, 1);
ret = replace_file_extents(trans, rc, root, leaf,
&inode_list);
if (ret < 0)
err = ret;
goto out;
}
memset(&next_key, 0, sizeof(next_key));
while (1) {
leaf = NULL;
replaced = 0; replaced = 0;
trans = btrfs_start_transaction(root, 0);
max_level = level; max_level = level;
ret = walk_down_reloc_tree(reloc_root, path, &level); ret = walk_down_reloc_tree(reloc_root, path, &level);
...@@ -1689,14 +2053,9 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc, ...@@ -1689,14 +2053,9 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
if (!find_next_key(path, level, &key) && if (!find_next_key(path, level, &key) &&
btrfs_comp_cpu_keys(&next_key, &key) >= 0) { btrfs_comp_cpu_keys(&next_key, &key) >= 0) {
ret = 0; ret = 0;
} else if (level == 1 && rc->stage == UPDATE_DATA_PTRS) {
ret = replace_path(trans, root, reloc_root,
path, &next_key, &leaf,
level, max_level);
} else { } else {
ret = replace_path(trans, root, reloc_root, ret = replace_path(trans, root, reloc_root, path,
path, &next_key, NULL, &next_key, level, max_level);
level, max_level);
} }
if (ret < 0) { if (ret < 0) {
err = ret; err = ret;
...@@ -1708,16 +2067,6 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc, ...@@ -1708,16 +2067,6 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
btrfs_node_key_to_cpu(path->nodes[level], &key, btrfs_node_key_to_cpu(path->nodes[level], &key,
path->slots[level]); path->slots[level]);
replaced = 1; replaced = 1;
} else if (leaf) {
/*
* no block got replaced, try replacing file extents
*/
btrfs_item_key_to_cpu(leaf, &key, 0);
ret = replace_file_extents(trans, rc, root, leaf,
&inode_list);
btrfs_tree_unlock(leaf);
free_extent_buffer(leaf);
BUG_ON(ret < 0);
} }
ret = walk_up_reloc_tree(reloc_root, path, &level); ret = walk_up_reloc_tree(reloc_root, path, &level);
...@@ -1734,15 +2083,10 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc, ...@@ -1734,15 +2083,10 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
root_item->drop_level = level; root_item->drop_level = level;
nr = trans->blocks_used; nr = trans->blocks_used;
btrfs_end_transaction(trans, root); btrfs_end_transaction_throttle(trans, root);
btrfs_btree_balance_dirty(root, nr); btrfs_btree_balance_dirty(root, nr);
/*
* put inodes outside transaction, otherwise we may deadlock.
*/
put_inodes(&inode_list);
if (replaced && rc->stage == UPDATE_DATA_PTRS) if (replaced && rc->stage == UPDATE_DATA_PTRS)
invalidate_extent_cache(root, &key, &next_key); invalidate_extent_cache(root, &key, &next_key);
} }
...@@ -1765,87 +2109,125 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc, ...@@ -1765,87 +2109,125 @@ static noinline_for_stack int merge_reloc_root(struct reloc_control *rc,
sizeof(root_item->drop_progress)); sizeof(root_item->drop_progress));
root_item->drop_level = 0; root_item->drop_level = 0;
btrfs_set_root_refs(root_item, 0); btrfs_set_root_refs(root_item, 0);
btrfs_update_reloc_root(trans, root);
} }
nr = trans->blocks_used; nr = trans->blocks_used;
btrfs_end_transaction(trans, root); btrfs_end_transaction_throttle(trans, root);
btrfs_btree_balance_dirty(root, nr); btrfs_btree_balance_dirty(root, nr);
put_inodes(&inode_list);
if (replaced && rc->stage == UPDATE_DATA_PTRS) if (replaced && rc->stage == UPDATE_DATA_PTRS)
invalidate_extent_cache(root, &key, &next_key); invalidate_extent_cache(root, &key, &next_key);
return err; return err;
} }
/* static noinline_for_stack
* callback for the work threads. int prepare_to_merge(struct reloc_control *rc, int err)
* this function merges reloc tree with corresponding fs tree,
* and then drops the reloc tree.
*/
static void merge_func(struct btrfs_work *work)
{ {
struct btrfs_trans_handle *trans; struct btrfs_root *root = rc->extent_root;
struct btrfs_root *root;
struct btrfs_root *reloc_root; struct btrfs_root *reloc_root;
struct async_merge *async; struct btrfs_trans_handle *trans;
LIST_HEAD(reloc_roots);
u64 num_bytes = 0;
int ret;
int retries = 0;
mutex_lock(&root->fs_info->trans_mutex);
rc->merging_rsv_size += root->nodesize * (BTRFS_MAX_LEVEL - 1) * 2;
rc->merging_rsv_size += rc->nodes_relocated * 2;
mutex_unlock(&root->fs_info->trans_mutex);
again:
if (!err) {
num_bytes = rc->merging_rsv_size;
ret = btrfs_block_rsv_add(NULL, root, rc->block_rsv,
num_bytes, &retries);
if (ret)
err = ret;
}
trans = btrfs_join_transaction(rc->extent_root, 1);
if (!err) {
if (num_bytes != rc->merging_rsv_size) {
btrfs_end_transaction(trans, rc->extent_root);
btrfs_block_rsv_release(rc->extent_root,
rc->block_rsv, num_bytes);
retries = 0;
goto again;
}
}
async = container_of(work, struct async_merge, work); rc->merge_reloc_tree = 1;
reloc_root = async->root;
while (!list_empty(&rc->reloc_roots)) {
reloc_root = list_entry(rc->reloc_roots.next,
struct btrfs_root, root_list);
list_del_init(&reloc_root->root_list);
if (btrfs_root_refs(&reloc_root->root_item) > 0) {
root = read_fs_root(reloc_root->fs_info, root = read_fs_root(reloc_root->fs_info,
reloc_root->root_key.offset); reloc_root->root_key.offset);
BUG_ON(IS_ERR(root)); BUG_ON(IS_ERR(root));
BUG_ON(root->reloc_root != reloc_root); BUG_ON(root->reloc_root != reloc_root);
merge_reloc_root(async->rc, root); /*
* set reference count to 1, so btrfs_recover_relocation
trans = btrfs_start_transaction(root, 0); * knows it should resumes merging
*/
if (!err)
btrfs_set_root_refs(&reloc_root->root_item, 1);
btrfs_update_reloc_root(trans, root); btrfs_update_reloc_root(trans, root);
btrfs_end_transaction(trans, root);
}
btrfs_drop_snapshot(reloc_root, 0); list_add(&reloc_root->root_list, &reloc_roots);
}
if (atomic_dec_and_test(async->num_pending)) list_splice(&reloc_roots, &rc->reloc_roots);
complete(async->done);
kfree(async); if (!err)
btrfs_commit_transaction(trans, rc->extent_root);
else
btrfs_end_transaction(trans, rc->extent_root);
return err;
} }
static int merge_reloc_roots(struct reloc_control *rc) static noinline_for_stack
int merge_reloc_roots(struct reloc_control *rc)
{ {
struct async_merge *async;
struct btrfs_root *root; struct btrfs_root *root;
struct completion done; struct btrfs_root *reloc_root;
atomic_t num_pending; LIST_HEAD(reloc_roots);
int found = 0;
int ret;
again:
root = rc->extent_root;
mutex_lock(&root->fs_info->trans_mutex);
list_splice_init(&rc->reloc_roots, &reloc_roots);
mutex_unlock(&root->fs_info->trans_mutex);
init_completion(&done); while (!list_empty(&reloc_roots)) {
atomic_set(&num_pending, 1); found = 1;
reloc_root = list_entry(reloc_roots.next,
struct btrfs_root, root_list);
while (!list_empty(&rc->reloc_roots)) { if (btrfs_root_refs(&reloc_root->root_item) > 0) {
root = list_entry(rc->reloc_roots.next, root = read_fs_root(reloc_root->fs_info,
struct btrfs_root, root_list); reloc_root->root_key.offset);
list_del_init(&root->root_list); BUG_ON(IS_ERR(root));
BUG_ON(root->reloc_root != reloc_root);
async = kmalloc(sizeof(*async), GFP_NOFS); ret = merge_reloc_root(rc, root);
BUG_ON(!async); BUG_ON(ret);
async->work.func = merge_func; } else {
async->work.flags = 0; list_del_init(&reloc_root->root_list);
async->rc = rc; }
async->root = root; btrfs_drop_snapshot(reloc_root, rc->block_rsv, 0);
async->done = &done;
async->num_pending = &num_pending;
atomic_inc(&num_pending);
btrfs_queue_worker(&rc->workers, &async->work);
} }
if (!atomic_dec_and_test(&num_pending)) if (found) {
wait_for_completion(&done); found = 0;
goto again;
}
BUG_ON(!RB_EMPTY_ROOT(&rc->reloc_root_tree.rb_root)); BUG_ON(!RB_EMPTY_ROOT(&rc->reloc_root_tree.rb_root));
return 0; return 0;
} }
...@@ -1876,119 +2258,169 @@ static int record_reloc_root_in_trans(struct btrfs_trans_handle *trans, ...@@ -1876,119 +2258,169 @@ static int record_reloc_root_in_trans(struct btrfs_trans_handle *trans,
return btrfs_record_root_in_trans(trans, root); return btrfs_record_root_in_trans(trans, root);
} }
/* static noinline_for_stack
* select one tree from trees that references the block. struct btrfs_root *select_reloc_root(struct btrfs_trans_handle *trans,
* for blocks in refernce counted trees, we preper reloc tree. struct reloc_control *rc,
* if no reloc tree found and reloc_only is true, NULL is returned. struct backref_node *node,
*/ struct backref_edge *edges[], int *nr)
static struct btrfs_root *__select_one_root(struct btrfs_trans_handle *trans,
struct backref_node *node,
struct backref_edge *edges[],
int *nr, int reloc_only)
{ {
struct backref_node *next; struct backref_node *next;
struct btrfs_root *root; struct btrfs_root *root;
int index; int index = 0;
int loop = 0;
again:
index = 0;
next = node; next = node;
while (1) { while (1) {
cond_resched(); cond_resched();
next = walk_up_backref(next, edges, &index); next = walk_up_backref(next, edges, &index);
root = next->root; root = next->root;
if (!root) { BUG_ON(!root);
BUG_ON(!node->old_root); BUG_ON(!root->ref_cows);
goto skip;
}
/* no other choice for non-refernce counted tree */
if (!root->ref_cows) {
BUG_ON(reloc_only);
break;
}
if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) { if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) {
record_reloc_root_in_trans(trans, root); record_reloc_root_in_trans(trans, root);
break; break;
} }
if (loop) { btrfs_record_root_in_trans(trans, root);
btrfs_record_root_in_trans(trans, root); root = root->reloc_root;
if (next->new_bytenr != root->node->start) {
BUG_ON(next->new_bytenr);
BUG_ON(!list_empty(&next->list));
next->new_bytenr = root->node->start;
next->root = root;
list_add_tail(&next->list,
&rc->backref_cache.changed);
__mark_block_processed(rc, next);
break; break;
} }
if (reloc_only || next != node) { WARN_ON(1);
if (!root->reloc_root)
btrfs_record_root_in_trans(trans, root);
root = root->reloc_root;
/*
* if the reloc tree was created in current
* transation, there is no node in backref tree
* corresponds to the root of the reloc tree.
*/
if (btrfs_root_last_snapshot(&root->root_item) ==
trans->transid - 1)
break;
}
skip:
root = NULL; root = NULL;
next = walk_down_backref(edges, &index); next = walk_down_backref(edges, &index);
if (!next || next->level <= node->level) if (!next || next->level <= node->level)
break; break;
} }
if (!root)
return NULL;
if (!root && !loop && !reloc_only) { *nr = index;
loop = 1; next = node;
goto again; /* setup backref node path for btrfs_reloc_cow_block */
while (1) {
rc->backref_cache.path[next->level] = next;
if (--index < 0)
break;
next = edges[index]->node[UPPER];
} }
if (root)
*nr = index;
else
*nr = 0;
return root; return root;
} }
/*
* select a tree root for relocation. return NULL if the block
* is reference counted. we should use do_relocation() in this
* case. return a tree root pointer if the block isn't reference
* counted. return -ENOENT if the block is root of reloc tree.
*/
static noinline_for_stack static noinline_for_stack
struct btrfs_root *select_one_root(struct btrfs_trans_handle *trans, struct btrfs_root *select_one_root(struct btrfs_trans_handle *trans,
struct backref_node *node) struct backref_node *node)
{ {
struct backref_node *next;
struct btrfs_root *root;
struct btrfs_root *fs_root = NULL;
struct backref_edge *edges[BTRFS_MAX_LEVEL - 1]; struct backref_edge *edges[BTRFS_MAX_LEVEL - 1];
int nr; int index = 0;
return __select_one_root(trans, node, edges, &nr, 0);
next = node;
while (1) {
cond_resched();
next = walk_up_backref(next, edges, &index);
root = next->root;
BUG_ON(!root);
/* no other choice for non-refernce counted tree */
if (!root->ref_cows)
return root;
if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID)
fs_root = root;
if (next != node)
return NULL;
next = walk_down_backref(edges, &index);
if (!next || next->level <= node->level)
break;
}
if (!fs_root)
return ERR_PTR(-ENOENT);
return fs_root;
} }
static noinline_for_stack static noinline_for_stack
struct btrfs_root *select_reloc_root(struct btrfs_trans_handle *trans, u64 calcu_metadata_size(struct reloc_control *rc,
struct backref_node *node, struct backref_node *node, int reserve)
struct backref_edge *edges[], int *nr)
{ {
return __select_one_root(trans, node, edges, nr, 1); struct backref_node *next = node;
struct backref_edge *edge;
struct backref_edge *edges[BTRFS_MAX_LEVEL - 1];
u64 num_bytes = 0;
int index = 0;
BUG_ON(reserve && node->processed);
while (next) {
cond_resched();
while (1) {
if (next->processed && (reserve || next != node))
break;
num_bytes += btrfs_level_size(rc->extent_root,
next->level);
if (list_empty(&next->upper))
break;
edge = list_entry(next->upper.next,
struct backref_edge, list[LOWER]);
edges[index++] = edge;
next = edge->node[UPPER];
}
next = walk_down_backref(edges, &index);
}
return num_bytes;
} }
static void grab_path_buffers(struct btrfs_path *path, static int reserve_metadata_space(struct btrfs_trans_handle *trans,
struct backref_node *node, struct reloc_control *rc,
struct backref_edge *edges[], int nr) struct backref_node *node)
{ {
int i = 0; struct btrfs_root *root = rc->extent_root;
while (1) { u64 num_bytes;
drop_node_buffer(node); int ret;
node->eb = path->nodes[node->level];
BUG_ON(!node->eb); num_bytes = calcu_metadata_size(rc, node, 1) * 2;
if (path->locks[node->level])
node->locked = 1;
path->nodes[node->level] = NULL;
path->locks[node->level] = 0;
if (i >= nr)
break;
edges[i]->blockptr = node->eb->start; trans->block_rsv = rc->block_rsv;
node = edges[i]->node[UPPER]; ret = btrfs_block_rsv_add(trans, root, rc->block_rsv, num_bytes,
i++; &rc->block_rsv_retries);
if (ret) {
if (ret == -EAGAIN)
rc->commit_transaction = 1;
return ret;
} }
rc->block_rsv_retries = 0;
return 0;
}
static void release_metadata_space(struct reloc_control *rc,
struct backref_node *node)
{
u64 num_bytes = calcu_metadata_size(rc, node, 0) * 2;
btrfs_block_rsv_release(rc->extent_root, rc->block_rsv, num_bytes);
} }
/* /*
...@@ -1999,6 +2431,7 @@ static void grab_path_buffers(struct btrfs_path *path, ...@@ -1999,6 +2431,7 @@ static void grab_path_buffers(struct btrfs_path *path,
* in that case this function just updates pointers. * in that case this function just updates pointers.
*/ */
static int do_relocation(struct btrfs_trans_handle *trans, static int do_relocation(struct btrfs_trans_handle *trans,
struct reloc_control *rc,
struct backref_node *node, struct backref_node *node,
struct btrfs_key *key, struct btrfs_key *key,
struct btrfs_path *path, int lowest) struct btrfs_path *path, int lowest)
...@@ -2019,18 +2452,25 @@ static int do_relocation(struct btrfs_trans_handle *trans, ...@@ -2019,18 +2452,25 @@ static int do_relocation(struct btrfs_trans_handle *trans,
BUG_ON(lowest && node->eb); BUG_ON(lowest && node->eb);
path->lowest_level = node->level + 1; path->lowest_level = node->level + 1;
rc->backref_cache.path[node->level] = node;
list_for_each_entry(edge, &node->upper, list[LOWER]) { list_for_each_entry(edge, &node->upper, list[LOWER]) {
cond_resched(); cond_resched();
if (node->eb && node->eb->start == edge->blockptr)
continue;
upper = edge->node[UPPER]; upper = edge->node[UPPER];
root = select_reloc_root(trans, upper, edges, &nr); root = select_reloc_root(trans, rc, upper, edges, &nr);
if (!root) BUG_ON(!root);
continue;
if (upper->eb && !upper->locked) {
if (upper->eb && !upper->locked) if (!lowest) {
ret = btrfs_bin_search(upper->eb, key,
upper->level, &slot);
BUG_ON(ret);
bytenr = btrfs_node_blockptr(upper->eb, slot);
if (node->eb->start == bytenr)
goto next;
}
drop_node_buffer(upper); drop_node_buffer(upper);
}
if (!upper->eb) { if (!upper->eb) {
ret = btrfs_search_slot(trans, root, key, path, 0, 1); ret = btrfs_search_slot(trans, root, key, path, 0, 1);
...@@ -2040,11 +2480,17 @@ static int do_relocation(struct btrfs_trans_handle *trans, ...@@ -2040,11 +2480,17 @@ static int do_relocation(struct btrfs_trans_handle *trans,
} }
BUG_ON(ret > 0); BUG_ON(ret > 0);
slot = path->slots[upper->level]; if (!upper->eb) {
upper->eb = path->nodes[upper->level];
path->nodes[upper->level] = NULL;
} else {
BUG_ON(upper->eb != path->nodes[upper->level]);
}
btrfs_unlock_up_safe(path, upper->level + 1); upper->locked = 1;
grab_path_buffers(path, upper, edges, nr); path->locks[upper->level] = 0;
slot = path->slots[upper->level];
btrfs_release_path(NULL, path); btrfs_release_path(NULL, path);
} else { } else {
ret = btrfs_bin_search(upper->eb, key, upper->level, ret = btrfs_bin_search(upper->eb, key, upper->level,
...@@ -2053,14 +2499,11 @@ static int do_relocation(struct btrfs_trans_handle *trans, ...@@ -2053,14 +2499,11 @@ static int do_relocation(struct btrfs_trans_handle *trans,
} }
bytenr = btrfs_node_blockptr(upper->eb, slot); bytenr = btrfs_node_blockptr(upper->eb, slot);
if (!lowest) { if (lowest) {
if (node->eb->start == bytenr) { BUG_ON(bytenr != node->bytenr);
btrfs_tree_unlock(upper->eb);
upper->locked = 0;
continue;
}
} else { } else {
BUG_ON(node->bytenr != bytenr); if (node->eb->start == bytenr)
goto next;
} }
blocksize = btrfs_level_size(root, node->level); blocksize = btrfs_level_size(root, node->level);
...@@ -2072,13 +2515,13 @@ static int do_relocation(struct btrfs_trans_handle *trans, ...@@ -2072,13 +2515,13 @@ static int do_relocation(struct btrfs_trans_handle *trans,
if (!node->eb) { if (!node->eb) {
ret = btrfs_cow_block(trans, root, eb, upper->eb, ret = btrfs_cow_block(trans, root, eb, upper->eb,
slot, &eb); slot, &eb);
btrfs_tree_unlock(eb);
free_extent_buffer(eb);
if (ret < 0) { if (ret < 0) {
err = ret; err = ret;
break; goto next;
} }
btrfs_set_lock_blocking(eb); BUG_ON(node->eb != eb);
node->eb = eb;
node->locked = 1;
} else { } else {
btrfs_set_node_blockptr(upper->eb, slot, btrfs_set_node_blockptr(upper->eb, slot,
node->eb->start); node->eb->start);
...@@ -2096,67 +2539,80 @@ static int do_relocation(struct btrfs_trans_handle *trans, ...@@ -2096,67 +2539,80 @@ static int do_relocation(struct btrfs_trans_handle *trans,
ret = btrfs_drop_subtree(trans, root, eb, upper->eb); ret = btrfs_drop_subtree(trans, root, eb, upper->eb);
BUG_ON(ret); BUG_ON(ret);
} }
if (!lowest) { next:
btrfs_tree_unlock(upper->eb); if (!upper->pending)
upper->locked = 0; drop_node_buffer(upper);
} else
unlock_node_buffer(upper);
if (err)
break;
} }
if (!err && node->pending) {
drop_node_buffer(node);
list_move_tail(&node->list, &rc->backref_cache.changed);
node->pending = 0;
}
path->lowest_level = 0; path->lowest_level = 0;
BUG_ON(err == -ENOSPC);
return err; return err;
} }
static int link_to_upper(struct btrfs_trans_handle *trans, static int link_to_upper(struct btrfs_trans_handle *trans,
struct reloc_control *rc,
struct backref_node *node, struct backref_node *node,
struct btrfs_path *path) struct btrfs_path *path)
{ {
struct btrfs_key key; struct btrfs_key key;
if (!node->eb || list_empty(&node->upper))
return 0;
btrfs_node_key_to_cpu(node->eb, &key, 0); btrfs_node_key_to_cpu(node->eb, &key, 0);
return do_relocation(trans, node, &key, path, 0); return do_relocation(trans, rc, node, &key, path, 0);
} }
static int finish_pending_nodes(struct btrfs_trans_handle *trans, static int finish_pending_nodes(struct btrfs_trans_handle *trans,
struct backref_cache *cache, struct reloc_control *rc,
struct btrfs_path *path) struct btrfs_path *path, int err)
{ {
LIST_HEAD(list);
struct backref_cache *cache = &rc->backref_cache;
struct backref_node *node; struct backref_node *node;
int level; int level;
int ret; int ret;
int err = 0;
for (level = 0; level < BTRFS_MAX_LEVEL; level++) { for (level = 0; level < BTRFS_MAX_LEVEL; level++) {
while (!list_empty(&cache->pending[level])) { while (!list_empty(&cache->pending[level])) {
node = list_entry(cache->pending[level].next, node = list_entry(cache->pending[level].next,
struct backref_node, lower); struct backref_node, list);
BUG_ON(node->level != level); list_move_tail(&node->list, &list);
BUG_ON(!node->pending);
ret = link_to_upper(trans, node, path); if (!err) {
if (ret < 0) ret = link_to_upper(trans, rc, node, path);
err = ret; if (ret < 0)
/* err = ret;
* this remove the node from the pending list and }
* may add some other nodes to the level + 1
* pending list
*/
remove_backref_node(cache, node);
} }
list_splice_init(&list, &cache->pending[level]);
} }
BUG_ON(!RB_EMPTY_ROOT(&cache->rb_root));
return err; return err;
} }
static void mark_block_processed(struct reloc_control *rc, static void mark_block_processed(struct reloc_control *rc,
struct backref_node *node) u64 bytenr, u32 blocksize)
{
set_extent_bits(&rc->processed_blocks, bytenr, bytenr + blocksize - 1,
EXTENT_DIRTY, GFP_NOFS);
}
static void __mark_block_processed(struct reloc_control *rc,
struct backref_node *node)
{ {
u32 blocksize; u32 blocksize;
if (node->level == 0 || if (node->level == 0 ||
in_block_group(node->bytenr, rc->block_group)) { in_block_group(node->bytenr, rc->block_group)) {
blocksize = btrfs_level_size(rc->extent_root, node->level); blocksize = btrfs_level_size(rc->extent_root, node->level);
set_extent_bits(&rc->processed_blocks, node->bytenr, mark_block_processed(rc, node->bytenr, blocksize);
node->bytenr + blocksize - 1, EXTENT_DIRTY,
GFP_NOFS);
} }
node->processed = 1; node->processed = 1;
} }
...@@ -2179,7 +2635,7 @@ static void update_processed_blocks(struct reloc_control *rc, ...@@ -2179,7 +2635,7 @@ static void update_processed_blocks(struct reloc_control *rc,
if (next->processed) if (next->processed)
break; break;
mark_block_processed(rc, next); __mark_block_processed(rc, next);
if (list_empty(&next->upper)) if (list_empty(&next->upper))
break; break;
...@@ -2193,145 +2649,13 @@ static void update_processed_blocks(struct reloc_control *rc, ...@@ -2193,145 +2649,13 @@ static void update_processed_blocks(struct reloc_control *rc,
} }
} }
static int tree_block_processed(u64 bytenr, u32 blocksize, static int tree_block_processed(u64 bytenr, u32 blocksize,
struct reloc_control *rc) struct reloc_control *rc)
{ {
if (test_range_bit(&rc->processed_blocks, bytenr, if (test_range_bit(&rc->processed_blocks, bytenr,
bytenr + blocksize - 1, EXTENT_DIRTY, 1, NULL)) bytenr + blocksize - 1, EXTENT_DIRTY, 1, NULL))
return 1; return 1;
return 0; return 0;
}
/*
* check if there are any file extent pointers in the leaf point to
* data require processing
*/
static int check_file_extents(struct reloc_control *rc,
u64 bytenr, u32 blocksize, u64 ptr_gen)
{
struct btrfs_key found_key;
struct btrfs_file_extent_item *fi;
struct extent_buffer *leaf;
u32 nritems;
int i;
int ret = 0;
leaf = read_tree_block(rc->extent_root, bytenr, blocksize, ptr_gen);
nritems = btrfs_header_nritems(leaf);
for (i = 0; i < nritems; i++) {
cond_resched();
btrfs_item_key_to_cpu(leaf, &found_key, i);
if (found_key.type != BTRFS_EXTENT_DATA_KEY)
continue;
fi = btrfs_item_ptr(leaf, i, struct btrfs_file_extent_item);
if (btrfs_file_extent_type(leaf, fi) ==
BTRFS_FILE_EXTENT_INLINE)
continue;
bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
if (bytenr == 0)
continue;
if (in_block_group(bytenr, rc->block_group)) {
ret = 1;
break;
}
}
free_extent_buffer(leaf);
return ret;
}
/*
* scan child blocks of a given block to find blocks require processing
*/
static int add_child_blocks(struct btrfs_trans_handle *trans,
struct reloc_control *rc,
struct backref_node *node,
struct rb_root *blocks)
{
struct tree_block *block;
struct rb_node *rb_node;
u64 bytenr;
u64 ptr_gen;
u32 blocksize;
u32 nritems;
int i;
int err = 0;
nritems = btrfs_header_nritems(node->eb);
blocksize = btrfs_level_size(rc->extent_root, node->level - 1);
for (i = 0; i < nritems; i++) {
cond_resched();
bytenr = btrfs_node_blockptr(node->eb, i);
ptr_gen = btrfs_node_ptr_generation(node->eb, i);
if (ptr_gen == trans->transid)
continue;
if (!in_block_group(bytenr, rc->block_group) &&
(node->level > 1 || rc->stage == MOVE_DATA_EXTENTS))
continue;
if (tree_block_processed(bytenr, blocksize, rc))
continue;
readahead_tree_block(rc->extent_root,
bytenr, blocksize, ptr_gen);
}
for (i = 0; i < nritems; i++) {
cond_resched();
bytenr = btrfs_node_blockptr(node->eb, i);
ptr_gen = btrfs_node_ptr_generation(node->eb, i);
if (ptr_gen == trans->transid)
continue;
if (!in_block_group(bytenr, rc->block_group) &&
(node->level > 1 || rc->stage == MOVE_DATA_EXTENTS))
continue;
if (tree_block_processed(bytenr, blocksize, rc))
continue;
if (!in_block_group(bytenr, rc->block_group) &&
!check_file_extents(rc, bytenr, blocksize, ptr_gen))
continue;
block = kmalloc(sizeof(*block), GFP_NOFS);
if (!block) {
err = -ENOMEM;
break;
}
block->bytenr = bytenr;
btrfs_node_key_to_cpu(node->eb, &block->key, i);
block->level = node->level - 1;
block->key_ready = 1;
rb_node = tree_insert(blocks, block->bytenr, &block->rb_node);
BUG_ON(rb_node);
}
if (err)
free_block_list(blocks);
return err;
}
/*
* find adjacent blocks require processing
*/
static noinline_for_stack
int add_adjacent_blocks(struct btrfs_trans_handle *trans,
struct reloc_control *rc,
struct backref_cache *cache,
struct rb_root *blocks, int level,
struct backref_node **upper)
{
struct backref_node *node;
int ret = 0;
WARN_ON(!list_empty(&cache->pending[level]));
if (list_empty(&cache->pending[level + 1]))
return 1;
node = list_entry(cache->pending[level + 1].next,
struct backref_node, lower);
if (node->eb)
ret = add_child_blocks(trans, rc, node, blocks);
*upper = node;
return ret;
} }
static int get_tree_block_key(struct reloc_control *rc, static int get_tree_block_key(struct reloc_control *rc,
...@@ -2371,40 +2695,53 @@ static int relocate_tree_block(struct btrfs_trans_handle *trans, ...@@ -2371,40 +2695,53 @@ static int relocate_tree_block(struct btrfs_trans_handle *trans,
struct btrfs_path *path) struct btrfs_path *path)
{ {
struct btrfs_root *root; struct btrfs_root *root;
int ret; int release = 0;
int ret = 0;
if (!node)
return 0;
BUG_ON(node->processed);
root = select_one_root(trans, node); root = select_one_root(trans, node);
if (unlikely(!root)) { if (root == ERR_PTR(-ENOENT)) {
rc->found_old_snapshot = 1;
update_processed_blocks(rc, node); update_processed_blocks(rc, node);
return 0; goto out;
} }
if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID) { if (!root || root->ref_cows) {
ret = do_relocation(trans, node, key, path, 1); ret = reserve_metadata_space(trans, rc, node);
if (ret < 0) if (ret)
goto out;
if (node->level == 0 && rc->stage == UPDATE_DATA_PTRS) {
ret = replace_file_extents(trans, rc, root,
node->eb, NULL);
if (ret < 0)
goto out;
}
drop_node_buffer(node);
} else if (!root->ref_cows) {
path->lowest_level = node->level;
ret = btrfs_search_slot(trans, root, key, path, 0, 1);
btrfs_release_path(root, path);
if (ret < 0)
goto out; goto out;
} else if (root != node->root) { release = 1;
WARN_ON(node->level > 0 || rc->stage != UPDATE_DATA_PTRS);
} }
update_processed_blocks(rc, node); if (root) {
ret = 0; if (root->ref_cows) {
BUG_ON(node->new_bytenr);
BUG_ON(!list_empty(&node->list));
btrfs_record_root_in_trans(trans, root);
root = root->reloc_root;
node->new_bytenr = root->node->start;
node->root = root;
list_add_tail(&node->list, &rc->backref_cache.changed);
} else {
path->lowest_level = node->level;
ret = btrfs_search_slot(trans, root, key, path, 0, 1);
btrfs_release_path(root, path);
if (ret > 0)
ret = 0;
}
if (!ret)
update_processed_blocks(rc, node);
} else {
ret = do_relocation(trans, rc, node, key, path, 1);
}
out: out:
drop_node_buffer(node); if (ret || node->level == 0 || node->cowonly) {
if (release)
release_metadata_space(rc, node);
remove_backref_node(&rc->backref_cache, node);
}
return ret; return ret;
} }
...@@ -2415,12 +2752,10 @@ static noinline_for_stack ...@@ -2415,12 +2752,10 @@ static noinline_for_stack
int relocate_tree_blocks(struct btrfs_trans_handle *trans, int relocate_tree_blocks(struct btrfs_trans_handle *trans,
struct reloc_control *rc, struct rb_root *blocks) struct reloc_control *rc, struct rb_root *blocks)
{ {
struct backref_cache *cache;
struct backref_node *node; struct backref_node *node;
struct btrfs_path *path; struct btrfs_path *path;
struct tree_block *block; struct tree_block *block;
struct rb_node *rb_node; struct rb_node *rb_node;
int level = -1;
int ret; int ret;
int err = 0; int err = 0;
...@@ -2428,21 +2763,9 @@ int relocate_tree_blocks(struct btrfs_trans_handle *trans, ...@@ -2428,21 +2763,9 @@ int relocate_tree_blocks(struct btrfs_trans_handle *trans,
if (!path) if (!path)
return -ENOMEM; return -ENOMEM;
cache = kmalloc(sizeof(*cache), GFP_NOFS);
if (!cache) {
btrfs_free_path(path);
return -ENOMEM;
}
backref_cache_init(cache);
rb_node = rb_first(blocks); rb_node = rb_first(blocks);
while (rb_node) { while (rb_node) {
block = rb_entry(rb_node, struct tree_block, rb_node); block = rb_entry(rb_node, struct tree_block, rb_node);
if (level == -1)
level = block->level;
else
BUG_ON(level != block->level);
if (!block->key_ready) if (!block->key_ready)
reada_tree_block(rc, block); reada_tree_block(rc, block);
rb_node = rb_next(rb_node); rb_node = rb_next(rb_node);
...@@ -2460,7 +2783,7 @@ int relocate_tree_blocks(struct btrfs_trans_handle *trans, ...@@ -2460,7 +2783,7 @@ int relocate_tree_blocks(struct btrfs_trans_handle *trans,
while (rb_node) { while (rb_node) {
block = rb_entry(rb_node, struct tree_block, rb_node); block = rb_entry(rb_node, struct tree_block, rb_node);
node = build_backref_tree(rc, cache, &block->key, node = build_backref_tree(rc, &block->key,
block->level, block->bytenr); block->level, block->bytenr);
if (IS_ERR(node)) { if (IS_ERR(node)) {
err = PTR_ERR(node); err = PTR_ERR(node);
...@@ -2470,77 +2793,16 @@ int relocate_tree_blocks(struct btrfs_trans_handle *trans, ...@@ -2470,77 +2793,16 @@ int relocate_tree_blocks(struct btrfs_trans_handle *trans,
ret = relocate_tree_block(trans, rc, node, &block->key, ret = relocate_tree_block(trans, rc, node, &block->key,
path); path);
if (ret < 0) { if (ret < 0) {
err = ret; if (ret != -EAGAIN || rb_node == rb_first(blocks))
err = ret;
goto out; goto out;
} }
remove_backref_node(cache, node);
rb_node = rb_next(rb_node); rb_node = rb_next(rb_node);
} }
if (level > 0)
goto out;
free_block_list(blocks);
/*
* now backrefs of some upper level tree blocks have been cached,
* try relocating blocks referenced by these upper level blocks.
*/
while (1) {
struct backref_node *upper = NULL;
if (trans->transaction->in_commit ||
trans->transaction->delayed_refs.flushing)
break;
ret = add_adjacent_blocks(trans, rc, cache, blocks, level,
&upper);
if (ret < 0)
err = ret;
if (ret != 0)
break;
rb_node = rb_first(blocks);
while (rb_node) {
block = rb_entry(rb_node, struct tree_block, rb_node);
if (trans->transaction->in_commit ||
trans->transaction->delayed_refs.flushing)
goto out;
BUG_ON(!block->key_ready);
node = build_backref_tree(rc, cache, &block->key,
level, block->bytenr);
if (IS_ERR(node)) {
err = PTR_ERR(node);
goto out;
}
ret = relocate_tree_block(trans, rc, node,
&block->key, path);
if (ret < 0) {
err = ret;
goto out;
}
remove_backref_node(cache, node);
rb_node = rb_next(rb_node);
}
free_block_list(blocks);
if (upper) {
ret = link_to_upper(trans, upper, path);
if (ret < 0) {
err = ret;
break;
}
remove_backref_node(cache, upper);
}
}
out: out:
free_block_list(blocks); free_block_list(blocks);
err = finish_pending_nodes(trans, rc, path, err);
ret = finish_pending_nodes(trans, cache, path);
if (ret < 0)
err = ret;
kfree(cache);
btrfs_free_path(path); btrfs_free_path(path);
return err; return err;
} }
...@@ -2910,9 +3172,6 @@ static int __add_tree_block(struct reloc_control *rc, ...@@ -2910,9 +3172,6 @@ static int __add_tree_block(struct reloc_control *rc,
static int block_use_full_backref(struct reloc_control *rc, static int block_use_full_backref(struct reloc_control *rc,
struct extent_buffer *eb) struct extent_buffer *eb)
{ {
struct btrfs_path *path;
struct btrfs_extent_item *ei;
struct btrfs_key key;
u64 flags; u64 flags;
int ret; int ret;
...@@ -2920,28 +3179,14 @@ static int block_use_full_backref(struct reloc_control *rc, ...@@ -2920,28 +3179,14 @@ static int block_use_full_backref(struct reloc_control *rc,
btrfs_header_backref_rev(eb) < BTRFS_MIXED_BACKREF_REV) btrfs_header_backref_rev(eb) < BTRFS_MIXED_BACKREF_REV)
return 1; return 1;
path = btrfs_alloc_path(); ret = btrfs_lookup_extent_info(NULL, rc->extent_root,
BUG_ON(!path); eb->start, eb->len, NULL, &flags);
key.objectid = eb->start;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = eb->len;
path->search_commit_root = 1;
path->skip_locking = 1;
ret = btrfs_search_slot(NULL, rc->extent_root,
&key, path, 0, 0);
BUG_ON(ret); BUG_ON(ret);
ei = btrfs_item_ptr(path->nodes[0], path->slots[0],
struct btrfs_extent_item);
flags = btrfs_extent_flags(path->nodes[0], ei);
BUG_ON(!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK));
if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF) if (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)
ret = 1; ret = 1;
else else
ret = 0; ret = 0;
btrfs_free_path(path);
return ret; return ret;
} }
...@@ -3114,22 +3359,10 @@ int add_data_references(struct reloc_control *rc, ...@@ -3114,22 +3359,10 @@ int add_data_references(struct reloc_control *rc,
struct btrfs_extent_inline_ref *iref; struct btrfs_extent_inline_ref *iref;
unsigned long ptr; unsigned long ptr;
unsigned long end; unsigned long end;
u32 blocksize; u32 blocksize = btrfs_level_size(rc->extent_root, 0);
int ret; int ret;
int err = 0; int err = 0;
ret = get_new_location(rc->data_inode, NULL, extent_key->objectid,
extent_key->offset);
BUG_ON(ret < 0);
if (ret > 0) {
/* the relocated data is fragmented */
rc->extents_skipped++;
btrfs_release_path(rc->extent_root, path);
return 0;
}
blocksize = btrfs_level_size(rc->extent_root, 0);
eb = path->nodes[0]; eb = path->nodes[0];
ptr = btrfs_item_ptr_offset(eb, path->slots[0]); ptr = btrfs_item_ptr_offset(eb, path->slots[0]);
end = ptr + btrfs_item_size_nr(eb, path->slots[0]); end = ptr + btrfs_item_size_nr(eb, path->slots[0]);
...@@ -3210,7 +3443,8 @@ int add_data_references(struct reloc_control *rc, ...@@ -3210,7 +3443,8 @@ int add_data_references(struct reloc_control *rc,
*/ */
static noinline_for_stack static noinline_for_stack
int find_next_extent(struct btrfs_trans_handle *trans, int find_next_extent(struct btrfs_trans_handle *trans,
struct reloc_control *rc, struct btrfs_path *path) struct reloc_control *rc, struct btrfs_path *path,
struct btrfs_key *extent_key)
{ {
struct btrfs_key key; struct btrfs_key key;
struct extent_buffer *leaf; struct extent_buffer *leaf;
...@@ -3265,6 +3499,7 @@ int find_next_extent(struct btrfs_trans_handle *trans, ...@@ -3265,6 +3499,7 @@ int find_next_extent(struct btrfs_trans_handle *trans,
rc->search_start = end + 1; rc->search_start = end + 1;
} else { } else {
rc->search_start = key.objectid + key.offset; rc->search_start = key.objectid + key.offset;
memcpy(extent_key, &key, sizeof(key));
return 0; return 0;
} }
} }
...@@ -3302,12 +3537,49 @@ static int check_extent_flags(u64 flags) ...@@ -3302,12 +3537,49 @@ static int check_extent_flags(u64 flags)
return 0; return 0;
} }
static noinline_for_stack
int prepare_to_relocate(struct reloc_control *rc)
{
struct btrfs_trans_handle *trans;
int ret;
rc->block_rsv = btrfs_alloc_block_rsv(rc->extent_root);
if (!rc->block_rsv)
return -ENOMEM;
/*
* reserve some space for creating reloc trees.
* btrfs_init_reloc_root will use them when there
* is no reservation in transaction handle.
*/
ret = btrfs_block_rsv_add(NULL, rc->extent_root, rc->block_rsv,
rc->extent_root->nodesize * 256,
&rc->block_rsv_retries);
if (ret)
return ret;
rc->block_rsv->refill_used = 1;
btrfs_add_durable_block_rsv(rc->extent_root->fs_info, rc->block_rsv);
memset(&rc->cluster, 0, sizeof(rc->cluster));
rc->search_start = rc->block_group->key.objectid;
rc->extents_found = 0;
rc->nodes_relocated = 0;
rc->merging_rsv_size = 0;
rc->block_rsv_retries = 0;
rc->create_reloc_tree = 1;
set_reloc_control(rc);
trans = btrfs_join_transaction(rc->extent_root, 1);
btrfs_commit_transaction(trans, rc->extent_root);
return 0;
}
static noinline_for_stack int relocate_block_group(struct reloc_control *rc) static noinline_for_stack int relocate_block_group(struct reloc_control *rc)
{ {
struct rb_root blocks = RB_ROOT; struct rb_root blocks = RB_ROOT;
struct btrfs_key key; struct btrfs_key key;
struct file_extent_cluster *cluster;
struct btrfs_trans_handle *trans = NULL; struct btrfs_trans_handle *trans = NULL;
struct btrfs_path *path; struct btrfs_path *path;
struct btrfs_extent_item *ei; struct btrfs_extent_item *ei;
...@@ -3317,33 +3589,25 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc) ...@@ -3317,33 +3589,25 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc)
int ret; int ret;
int err = 0; int err = 0;
cluster = kzalloc(sizeof(*cluster), GFP_NOFS);
if (!cluster)
return -ENOMEM;
path = btrfs_alloc_path(); path = btrfs_alloc_path();
if (!path) { if (!path)
kfree(cluster);
return -ENOMEM; return -ENOMEM;
}
rc->extents_found = 0;
rc->extents_skipped = 0;
rc->search_start = rc->block_group->key.objectid;
clear_extent_bits(&rc->processed_blocks, 0, (u64)-1, EXTENT_DIRTY,
GFP_NOFS);
rc->create_reloc_root = 1;
set_reloc_control(rc);
trans = btrfs_join_transaction(rc->extent_root, 1); ret = prepare_to_relocate(rc);
btrfs_commit_transaction(trans, rc->extent_root); if (ret) {
err = ret;
goto out_free;
}
while (1) { while (1) {
trans = btrfs_start_transaction(rc->extent_root, 0); trans = btrfs_start_transaction(rc->extent_root, 0);
ret = find_next_extent(trans, rc, path); if (update_backref_cache(trans, &rc->backref_cache)) {
btrfs_end_transaction(trans, rc->extent_root);
continue;
}
ret = find_next_extent(trans, rc, path, &key);
if (ret < 0) if (ret < 0)
err = ret; err = ret;
if (ret != 0) if (ret != 0)
...@@ -3353,9 +3617,7 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc) ...@@ -3353,9 +3617,7 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc)
ei = btrfs_item_ptr(path->nodes[0], path->slots[0], ei = btrfs_item_ptr(path->nodes[0], path->slots[0],
struct btrfs_extent_item); struct btrfs_extent_item);
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); item_size = btrfs_item_size_nr(path->nodes[0], path->slots[0]);
item_size = btrfs_item_size_nr(path->nodes[0],
path->slots[0]);
if (item_size >= sizeof(*ei)) { if (item_size >= sizeof(*ei)) {
flags = btrfs_extent_flags(path->nodes[0], ei); flags = btrfs_extent_flags(path->nodes[0], ei);
ret = check_extent_flags(flags); ret = check_extent_flags(flags);
...@@ -3396,73 +3658,100 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc) ...@@ -3396,73 +3658,100 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc)
if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
ret = add_tree_block(rc, &key, path, &blocks); ret = add_tree_block(rc, &key, path, &blocks);
} else if (rc->stage == UPDATE_DATA_PTRS && } else if (rc->stage == UPDATE_DATA_PTRS &&
(flags & BTRFS_EXTENT_FLAG_DATA)) { (flags & BTRFS_EXTENT_FLAG_DATA)) {
ret = add_data_references(rc, &key, path, &blocks); ret = add_data_references(rc, &key, path, &blocks);
} else { } else {
btrfs_release_path(rc->extent_root, path); btrfs_release_path(rc->extent_root, path);
ret = 0; ret = 0;
} }
if (ret < 0) { if (ret < 0) {
err = 0; err = ret;
break; break;
} }
if (!RB_EMPTY_ROOT(&blocks)) { if (!RB_EMPTY_ROOT(&blocks)) {
ret = relocate_tree_blocks(trans, rc, &blocks); ret = relocate_tree_blocks(trans, rc, &blocks);
if (ret < 0) { if (ret < 0) {
if (ret != -EAGAIN) {
err = ret;
break;
}
rc->extents_found--;
rc->search_start = key.objectid;
}
}
ret = btrfs_block_rsv_check(trans, rc->extent_root,
rc->block_rsv, 0, 5);
if (ret < 0) {
if (ret != -EAGAIN) {
err = ret; err = ret;
WARN_ON(1);
break; break;
} }
rc->commit_transaction = 1;
} }
nr = trans->blocks_used; if (rc->commit_transaction) {
btrfs_end_transaction(trans, rc->extent_root); rc->commit_transaction = 0;
ret = btrfs_commit_transaction(trans, rc->extent_root);
BUG_ON(ret);
} else {
nr = trans->blocks_used;
btrfs_end_transaction_throttle(trans, rc->extent_root);
btrfs_btree_balance_dirty(rc->extent_root, nr);
}
trans = NULL; trans = NULL;
btrfs_btree_balance_dirty(rc->extent_root, nr);
if (rc->stage == MOVE_DATA_EXTENTS && if (rc->stage == MOVE_DATA_EXTENTS &&
(flags & BTRFS_EXTENT_FLAG_DATA)) { (flags & BTRFS_EXTENT_FLAG_DATA)) {
rc->found_file_extent = 1; rc->found_file_extent = 1;
ret = relocate_data_extent(rc->data_inode, ret = relocate_data_extent(rc->data_inode,
&key, cluster); &key, &rc->cluster);
if (ret < 0) { if (ret < 0) {
err = ret; err = ret;
break; break;
} }
} }
} }
btrfs_free_path(path);
btrfs_release_path(rc->extent_root, path);
clear_extent_bits(&rc->processed_blocks, 0, (u64)-1, EXTENT_DIRTY,
GFP_NOFS);
if (trans) { if (trans) {
nr = trans->blocks_used; nr = trans->blocks_used;
btrfs_end_transaction(trans, rc->extent_root); btrfs_end_transaction_throttle(trans, rc->extent_root);
btrfs_btree_balance_dirty(rc->extent_root, nr); btrfs_btree_balance_dirty(rc->extent_root, nr);
} }
if (!err) { if (!err) {
ret = relocate_file_extent_cluster(rc->data_inode, cluster); ret = relocate_file_extent_cluster(rc->data_inode,
&rc->cluster);
if (ret < 0) if (ret < 0)
err = ret; err = ret;
} }
kfree(cluster); rc->create_reloc_tree = 0;
set_reloc_control(rc);
rc->create_reloc_root = 0; backref_cache_cleanup(&rc->backref_cache);
smp_mb(); btrfs_block_rsv_release(rc->extent_root, rc->block_rsv, (u64)-1);
if (rc->extents_found > 0) { err = prepare_to_merge(rc, err);
trans = btrfs_join_transaction(rc->extent_root, 1);
btrfs_commit_transaction(trans, rc->extent_root);
}
merge_reloc_roots(rc); merge_reloc_roots(rc);
rc->merge_reloc_tree = 0;
unset_reloc_control(rc); unset_reloc_control(rc);
btrfs_block_rsv_release(rc->extent_root, rc->block_rsv, (u64)-1);
/* get rid of pinned extents */ /* get rid of pinned extents */
trans = btrfs_join_transaction(rc->extent_root, 1); trans = btrfs_join_transaction(rc->extent_root, 1);
btrfs_commit_transaction(trans, rc->extent_root); btrfs_commit_transaction(trans, rc->extent_root);
out_free:
btrfs_free_block_rsv(rc->extent_root, rc->block_rsv);
btrfs_free_path(path);
return err; return err;
} }
...@@ -3488,7 +3777,8 @@ static int __insert_orphan_inode(struct btrfs_trans_handle *trans, ...@@ -3488,7 +3777,8 @@ static int __insert_orphan_inode(struct btrfs_trans_handle *trans,
btrfs_set_inode_generation(leaf, item, 1); btrfs_set_inode_generation(leaf, item, 1);
btrfs_set_inode_size(leaf, item, 0); btrfs_set_inode_size(leaf, item, 0);
btrfs_set_inode_mode(leaf, item, S_IFREG | 0600); btrfs_set_inode_mode(leaf, item, S_IFREG | 0600);
btrfs_set_inode_flags(leaf, item, BTRFS_INODE_NOCOMPRESS); btrfs_set_inode_flags(leaf, item, BTRFS_INODE_NOCOMPRESS |
BTRFS_INODE_PREALLOC);
btrfs_mark_buffer_dirty(leaf); btrfs_mark_buffer_dirty(leaf);
btrfs_release_path(root, path); btrfs_release_path(root, path);
out: out:
...@@ -3500,8 +3790,9 @@ static int __insert_orphan_inode(struct btrfs_trans_handle *trans, ...@@ -3500,8 +3790,9 @@ static int __insert_orphan_inode(struct btrfs_trans_handle *trans,
* helper to create inode for data relocation. * helper to create inode for data relocation.
* the inode is in data relocation tree and its link count is 0 * the inode is in data relocation tree and its link count is 0
*/ */
static struct inode *create_reloc_inode(struct btrfs_fs_info *fs_info, static noinline_for_stack
struct btrfs_block_group_cache *group) struct inode *create_reloc_inode(struct btrfs_fs_info *fs_info,
struct btrfs_block_group_cache *group)
{ {
struct inode *inode = NULL; struct inode *inode = NULL;
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans;
...@@ -3516,7 +3807,8 @@ static struct inode *create_reloc_inode(struct btrfs_fs_info *fs_info, ...@@ -3516,7 +3807,8 @@ static struct inode *create_reloc_inode(struct btrfs_fs_info *fs_info,
return ERR_CAST(root); return ERR_CAST(root);
trans = btrfs_start_transaction(root, 6); trans = btrfs_start_transaction(root, 6);
BUG_ON(!trans); if (IS_ERR(trans))
return ERR_CAST(trans);
err = btrfs_find_free_objectid(trans, root, objectid, &objectid); err = btrfs_find_free_objectid(trans, root, objectid, &objectid);
if (err) if (err)
...@@ -3536,7 +3828,6 @@ static struct inode *create_reloc_inode(struct btrfs_fs_info *fs_info, ...@@ -3536,7 +3828,6 @@ static struct inode *create_reloc_inode(struct btrfs_fs_info *fs_info,
out: out:
nr = trans->blocks_used; nr = trans->blocks_used;
btrfs_end_transaction(trans, root); btrfs_end_transaction(trans, root);
btrfs_btree_balance_dirty(root, nr); btrfs_btree_balance_dirty(root, nr);
if (err) { if (err) {
if (inode) if (inode)
...@@ -3546,6 +3837,21 @@ static struct inode *create_reloc_inode(struct btrfs_fs_info *fs_info, ...@@ -3546,6 +3837,21 @@ static struct inode *create_reloc_inode(struct btrfs_fs_info *fs_info,
return inode; return inode;
} }
static struct reloc_control *alloc_reloc_control(void)
{
struct reloc_control *rc;
rc = kzalloc(sizeof(*rc), GFP_NOFS);
if (!rc)
return NULL;
INIT_LIST_HEAD(&rc->reloc_roots);
backref_cache_init(&rc->backref_cache);
mapping_tree_init(&rc->reloc_root_tree);
extent_io_tree_init(&rc->processed_blocks, NULL, GFP_NOFS);
return rc;
}
/* /*
* function to relocate all extents in a block group. * function to relocate all extents in a block group.
*/ */
...@@ -3557,15 +3863,12 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start) ...@@ -3557,15 +3863,12 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
int rw = 0; int rw = 0;
int err = 0; int err = 0;
rc = kzalloc(sizeof(*rc), GFP_NOFS); rc = alloc_reloc_control();
if (!rc) if (!rc)
return -ENOMEM; return -ENOMEM;
mapping_tree_init(&rc->reloc_root_tree);
extent_io_tree_init(&rc->processed_blocks, NULL, GFP_NOFS);
INIT_LIST_HEAD(&rc->reloc_roots);
rc->extent_root = extent_root; rc->extent_root = extent_root;
rc->block_group = btrfs_lookup_block_group(fs_info, group_start); rc->block_group = btrfs_lookup_block_group(fs_info, group_start);
BUG_ON(!rc->block_group); BUG_ON(!rc->block_group);
...@@ -3578,9 +3881,6 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start) ...@@ -3578,9 +3881,6 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
rw = 1; rw = 1;
} }
btrfs_init_workers(&rc->workers, "relocate",
fs_info->thread_pool_size, NULL);
rc->data_inode = create_reloc_inode(fs_info, rc->block_group); rc->data_inode = create_reloc_inode(fs_info, rc->block_group);
if (IS_ERR(rc->data_inode)) { if (IS_ERR(rc->data_inode)) {
err = PTR_ERR(rc->data_inode); err = PTR_ERR(rc->data_inode);
...@@ -3596,9 +3896,6 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start) ...@@ -3596,9 +3896,6 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
btrfs_wait_ordered_extents(fs_info->tree_root, 0, 0); btrfs_wait_ordered_extents(fs_info->tree_root, 0, 0);
while (1) { while (1) {
rc->extents_found = 0;
rc->extents_skipped = 0;
mutex_lock(&fs_info->cleaner_mutex); mutex_lock(&fs_info->cleaner_mutex);
btrfs_clean_old_snapshots(fs_info->tree_root); btrfs_clean_old_snapshots(fs_info->tree_root);
...@@ -3607,7 +3904,7 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start) ...@@ -3607,7 +3904,7 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
mutex_unlock(&fs_info->cleaner_mutex); mutex_unlock(&fs_info->cleaner_mutex);
if (ret < 0) { if (ret < 0) {
err = ret; err = ret;
break; goto out;
} }
if (rc->extents_found == 0) if (rc->extents_found == 0)
...@@ -3621,18 +3918,6 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start) ...@@ -3621,18 +3918,6 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
invalidate_mapping_pages(rc->data_inode->i_mapping, invalidate_mapping_pages(rc->data_inode->i_mapping,
0, -1); 0, -1);
rc->stage = UPDATE_DATA_PTRS; rc->stage = UPDATE_DATA_PTRS;
} else if (rc->stage == UPDATE_DATA_PTRS &&
rc->extents_skipped >= rc->extents_found) {
iput(rc->data_inode);
rc->data_inode = create_reloc_inode(fs_info,
rc->block_group);
if (IS_ERR(rc->data_inode)) {
err = PTR_ERR(rc->data_inode);
rc->data_inode = NULL;
break;
}
rc->stage = MOVE_DATA_EXTENTS;
rc->found_file_extent = 0;
} }
} }
...@@ -3648,7 +3933,6 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start) ...@@ -3648,7 +3933,6 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
if (err && rw) if (err && rw)
btrfs_set_block_group_rw(extent_root, rc->block_group); btrfs_set_block_group_rw(extent_root, rc->block_group);
iput(rc->data_inode); iput(rc->data_inode);
btrfs_stop_workers(&rc->workers);
btrfs_put_block_group(rc->block_group); btrfs_put_block_group(rc->block_group);
kfree(rc); kfree(rc);
return err; return err;
...@@ -3752,20 +4036,20 @@ int btrfs_recover_relocation(struct btrfs_root *root) ...@@ -3752,20 +4036,20 @@ int btrfs_recover_relocation(struct btrfs_root *root)
if (list_empty(&reloc_roots)) if (list_empty(&reloc_roots))
goto out; goto out;
rc = kzalloc(sizeof(*rc), GFP_NOFS); rc = alloc_reloc_control();
if (!rc) { if (!rc) {
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
} }
mapping_tree_init(&rc->reloc_root_tree);
INIT_LIST_HEAD(&rc->reloc_roots);
btrfs_init_workers(&rc->workers, "relocate",
root->fs_info->thread_pool_size, NULL);
rc->extent_root = root->fs_info->extent_root; rc->extent_root = root->fs_info->extent_root;
set_reloc_control(rc); set_reloc_control(rc);
trans = btrfs_join_transaction(rc->extent_root, 1);
rc->merge_reloc_tree = 1;
while (!list_empty(&reloc_roots)) { while (!list_empty(&reloc_roots)) {
reloc_root = list_entry(reloc_roots.next, reloc_root = list_entry(reloc_roots.next,
struct btrfs_root, root_list); struct btrfs_root, root_list);
...@@ -3785,20 +4069,16 @@ int btrfs_recover_relocation(struct btrfs_root *root) ...@@ -3785,20 +4069,16 @@ int btrfs_recover_relocation(struct btrfs_root *root)
fs_root->reloc_root = reloc_root; fs_root->reloc_root = reloc_root;
} }
trans = btrfs_start_transaction(rc->extent_root, 1);
btrfs_commit_transaction(trans, rc->extent_root); btrfs_commit_transaction(trans, rc->extent_root);
merge_reloc_roots(rc); merge_reloc_roots(rc);
unset_reloc_control(rc); unset_reloc_control(rc);
trans = btrfs_start_transaction(rc->extent_root, 1); trans = btrfs_join_transaction(rc->extent_root, 1);
btrfs_commit_transaction(trans, rc->extent_root); btrfs_commit_transaction(trans, rc->extent_root);
out: out:
if (rc) { kfree(rc);
btrfs_stop_workers(&rc->workers);
kfree(rc);
}
while (!list_empty(&reloc_roots)) { while (!list_empty(&reloc_roots)) {
reloc_root = list_entry(reloc_roots.next, reloc_root = list_entry(reloc_roots.next,
struct btrfs_root, root_list); struct btrfs_root, root_list);
...@@ -3864,3 +4144,130 @@ int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len) ...@@ -3864,3 +4144,130 @@ int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len)
btrfs_put_ordered_extent(ordered); btrfs_put_ordered_extent(ordered);
return 0; return 0;
} }
void btrfs_reloc_cow_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct extent_buffer *buf,
struct extent_buffer *cow)
{
struct reloc_control *rc;
struct backref_node *node;
int first_cow = 0;
int level;
int ret;
rc = root->fs_info->reloc_ctl;
if (!rc)
return;
BUG_ON(rc->stage == UPDATE_DATA_PTRS &&
root->root_key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID);
level = btrfs_header_level(buf);
if (btrfs_header_generation(buf) <=
btrfs_root_last_snapshot(&root->root_item))
first_cow = 1;
if (root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID &&
rc->create_reloc_tree) {
WARN_ON(!first_cow && level == 0);
node = rc->backref_cache.path[level];
BUG_ON(node->bytenr != buf->start &&
node->new_bytenr != buf->start);
drop_node_buffer(node);
extent_buffer_get(cow);
node->eb = cow;
node->new_bytenr = cow->start;
if (!node->pending) {
list_move_tail(&node->list,
&rc->backref_cache.pending[level]);
node->pending = 1;
}
if (first_cow)
__mark_block_processed(rc, node);
if (first_cow && level > 0)
rc->nodes_relocated += buf->len;
}
if (level == 0 && first_cow && rc->stage == UPDATE_DATA_PTRS) {
ret = replace_file_extents(trans, rc, root, cow);
BUG_ON(ret);
}
}
/*
* called before creating snapshot. it calculates metadata reservation
* requried for relocating tree blocks in the snapshot
*/
void btrfs_reloc_pre_snapshot(struct btrfs_trans_handle *trans,
struct btrfs_pending_snapshot *pending,
u64 *bytes_to_reserve)
{
struct btrfs_root *root;
struct reloc_control *rc;
root = pending->root;
if (!root->reloc_root)
return;
rc = root->fs_info->reloc_ctl;
if (!rc->merge_reloc_tree)
return;
root = root->reloc_root;
BUG_ON(btrfs_root_refs(&root->root_item) == 0);
/*
* relocation is in the stage of merging trees. the space
* used by merging a reloc tree is twice the size of
* relocated tree nodes in the worst case. half for cowing
* the reloc tree, half for cowing the fs tree. the space
* used by cowing the reloc tree will be freed after the
* tree is dropped. if we create snapshot, cowing the fs
* tree may use more space than it frees. so we need
* reserve extra space.
*/
*bytes_to_reserve += rc->nodes_relocated;
}
/*
* called after snapshot is created. migrate block reservation
* and create reloc root for the newly created snapshot
*/
void btrfs_reloc_post_snapshot(struct btrfs_trans_handle *trans,
struct btrfs_pending_snapshot *pending)
{
struct btrfs_root *root = pending->root;
struct btrfs_root *reloc_root;
struct btrfs_root *new_root;
struct reloc_control *rc;
int ret;
if (!root->reloc_root)
return;
rc = root->fs_info->reloc_ctl;
rc->merging_rsv_size += rc->nodes_relocated;
if (rc->merge_reloc_tree) {
ret = btrfs_block_rsv_migrate(&pending->block_rsv,
rc->block_rsv,
rc->nodes_relocated);
BUG_ON(ret);
}
new_root = pending->snap;
reloc_root = create_reloc_root(trans, root->reloc_root,
new_root->root_key.objectid);
__add_reloc_root(reloc_root);
new_root->reloc_root = reloc_root;
if (rc->create_reloc_tree) {
ret = clone_backref_node(trans, rc, root, reloc_root);
BUG_ON(ret);
}
}
...@@ -853,6 +853,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, ...@@ -853,6 +853,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
goto fail; goto fail;
} }
btrfs_reloc_pre_snapshot(trans, pending, &to_reserve);
btrfs_orphan_pre_snapshot(trans, pending, &to_reserve); btrfs_orphan_pre_snapshot(trans, pending, &to_reserve);
if (to_reserve > 0) { if (to_reserve > 0) {
...@@ -924,6 +925,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, ...@@ -924,6 +925,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
pending->snap = btrfs_read_fs_root_no_name(root->fs_info, &key); pending->snap = btrfs_read_fs_root_no_name(root->fs_info, &key);
BUG_ON(IS_ERR(pending->snap)); BUG_ON(IS_ERR(pending->snap));
btrfs_reloc_post_snapshot(trans, pending);
btrfs_orphan_post_snapshot(trans, pending); btrfs_orphan_post_snapshot(trans, pending);
fail: fail:
kfree(new_root_item); kfree(new_root_item);
...@@ -1213,9 +1215,9 @@ int btrfs_clean_old_snapshots(struct btrfs_root *root) ...@@ -1213,9 +1215,9 @@ int btrfs_clean_old_snapshots(struct btrfs_root *root)
if (btrfs_header_backref_rev(root->node) < if (btrfs_header_backref_rev(root->node) <
BTRFS_MIXED_BACKREF_REV) BTRFS_MIXED_BACKREF_REV)
btrfs_drop_snapshot(root, 0); btrfs_drop_snapshot(root, NULL, 0);
else else
btrfs_drop_snapshot(root, 1); btrfs_drop_snapshot(root, NULL, 1);
} }
return 0; return 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