Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable
* git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable: Btrfs: fix error message formatting Btrfs: fix use after free in btrfs_start_workers fail path Btrfs: honor nodatacow/sum mount options for new files Btrfs: update backrefs while dropping snapshot Btrfs: account for space we may use in fallocate Btrfs: fix the file clone ioctl for preallocated extents Btrfs: don't log the inode in file_write while growing the file
This commit is contained in:
Коммит
5291a12f05
|
@ -299,8 +299,8 @@ int btrfs_start_workers(struct btrfs_workers *workers, int num_workers)
|
||||||
"btrfs-%s-%d", workers->name,
|
"btrfs-%s-%d", workers->name,
|
||||||
workers->num_workers + i);
|
workers->num_workers + i);
|
||||||
if (IS_ERR(worker->task)) {
|
if (IS_ERR(worker->task)) {
|
||||||
kfree(worker);
|
|
||||||
ret = PTR_ERR(worker->task);
|
ret = PTR_ERR(worker->task);
|
||||||
|
kfree(worker);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2074,8 +2074,7 @@ 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_trans_handle *trans, struct btrfs_root
|
int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref);
|
||||||
*root);
|
|
||||||
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,
|
||||||
|
|
|
@ -990,15 +990,13 @@ static inline int extent_ref_type(u64 parent, u64 owner)
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_next_key(struct btrfs_path *path, struct btrfs_key *key)
|
static int find_next_key(struct btrfs_path *path, int level,
|
||||||
|
struct btrfs_key *key)
|
||||||
|
|
||||||
{
|
{
|
||||||
int level;
|
for (; level < BTRFS_MAX_LEVEL; level++) {
|
||||||
BUG_ON(!path->keep_locks);
|
|
||||||
for (level = 0; level < BTRFS_MAX_LEVEL; level++) {
|
|
||||||
if (!path->nodes[level])
|
if (!path->nodes[level])
|
||||||
break;
|
break;
|
||||||
btrfs_assert_tree_locked(path->nodes[level]);
|
|
||||||
if (path->slots[level] + 1 >=
|
if (path->slots[level] + 1 >=
|
||||||
btrfs_header_nritems(path->nodes[level]))
|
btrfs_header_nritems(path->nodes[level]))
|
||||||
continue;
|
continue;
|
||||||
|
@ -1158,7 +1156,8 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
|
||||||
* For simplicity, we just do not add new inline back
|
* For simplicity, we just do not add new inline back
|
||||||
* ref if there is any kind of item for this block
|
* ref if there is any kind of item for this block
|
||||||
*/
|
*/
|
||||||
if (find_next_key(path, &key) == 0 && key.objectid == bytenr &&
|
if (find_next_key(path, 0, &key) == 0 &&
|
||||||
|
key.objectid == bytenr &&
|
||||||
key.type < BTRFS_BLOCK_GROUP_ITEM_KEY) {
|
key.type < BTRFS_BLOCK_GROUP_ITEM_KEY) {
|
||||||
err = -EAGAIN;
|
err = -EAGAIN;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -2697,7 +2696,7 @@ again:
|
||||||
|
|
||||||
printk(KERN_ERR "no space left, need %llu, %llu delalloc bytes"
|
printk(KERN_ERR "no space left, need %llu, %llu delalloc bytes"
|
||||||
", %llu bytes_used, %llu bytes_reserved, "
|
", %llu bytes_used, %llu bytes_reserved, "
|
||||||
"%llu bytes_pinned, %llu bytes_readonly, %llu may use"
|
"%llu bytes_pinned, %llu bytes_readonly, %llu may use "
|
||||||
"%llu total\n", (unsigned long long)bytes,
|
"%llu total\n", (unsigned long long)bytes,
|
||||||
(unsigned long long)data_sinfo->bytes_delalloc,
|
(unsigned long long)data_sinfo->bytes_delalloc,
|
||||||
(unsigned long long)data_sinfo->bytes_used,
|
(unsigned long long)data_sinfo->bytes_used,
|
||||||
|
@ -4128,6 +4127,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
int btrfs_drop_leaf_ref(struct btrfs_trans_handle *trans,
|
int btrfs_drop_leaf_ref(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_root *root, struct extent_buffer *leaf)
|
struct btrfs_root *root, struct extent_buffer *leaf)
|
||||||
{
|
{
|
||||||
|
@ -4171,8 +4171,6 @@ int btrfs_drop_leaf_ref(struct btrfs_trans_handle *trans,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
|
|
||||||
static noinline int cache_drop_leaf_ref(struct btrfs_trans_handle *trans,
|
static noinline int cache_drop_leaf_ref(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_root *root,
|
struct btrfs_root *root,
|
||||||
struct btrfs_leaf_ref *ref)
|
struct btrfs_leaf_ref *ref)
|
||||||
|
@ -4553,262 +4551,471 @@ out:
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct walk_control {
|
||||||
|
u64 refs[BTRFS_MAX_LEVEL];
|
||||||
|
u64 flags[BTRFS_MAX_LEVEL];
|
||||||
|
struct btrfs_key update_progress;
|
||||||
|
int stage;
|
||||||
|
int level;
|
||||||
|
int shared_level;
|
||||||
|
int update_ref;
|
||||||
|
int keep_locks;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DROP_REFERENCE 1
|
||||||
|
#define UPDATE_BACKREF 2
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* helper function for drop_subtree, this function is similar to
|
* hepler to process tree block while walking down the tree.
|
||||||
* walk_down_tree. The main difference is that it checks reference
|
*
|
||||||
* counts while tree blocks are locked.
|
* when wc->stage == DROP_REFERENCE, this function checks
|
||||||
|
* reference count of the block. if the block is shared and
|
||||||
|
* we need update back refs for the subtree rooted at the
|
||||||
|
* block, this function changes wc->stage to UPDATE_BACKREF
|
||||||
|
*
|
||||||
|
* when wc->stage == UPDATE_BACKREF, this function updates
|
||||||
|
* back refs for pointers in the block.
|
||||||
|
*
|
||||||
|
* NOTE: return value 1 means we should stop walking down.
|
||||||
*/
|
*/
|
||||||
|
static noinline int walk_down_proc(struct btrfs_trans_handle *trans,
|
||||||
|
struct btrfs_root *root,
|
||||||
|
struct btrfs_path *path,
|
||||||
|
struct walk_control *wc)
|
||||||
|
{
|
||||||
|
int level = wc->level;
|
||||||
|
struct extent_buffer *eb = path->nodes[level];
|
||||||
|
struct btrfs_key key;
|
||||||
|
u64 flag = BTRFS_BLOCK_FLAG_FULL_BACKREF;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (wc->stage == UPDATE_BACKREF &&
|
||||||
|
btrfs_header_owner(eb) != root->root_key.objectid)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* when reference count of tree block is 1, it won't increase
|
||||||
|
* again. once full backref flag is set, we never clear it.
|
||||||
|
*/
|
||||||
|
if ((wc->stage == DROP_REFERENCE && wc->refs[level] != 1) ||
|
||||||
|
(wc->stage == UPDATE_BACKREF && !(wc->flags[level] & flag))) {
|
||||||
|
BUG_ON(!path->locks[level]);
|
||||||
|
ret = btrfs_lookup_extent_info(trans, root,
|
||||||
|
eb->start, eb->len,
|
||||||
|
&wc->refs[level],
|
||||||
|
&wc->flags[level]);
|
||||||
|
BUG_ON(ret);
|
||||||
|
BUG_ON(wc->refs[level] == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wc->stage == DROP_REFERENCE &&
|
||||||
|
wc->update_ref && wc->refs[level] > 1) {
|
||||||
|
BUG_ON(eb == root->node);
|
||||||
|
BUG_ON(path->slots[level] > 0);
|
||||||
|
if (level == 0)
|
||||||
|
btrfs_item_key_to_cpu(eb, &key, path->slots[level]);
|
||||||
|
else
|
||||||
|
btrfs_node_key_to_cpu(eb, &key, path->slots[level]);
|
||||||
|
if (btrfs_header_owner(eb) == root->root_key.objectid &&
|
||||||
|
btrfs_comp_cpu_keys(&key, &wc->update_progress) >= 0) {
|
||||||
|
wc->stage = UPDATE_BACKREF;
|
||||||
|
wc->shared_level = level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wc->stage == DROP_REFERENCE) {
|
||||||
|
if (wc->refs[level] > 1)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (path->locks[level] && !wc->keep_locks) {
|
||||||
|
btrfs_tree_unlock(eb);
|
||||||
|
path->locks[level] = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wc->stage == UPDATE_BACKREF */
|
||||||
|
if (!(wc->flags[level] & flag)) {
|
||||||
|
BUG_ON(!path->locks[level]);
|
||||||
|
ret = btrfs_inc_ref(trans, root, eb, 1);
|
||||||
|
BUG_ON(ret);
|
||||||
|
ret = btrfs_dec_ref(trans, root, eb, 0);
|
||||||
|
BUG_ON(ret);
|
||||||
|
ret = btrfs_set_disk_extent_flags(trans, root, eb->start,
|
||||||
|
eb->len, flag, 0);
|
||||||
|
BUG_ON(ret);
|
||||||
|
wc->flags[level] |= flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* the block is shared by multiple trees, so it's not good to
|
||||||
|
* keep the tree lock
|
||||||
|
*/
|
||||||
|
if (path->locks[level] && level > 0) {
|
||||||
|
btrfs_tree_unlock(eb);
|
||||||
|
path->locks[level] = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hepler to process tree block while walking up the tree.
|
||||||
|
*
|
||||||
|
* when wc->stage == DROP_REFERENCE, this function drops
|
||||||
|
* reference count on the block.
|
||||||
|
*
|
||||||
|
* when wc->stage == UPDATE_BACKREF, this function changes
|
||||||
|
* wc->stage back to DROP_REFERENCE if we changed wc->stage
|
||||||
|
* to UPDATE_BACKREF previously while processing the block.
|
||||||
|
*
|
||||||
|
* NOTE: return value 1 means we should stop walking up.
|
||||||
|
*/
|
||||||
|
static noinline int walk_up_proc(struct btrfs_trans_handle *trans,
|
||||||
|
struct btrfs_root *root,
|
||||||
|
struct btrfs_path *path,
|
||||||
|
struct walk_control *wc)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
int level = wc->level;
|
||||||
|
struct extent_buffer *eb = path->nodes[level];
|
||||||
|
u64 parent = 0;
|
||||||
|
|
||||||
|
if (wc->stage == UPDATE_BACKREF) {
|
||||||
|
BUG_ON(wc->shared_level < level);
|
||||||
|
if (level < wc->shared_level)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
BUG_ON(wc->refs[level] <= 1);
|
||||||
|
ret = find_next_key(path, level + 1, &wc->update_progress);
|
||||||
|
if (ret > 0)
|
||||||
|
wc->update_ref = 0;
|
||||||
|
|
||||||
|
wc->stage = DROP_REFERENCE;
|
||||||
|
wc->shared_level = -1;
|
||||||
|
path->slots[level] = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check reference count again if the block isn't locked.
|
||||||
|
* we should start walking down the tree again if reference
|
||||||
|
* count is one.
|
||||||
|
*/
|
||||||
|
if (!path->locks[level]) {
|
||||||
|
BUG_ON(level == 0);
|
||||||
|
btrfs_tree_lock(eb);
|
||||||
|
btrfs_set_lock_blocking(eb);
|
||||||
|
path->locks[level] = 1;
|
||||||
|
|
||||||
|
ret = btrfs_lookup_extent_info(trans, root,
|
||||||
|
eb->start, eb->len,
|
||||||
|
&wc->refs[level],
|
||||||
|
&wc->flags[level]);
|
||||||
|
BUG_ON(ret);
|
||||||
|
BUG_ON(wc->refs[level] == 0);
|
||||||
|
if (wc->refs[level] == 1) {
|
||||||
|
btrfs_tree_unlock(eb);
|
||||||
|
path->locks[level] = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
BUG_ON(level != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wc->stage == DROP_REFERENCE */
|
||||||
|
BUG_ON(wc->refs[level] > 1 && !path->locks[level]);
|
||||||
|
|
||||||
|
if (wc->refs[level] == 1) {
|
||||||
|
if (level == 0) {
|
||||||
|
if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF)
|
||||||
|
ret = btrfs_dec_ref(trans, root, eb, 1);
|
||||||
|
else
|
||||||
|
ret = btrfs_dec_ref(trans, root, eb, 0);
|
||||||
|
BUG_ON(ret);
|
||||||
|
}
|
||||||
|
/* make block locked assertion in clean_tree_block happy */
|
||||||
|
if (!path->locks[level] &&
|
||||||
|
btrfs_header_generation(eb) == trans->transid) {
|
||||||
|
btrfs_tree_lock(eb);
|
||||||
|
btrfs_set_lock_blocking(eb);
|
||||||
|
path->locks[level] = 1;
|
||||||
|
}
|
||||||
|
clean_tree_block(trans, root, eb);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eb == root->node) {
|
||||||
|
if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF)
|
||||||
|
parent = eb->start;
|
||||||
|
else
|
||||||
|
BUG_ON(root->root_key.objectid !=
|
||||||
|
btrfs_header_owner(eb));
|
||||||
|
} else {
|
||||||
|
if (wc->flags[level + 1] & BTRFS_BLOCK_FLAG_FULL_BACKREF)
|
||||||
|
parent = path->nodes[level + 1]->start;
|
||||||
|
else
|
||||||
|
BUG_ON(root->root_key.objectid !=
|
||||||
|
btrfs_header_owner(path->nodes[level + 1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = btrfs_free_extent(trans, root, eb->start, eb->len, parent,
|
||||||
|
root->root_key.objectid, level, 0);
|
||||||
|
BUG_ON(ret);
|
||||||
|
out:
|
||||||
|
wc->refs[level] = 0;
|
||||||
|
wc->flags[level] = 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static noinline int walk_down_tree(struct btrfs_trans_handle *trans,
|
static noinline int walk_down_tree(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_root *root,
|
struct btrfs_root *root,
|
||||||
struct btrfs_path *path, int *level)
|
struct btrfs_path *path,
|
||||||
|
struct walk_control *wc)
|
||||||
{
|
{
|
||||||
struct extent_buffer *next;
|
struct extent_buffer *next;
|
||||||
struct extent_buffer *cur;
|
struct extent_buffer *cur;
|
||||||
struct extent_buffer *parent;
|
|
||||||
u64 bytenr;
|
u64 bytenr;
|
||||||
u64 ptr_gen;
|
u64 ptr_gen;
|
||||||
u64 refs;
|
|
||||||
u64 flags;
|
|
||||||
u32 blocksize;
|
u32 blocksize;
|
||||||
|
int level = wc->level;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
cur = path->nodes[*level];
|
while (level >= 0) {
|
||||||
ret = btrfs_lookup_extent_info(trans, root, cur->start, cur->len,
|
cur = path->nodes[level];
|
||||||
&refs, &flags);
|
BUG_ON(path->slots[level] >= btrfs_header_nritems(cur));
|
||||||
BUG_ON(ret);
|
|
||||||
if (refs > 1)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
BUG_ON(!(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF));
|
ret = walk_down_proc(trans, root, path, wc);
|
||||||
|
if (ret > 0)
|
||||||
while (*level >= 0) {
|
|
||||||
cur = path->nodes[*level];
|
|
||||||
if (*level == 0) {
|
|
||||||
ret = btrfs_drop_leaf_ref(trans, root, cur);
|
|
||||||
BUG_ON(ret);
|
|
||||||
clean_tree_block(trans, root, cur);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
if (path->slots[*level] >= btrfs_header_nritems(cur)) {
|
|
||||||
clean_tree_block(trans, root, cur);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytenr = btrfs_node_blockptr(cur, path->slots[*level]);
|
if (level == 0)
|
||||||
blocksize = btrfs_level_size(root, *level - 1);
|
break;
|
||||||
ptr_gen = btrfs_node_ptr_generation(cur, path->slots[*level]);
|
|
||||||
|
bytenr = btrfs_node_blockptr(cur, path->slots[level]);
|
||||||
|
blocksize = btrfs_level_size(root, level - 1);
|
||||||
|
ptr_gen = btrfs_node_ptr_generation(cur, path->slots[level]);
|
||||||
|
|
||||||
next = read_tree_block(root, bytenr, blocksize, ptr_gen);
|
next = read_tree_block(root, bytenr, blocksize, ptr_gen);
|
||||||
btrfs_tree_lock(next);
|
btrfs_tree_lock(next);
|
||||||
btrfs_set_lock_blocking(next);
|
btrfs_set_lock_blocking(next);
|
||||||
|
|
||||||
ret = btrfs_lookup_extent_info(trans, root, bytenr, blocksize,
|
level--;
|
||||||
&refs, &flags);
|
BUG_ON(level != btrfs_header_level(next));
|
||||||
BUG_ON(ret);
|
path->nodes[level] = next;
|
||||||
if (refs > 1) {
|
path->slots[level] = 0;
|
||||||
parent = path->nodes[*level];
|
path->locks[level] = 1;
|
||||||
ret = btrfs_free_extent(trans, root, bytenr,
|
wc->level = level;
|
||||||
blocksize, parent->start,
|
|
||||||
btrfs_header_owner(parent),
|
|
||||||
*level - 1, 0);
|
|
||||||
BUG_ON(ret);
|
|
||||||
path->slots[*level]++;
|
|
||||||
btrfs_tree_unlock(next);
|
|
||||||
free_extent_buffer(next);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
BUG_ON(!(flags & BTRFS_BLOCK_FLAG_FULL_BACKREF));
|
|
||||||
|
|
||||||
*level = btrfs_header_level(next);
|
|
||||||
path->nodes[*level] = next;
|
|
||||||
path->slots[*level] = 0;
|
|
||||||
path->locks[*level] = 1;
|
|
||||||
cond_resched();
|
|
||||||
}
|
}
|
||||||
out:
|
|
||||||
if (path->nodes[*level] == root->node)
|
|
||||||
parent = path->nodes[*level];
|
|
||||||
else
|
|
||||||
parent = path->nodes[*level + 1];
|
|
||||||
bytenr = path->nodes[*level]->start;
|
|
||||||
blocksize = path->nodes[*level]->len;
|
|
||||||
|
|
||||||
ret = btrfs_free_extent(trans, root, bytenr, blocksize, parent->start,
|
|
||||||
btrfs_header_owner(parent), *level, 0);
|
|
||||||
BUG_ON(ret);
|
|
||||||
|
|
||||||
if (path->locks[*level]) {
|
|
||||||
btrfs_tree_unlock(path->nodes[*level]);
|
|
||||||
path->locks[*level] = 0;
|
|
||||||
}
|
|
||||||
free_extent_buffer(path->nodes[*level]);
|
|
||||||
path->nodes[*level] = NULL;
|
|
||||||
*level += 1;
|
|
||||||
cond_resched();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* helper for dropping snapshots. This walks back up the tree in the path
|
|
||||||
* to find the first node higher up where we haven't yet gone through
|
|
||||||
* all the slots
|
|
||||||
*/
|
|
||||||
static noinline int walk_up_tree(struct btrfs_trans_handle *trans,
|
static noinline int walk_up_tree(struct btrfs_trans_handle *trans,
|
||||||
struct btrfs_root *root,
|
struct btrfs_root *root,
|
||||||
struct btrfs_path *path,
|
struct btrfs_path *path,
|
||||||
int *level, int max_level)
|
struct walk_control *wc, int max_level)
|
||||||
{
|
{
|
||||||
struct btrfs_root_item *root_item = &root->root_item;
|
int level = wc->level;
|
||||||
int i;
|
|
||||||
int slot;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
for (i = *level; i < max_level && path->nodes[i]; i++) {
|
path->slots[level] = btrfs_header_nritems(path->nodes[level]);
|
||||||
slot = path->slots[i];
|
while (level < max_level && path->nodes[level]) {
|
||||||
if (slot + 1 < btrfs_header_nritems(path->nodes[i])) {
|
wc->level = level;
|
||||||
/*
|
if (path->slots[level] + 1 <
|
||||||
* there is more work to do in this level.
|
btrfs_header_nritems(path->nodes[level])) {
|
||||||
* Update the drop_progress marker to reflect
|
path->slots[level]++;
|
||||||
* the work we've done so far, and then bump
|
|
||||||
* the slot number
|
|
||||||
*/
|
|
||||||
path->slots[i]++;
|
|
||||||
WARN_ON(*level == 0);
|
|
||||||
if (max_level == BTRFS_MAX_LEVEL) {
|
|
||||||
btrfs_node_key(path->nodes[i],
|
|
||||||
&root_item->drop_progress,
|
|
||||||
path->slots[i]);
|
|
||||||
root_item->drop_level = i;
|
|
||||||
}
|
|
||||||
*level = i;
|
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
struct extent_buffer *parent;
|
ret = walk_up_proc(trans, root, path, wc);
|
||||||
|
if (ret > 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/*
|
if (path->locks[level]) {
|
||||||
* this whole node is done, free our reference
|
btrfs_tree_unlock(path->nodes[level]);
|
||||||
* on it and go up one level
|
path->locks[level] = 0;
|
||||||
*/
|
|
||||||
if (path->nodes[*level] == root->node)
|
|
||||||
parent = path->nodes[*level];
|
|
||||||
else
|
|
||||||
parent = path->nodes[*level + 1];
|
|
||||||
|
|
||||||
clean_tree_block(trans, root, path->nodes[i]);
|
|
||||||
ret = btrfs_free_extent(trans, root,
|
|
||||||
path->nodes[i]->start,
|
|
||||||
path->nodes[i]->len,
|
|
||||||
parent->start,
|
|
||||||
btrfs_header_owner(parent),
|
|
||||||
*level, 0);
|
|
||||||
BUG_ON(ret);
|
|
||||||
if (path->locks[*level]) {
|
|
||||||
btrfs_tree_unlock(path->nodes[i]);
|
|
||||||
path->locks[i] = 0;
|
|
||||||
}
|
}
|
||||||
free_extent_buffer(path->nodes[i]);
|
free_extent_buffer(path->nodes[level]);
|
||||||
path->nodes[i] = NULL;
|
path->nodes[level] = NULL;
|
||||||
*level = i + 1;
|
level++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* drop the reference count on the tree rooted at 'snap'. This traverses
|
* drop a subvolume tree.
|
||||||
* the tree freeing any blocks that have a ref count of zero after being
|
*
|
||||||
* decremented.
|
* this function traverses the tree freeing any blocks that only
|
||||||
|
* referenced by the tree.
|
||||||
|
*
|
||||||
|
* when a shared tree block is found. this function decreases its
|
||||||
|
* reference count by one. if update_ref is true, this function
|
||||||
|
* also make sure backrefs for the shared block and all lower level
|
||||||
|
* blocks are properly updated.
|
||||||
*/
|
*/
|
||||||
int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
|
int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref)
|
||||||
*root)
|
|
||||||
{
|
{
|
||||||
int ret = 0;
|
|
||||||
int wret;
|
|
||||||
int level;
|
|
||||||
struct btrfs_path *path;
|
struct btrfs_path *path;
|
||||||
int update_count;
|
struct btrfs_trans_handle *trans;
|
||||||
|
struct btrfs_root *tree_root = root->fs_info->tree_root;
|
||||||
struct btrfs_root_item *root_item = &root->root_item;
|
struct btrfs_root_item *root_item = &root->root_item;
|
||||||
|
struct walk_control *wc;
|
||||||
|
struct btrfs_key key;
|
||||||
|
int err = 0;
|
||||||
|
int ret;
|
||||||
|
int level;
|
||||||
|
|
||||||
path = btrfs_alloc_path();
|
path = btrfs_alloc_path();
|
||||||
BUG_ON(!path);
|
BUG_ON(!path);
|
||||||
|
|
||||||
level = btrfs_header_level(root->node);
|
wc = kzalloc(sizeof(*wc), GFP_NOFS);
|
||||||
|
BUG_ON(!wc);
|
||||||
|
|
||||||
|
trans = btrfs_start_transaction(tree_root, 1);
|
||||||
|
|
||||||
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);
|
||||||
path->nodes[level] = btrfs_lock_root_node(root);
|
path->nodes[level] = btrfs_lock_root_node(root);
|
||||||
btrfs_set_lock_blocking(path->nodes[level]);
|
btrfs_set_lock_blocking(path->nodes[level]);
|
||||||
path->slots[level] = 0;
|
path->slots[level] = 0;
|
||||||
path->locks[level] = 1;
|
path->locks[level] = 1;
|
||||||
|
memset(&wc->update_progress, 0,
|
||||||
|
sizeof(wc->update_progress));
|
||||||
} else {
|
} else {
|
||||||
struct btrfs_key key;
|
|
||||||
struct btrfs_disk_key found_key;
|
|
||||||
struct extent_buffer *node;
|
|
||||||
|
|
||||||
btrfs_disk_key_to_cpu(&key, &root_item->drop_progress);
|
btrfs_disk_key_to_cpu(&key, &root_item->drop_progress);
|
||||||
|
memcpy(&wc->update_progress, &key,
|
||||||
|
sizeof(wc->update_progress));
|
||||||
|
|
||||||
level = root_item->drop_level;
|
level = root_item->drop_level;
|
||||||
|
BUG_ON(level == 0);
|
||||||
path->lowest_level = level;
|
path->lowest_level = level;
|
||||||
wret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
|
||||||
if (wret < 0) {
|
path->lowest_level = 0;
|
||||||
ret = wret;
|
if (ret < 0) {
|
||||||
|
err = ret;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
node = path->nodes[level];
|
btrfs_node_key_to_cpu(path->nodes[level], &key,
|
||||||
btrfs_node_key(node, &found_key, path->slots[level]);
|
path->slots[level]);
|
||||||
WARN_ON(memcmp(&found_key, &root_item->drop_progress,
|
WARN_ON(memcmp(&key, &wc->update_progress, sizeof(key)));
|
||||||
sizeof(found_key)));
|
|
||||||
/*
|
/*
|
||||||
* unlock our path, this is safe because only this
|
* unlock our path, this is safe because only this
|
||||||
* function is allowed to delete this snapshot
|
* function is allowed to delete this snapshot
|
||||||
*/
|
*/
|
||||||
btrfs_unlock_up_safe(path, 0);
|
btrfs_unlock_up_safe(path, 0);
|
||||||
}
|
|
||||||
while (1) {
|
|
||||||
unsigned long update;
|
|
||||||
wret = walk_down_tree(trans, root, path, &level);
|
|
||||||
if (wret > 0)
|
|
||||||
break;
|
|
||||||
if (wret < 0)
|
|
||||||
ret = wret;
|
|
||||||
|
|
||||||
wret = walk_up_tree(trans, root, path, &level,
|
level = btrfs_header_level(root->node);
|
||||||
BTRFS_MAX_LEVEL);
|
while (1) {
|
||||||
if (wret > 0)
|
btrfs_tree_lock(path->nodes[level]);
|
||||||
break;
|
btrfs_set_lock_blocking(path->nodes[level]);
|
||||||
if (wret < 0)
|
|
||||||
ret = wret;
|
ret = btrfs_lookup_extent_info(trans, root,
|
||||||
if (trans->transaction->in_commit ||
|
path->nodes[level]->start,
|
||||||
trans->transaction->delayed_refs.flushing) {
|
path->nodes[level]->len,
|
||||||
ret = -EAGAIN;
|
&wc->refs[level],
|
||||||
|
&wc->flags[level]);
|
||||||
|
BUG_ON(ret);
|
||||||
|
BUG_ON(wc->refs[level] == 0);
|
||||||
|
|
||||||
|
if (level == root_item->drop_level)
|
||||||
|
break;
|
||||||
|
|
||||||
|
btrfs_tree_unlock(path->nodes[level]);
|
||||||
|
WARN_ON(wc->refs[level] != 1);
|
||||||
|
level--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wc->level = level;
|
||||||
|
wc->shared_level = -1;
|
||||||
|
wc->stage = DROP_REFERENCE;
|
||||||
|
wc->update_ref = update_ref;
|
||||||
|
wc->keep_locks = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
ret = walk_down_tree(trans, root, path, wc);
|
||||||
|
if (ret < 0) {
|
||||||
|
err = ret;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for (update_count = 0; update_count < 16; update_count++) {
|
|
||||||
|
ret = walk_up_tree(trans, root, path, wc, BTRFS_MAX_LEVEL);
|
||||||
|
if (ret < 0) {
|
||||||
|
err = ret;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret > 0) {
|
||||||
|
BUG_ON(wc->stage != DROP_REFERENCE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wc->stage == DROP_REFERENCE) {
|
||||||
|
level = wc->level;
|
||||||
|
btrfs_node_key(path->nodes[level],
|
||||||
|
&root_item->drop_progress,
|
||||||
|
path->slots[level]);
|
||||||
|
root_item->drop_level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
BUG_ON(wc->level == 0);
|
||||||
|
if (trans->transaction->in_commit ||
|
||||||
|
trans->transaction->delayed_refs.flushing) {
|
||||||
|
ret = btrfs_update_root(trans, tree_root,
|
||||||
|
&root->root_key,
|
||||||
|
root_item);
|
||||||
|
BUG_ON(ret);
|
||||||
|
|
||||||
|
btrfs_end_transaction(trans, tree_root);
|
||||||
|
trans = btrfs_start_transaction(tree_root, 1);
|
||||||
|
} else {
|
||||||
|
unsigned long update;
|
||||||
update = trans->delayed_ref_updates;
|
update = trans->delayed_ref_updates;
|
||||||
trans->delayed_ref_updates = 0;
|
trans->delayed_ref_updates = 0;
|
||||||
if (update)
|
if (update)
|
||||||
btrfs_run_delayed_refs(trans, root, update);
|
btrfs_run_delayed_refs(trans, tree_root,
|
||||||
else
|
update);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
btrfs_release_path(root, path);
|
||||||
|
BUG_ON(err);
|
||||||
|
|
||||||
|
ret = btrfs_del_root(trans, tree_root, &root->root_key);
|
||||||
|
BUG_ON(ret);
|
||||||
|
|
||||||
|
free_extent_buffer(root->node);
|
||||||
|
free_extent_buffer(root->commit_root);
|
||||||
|
kfree(root);
|
||||||
out:
|
out:
|
||||||
|
btrfs_end_transaction(trans, tree_root);
|
||||||
|
kfree(wc);
|
||||||
btrfs_free_path(path);
|
btrfs_free_path(path);
|
||||||
return ret;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* drop subtree rooted at tree block 'node'.
|
||||||
|
*
|
||||||
|
* NOTE: this function will unlock and release tree block 'node'
|
||||||
|
*/
|
||||||
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,
|
||||||
struct extent_buffer *parent)
|
struct extent_buffer *parent)
|
||||||
{
|
{
|
||||||
struct btrfs_path *path;
|
struct btrfs_path *path;
|
||||||
|
struct walk_control *wc;
|
||||||
int level;
|
int level;
|
||||||
int parent_level;
|
int parent_level;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int wret;
|
int wret;
|
||||||
|
|
||||||
|
BUG_ON(root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID);
|
||||||
|
|
||||||
path = btrfs_alloc_path();
|
path = btrfs_alloc_path();
|
||||||
BUG_ON(!path);
|
BUG_ON(!path);
|
||||||
|
|
||||||
|
wc = kzalloc(sizeof(*wc), GFP_NOFS);
|
||||||
|
BUG_ON(!wc);
|
||||||
|
|
||||||
btrfs_assert_tree_locked(parent);
|
btrfs_assert_tree_locked(parent);
|
||||||
parent_level = btrfs_header_level(parent);
|
parent_level = btrfs_header_level(parent);
|
||||||
extent_buffer_get(parent);
|
extent_buffer_get(parent);
|
||||||
|
@ -4817,24 +5024,33 @@ int btrfs_drop_subtree(struct btrfs_trans_handle *trans,
|
||||||
|
|
||||||
btrfs_assert_tree_locked(node);
|
btrfs_assert_tree_locked(node);
|
||||||
level = btrfs_header_level(node);
|
level = btrfs_header_level(node);
|
||||||
extent_buffer_get(node);
|
|
||||||
path->nodes[level] = node;
|
path->nodes[level] = node;
|
||||||
path->slots[level] = 0;
|
path->slots[level] = 0;
|
||||||
|
path->locks[level] = 1;
|
||||||
|
|
||||||
|
wc->refs[parent_level] = 1;
|
||||||
|
wc->flags[parent_level] = BTRFS_BLOCK_FLAG_FULL_BACKREF;
|
||||||
|
wc->level = level;
|
||||||
|
wc->shared_level = -1;
|
||||||
|
wc->stage = DROP_REFERENCE;
|
||||||
|
wc->update_ref = 0;
|
||||||
|
wc->keep_locks = 1;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
wret = walk_down_tree(trans, root, path, &level);
|
wret = walk_down_tree(trans, root, path, wc);
|
||||||
if (wret < 0)
|
if (wret < 0) {
|
||||||
ret = wret;
|
ret = wret;
|
||||||
if (wret != 0)
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
wret = walk_up_tree(trans, root, path, &level, parent_level);
|
wret = walk_up_tree(trans, root, path, wc, parent_level);
|
||||||
if (wret < 0)
|
if (wret < 0)
|
||||||
ret = wret;
|
ret = wret;
|
||||||
if (wret != 0)
|
if (wret != 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kfree(wc);
|
||||||
btrfs_free_path(path);
|
btrfs_free_path(path);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,7 +151,10 @@ static noinline int dirty_and_release_pages(struct btrfs_trans_handle *trans,
|
||||||
}
|
}
|
||||||
if (end_pos > isize) {
|
if (end_pos > isize) {
|
||||||
i_size_write(inode, end_pos);
|
i_size_write(inode, end_pos);
|
||||||
btrfs_update_inode(trans, root, inode);
|
/* we've only changed i_size in ram, and we haven't updated
|
||||||
|
* the disk i_size. There is no need to log the inode
|
||||||
|
* at this time.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
err = btrfs_end_transaction(trans, root);
|
err = btrfs_end_transaction(trans, root);
|
||||||
out_unlock:
|
out_unlock:
|
||||||
|
|
|
@ -3580,12 +3580,6 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
||||||
owner = 1;
|
owner = 1;
|
||||||
BTRFS_I(inode)->block_group =
|
BTRFS_I(inode)->block_group =
|
||||||
btrfs_find_block_group(root, 0, alloc_hint, owner);
|
btrfs_find_block_group(root, 0, alloc_hint, owner);
|
||||||
if ((mode & S_IFREG)) {
|
|
||||||
if (btrfs_test_opt(root, NODATASUM))
|
|
||||||
BTRFS_I(inode)->flags |= BTRFS_INODE_NODATASUM;
|
|
||||||
if (btrfs_test_opt(root, NODATACOW))
|
|
||||||
BTRFS_I(inode)->flags |= BTRFS_INODE_NODATACOW;
|
|
||||||
}
|
|
||||||
|
|
||||||
key[0].objectid = objectid;
|
key[0].objectid = objectid;
|
||||||
btrfs_set_key_type(&key[0], BTRFS_INODE_ITEM_KEY);
|
btrfs_set_key_type(&key[0], BTRFS_INODE_ITEM_KEY);
|
||||||
|
@ -3640,6 +3634,13 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
||||||
|
|
||||||
btrfs_inherit_iflags(inode, dir);
|
btrfs_inherit_iflags(inode, dir);
|
||||||
|
|
||||||
|
if ((mode & S_IFREG)) {
|
||||||
|
if (btrfs_test_opt(root, NODATASUM))
|
||||||
|
BTRFS_I(inode)->flags |= BTRFS_INODE_NODATASUM;
|
||||||
|
if (btrfs_test_opt(root, NODATACOW))
|
||||||
|
BTRFS_I(inode)->flags |= BTRFS_INODE_NODATACOW;
|
||||||
|
}
|
||||||
|
|
||||||
insert_inode_hash(inode);
|
insert_inode_hash(inode);
|
||||||
inode_tree_add(inode);
|
inode_tree_add(inode);
|
||||||
return inode;
|
return inode;
|
||||||
|
@ -5082,6 +5083,7 @@ static long btrfs_fallocate(struct inode *inode, int mode,
|
||||||
u64 mask = BTRFS_I(inode)->root->sectorsize - 1;
|
u64 mask = BTRFS_I(inode)->root->sectorsize - 1;
|
||||||
struct extent_map *em;
|
struct extent_map *em;
|
||||||
struct btrfs_trans_handle *trans;
|
struct btrfs_trans_handle *trans;
|
||||||
|
struct btrfs_root *root;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
alloc_start = offset & ~mask;
|
alloc_start = offset & ~mask;
|
||||||
|
@ -5100,6 +5102,13 @@ static long btrfs_fallocate(struct inode *inode, int mode,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
root = BTRFS_I(inode)->root;
|
||||||
|
|
||||||
|
ret = btrfs_check_data_free_space(root, inode,
|
||||||
|
alloc_end - alloc_start);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
locked_end = alloc_end - 1;
|
locked_end = alloc_end - 1;
|
||||||
while (1) {
|
while (1) {
|
||||||
struct btrfs_ordered_extent *ordered;
|
struct btrfs_ordered_extent *ordered;
|
||||||
|
@ -5107,7 +5116,7 @@ static long btrfs_fallocate(struct inode *inode, int mode,
|
||||||
trans = btrfs_start_transaction(BTRFS_I(inode)->root, 1);
|
trans = btrfs_start_transaction(BTRFS_I(inode)->root, 1);
|
||||||
if (!trans) {
|
if (!trans) {
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto out;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* the extent lock is ordered inside the running
|
/* the extent lock is ordered inside the running
|
||||||
|
@ -5168,6 +5177,8 @@ static long btrfs_fallocate(struct inode *inode, int mode,
|
||||||
GFP_NOFS);
|
GFP_NOFS);
|
||||||
|
|
||||||
btrfs_end_transaction(trans, BTRFS_I(inode)->root);
|
btrfs_end_transaction(trans, BTRFS_I(inode)->root);
|
||||||
|
out_free:
|
||||||
|
btrfs_free_reserved_data_space(root, inode, alloc_end - alloc_start);
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&inode->i_mutex);
|
mutex_unlock(&inode->i_mutex);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -1028,7 +1028,8 @@ static long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
||||||
struct btrfs_file_extent_item);
|
struct btrfs_file_extent_item);
|
||||||
comp = btrfs_file_extent_compression(leaf, extent);
|
comp = btrfs_file_extent_compression(leaf, extent);
|
||||||
type = btrfs_file_extent_type(leaf, extent);
|
type = btrfs_file_extent_type(leaf, extent);
|
||||||
if (type == BTRFS_FILE_EXTENT_REG) {
|
if (type == BTRFS_FILE_EXTENT_REG ||
|
||||||
|
type == BTRFS_FILE_EXTENT_PREALLOC) {
|
||||||
disko = btrfs_file_extent_disk_bytenr(leaf,
|
disko = btrfs_file_extent_disk_bytenr(leaf,
|
||||||
extent);
|
extent);
|
||||||
diskl = btrfs_file_extent_disk_num_bytes(leaf,
|
diskl = btrfs_file_extent_disk_num_bytes(leaf,
|
||||||
|
@ -1051,7 +1052,8 @@ static long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
||||||
new_key.objectid = inode->i_ino;
|
new_key.objectid = inode->i_ino;
|
||||||
new_key.offset = key.offset + destoff - off;
|
new_key.offset = key.offset + destoff - off;
|
||||||
|
|
||||||
if (type == BTRFS_FILE_EXTENT_REG) {
|
if (type == BTRFS_FILE_EXTENT_REG ||
|
||||||
|
type == BTRFS_FILE_EXTENT_PREALLOC) {
|
||||||
ret = btrfs_insert_empty_item(trans, root, path,
|
ret = btrfs_insert_empty_item(trans, root, path,
|
||||||
&new_key, size);
|
&new_key, size);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|
|
@ -1788,7 +1788,7 @@ static void merge_func(struct btrfs_work *work)
|
||||||
btrfs_end_transaction(trans, root);
|
btrfs_end_transaction(trans, root);
|
||||||
}
|
}
|
||||||
|
|
||||||
btrfs_drop_dead_root(reloc_root);
|
btrfs_drop_snapshot(reloc_root, 0);
|
||||||
|
|
||||||
if (atomic_dec_and_test(async->num_pending))
|
if (atomic_dec_and_test(async->num_pending))
|
||||||
complete(async->done);
|
complete(async->done);
|
||||||
|
@ -2075,9 +2075,6 @@ 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);
|
||||||
|
|
||||||
btrfs_tree_unlock(eb);
|
|
||||||
free_extent_buffer(eb);
|
|
||||||
}
|
}
|
||||||
if (!lowest) {
|
if (!lowest) {
|
||||||
btrfs_tree_unlock(upper->eb);
|
btrfs_tree_unlock(upper->eb);
|
||||||
|
|
|
@ -593,6 +593,7 @@ int btrfs_defrag_root(struct btrfs_root *root, int cacheonly)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
/*
|
/*
|
||||||
* when dropping snapshots, we generate a ton of delayed refs, and it makes
|
* when dropping snapshots, we generate a ton of delayed refs, and it makes
|
||||||
* sense not to join the transaction while it is trying to flush the current
|
* sense not to join the transaction while it is trying to flush the current
|
||||||
|
@ -681,6 +682,7 @@ int btrfs_drop_dead_root(struct btrfs_root *root)
|
||||||
btrfs_btree_balance_dirty(tree_root, nr);
|
btrfs_btree_balance_dirty(tree_root, nr);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* new snapshots need to be created at a very specific time in the
|
* new snapshots need to be created at a very specific time in the
|
||||||
|
@ -1081,7 +1083,7 @@ int btrfs_clean_old_snapshots(struct btrfs_root *root)
|
||||||
while (!list_empty(&list)) {
|
while (!list_empty(&list)) {
|
||||||
root = list_entry(list.next, struct btrfs_root, root_list);
|
root = list_entry(list.next, struct btrfs_root, root_list);
|
||||||
list_del_init(&root->root_list);
|
list_del_init(&root->root_list);
|
||||||
btrfs_drop_dead_root(root);
|
btrfs_drop_snapshot(root, 0);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче