Btrfs: pin freed blocks from the FS tree too
Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 68f66b32..13128b5 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -55,13 +55,13 @@
root->node = cow;
cow->count++;
if (buf != root->commit_root)
- btrfs_free_extent(root, buf->blocknr, 1);
+ btrfs_free_extent(root, buf->blocknr, 1, 1);
btrfs_block_release(root, buf);
} else {
btrfs_set_node_blockptr(&parent->node, parent_slot,
cow->blocknr);
BUG_ON(list_empty(&parent->dirty));
- btrfs_free_extent(root, buf->blocknr, 1);
+ btrfs_free_extent(root, buf->blocknr, 1, 1);
}
btrfs_block_release(root, buf);
return 0;
@@ -311,7 +311,7 @@
/* once for the root ptr */
btrfs_block_release(root, mid_buf);
clean_tree_block(root, mid_buf);
- return btrfs_free_extent(root, blocknr, 1);
+ return btrfs_free_extent(root, blocknr, 1, 1);
}
parent = &parent_buf->node;
@@ -352,7 +352,7 @@
wret = del_ptr(root, path, level + 1, pslot + 1);
if (wret)
ret = wret;
- wret = btrfs_free_extent(root, blocknr, 1);
+ wret = btrfs_free_extent(root, blocknr, 1, 1);
if (wret)
ret = wret;
} else {
@@ -388,7 +388,7 @@
wret = del_ptr(root, path, level + 1, pslot);
if (wret)
ret = wret;
- wret = btrfs_free_extent(root, blocknr, 1);
+ wret = btrfs_free_extent(root, blocknr, 1, 1);
if (wret)
ret = wret;
} else {
@@ -1310,7 +1310,7 @@
wret = del_ptr(root, path, 1, path->slots[1]);
if (wret)
ret = wret;
- wret = btrfs_free_extent(root, leaf_buf->blocknr, 1);
+ wret = btrfs_free_extent(root, leaf_buf->blocknr, 1, 1);
if (wret)
ret = wret;
}
@@ -1348,7 +1348,7 @@
if (wret)
ret = wret;
btrfs_block_release(root, leaf_buf);
- wret = btrfs_free_extent(root, blocknr, 1);
+ wret = btrfs_free_extent(root, blocknr, 1, 1);
if (wret)
ret = wret;
} else {
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 7c66b64..d15a2ed 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -658,7 +658,8 @@
struct btrfs_buffer *btrfs_alloc_free_block(struct btrfs_root *root);
int btrfs_inc_ref(struct btrfs_root *root, struct btrfs_buffer *buf);
-int btrfs_free_extent(struct btrfs_root *root, u64 blocknr, u64 num_blocks);
+int btrfs_free_extent(struct btrfs_root *root, u64 blocknr, u64 num_blocks,
+ int pin);
int btrfs_search_slot(struct btrfs_root *root, struct btrfs_key *key,
struct btrfs_path *p, int ins_len, int cow);
void btrfs_release_path(struct btrfs_root *root, struct btrfs_path *p);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index c81e141..4a40282 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -95,6 +95,7 @@
int btrfs_finish_extent_commit(struct btrfs_root *root)
{
unsigned long gang[8];
+ u64 first = 0;
int ret;
int i;
@@ -104,11 +105,13 @@
ARRAY_SIZE(gang));
if (!ret)
break;
+ if (!first)
+ first = gang[0];
for (i = 0; i < ret; i++) {
radix_tree_delete(&root->pinned_radix, gang[i]);
}
}
- root->last_insert.objectid = 0;
+ root->last_insert.objectid = first;
root->last_insert.offset = 0;
return 0;
}
@@ -140,7 +143,8 @@
/*
* remove an extent from the root, returns 0 on success
*/
-static int __free_extent(struct btrfs_root *root, u64 blocknr, u64 num_blocks)
+static int __free_extent(struct btrfs_root *root, u64 blocknr, u64 num_blocks,
+ int pin)
{
struct btrfs_path path;
struct btrfs_key key;
@@ -150,6 +154,7 @@
struct btrfs_key ins;
u32 refs;
+ BUG_ON(pin && num_blocks != 1);
key.objectid = blocknr;
key.flags = 0;
btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
@@ -170,7 +175,7 @@
refs = btrfs_extent_refs(ei) - 1;
btrfs_set_extent_refs(ei, refs);
if (refs == 0) {
- if (!root->ref_cows) {
+ if (pin) {
int err;
radix_tree_preload(GFP_KERNEL);
err = radix_tree_insert(&extent_root->pinned_radix,
@@ -179,8 +184,7 @@
radix_tree_preload_end();
}
ret = btrfs_del_item(extent_root, &path);
- if (root != extent_root &&
- extent_root->last_insert.objectid > blocknr)
+ if (!pin && extent_root->last_insert.objectid > blocknr)
extent_root->last_insert.objectid = blocknr;
if (ret)
BUG();
@@ -208,7 +212,8 @@
if (!ret)
break;
for (i = 0; i < ret; i++) {
- ret = __free_extent(extent_root, gang[i]->blocknr, 1);
+ ret = __free_extent(extent_root,
+ gang[i]->blocknr, 1, 1);
radix_tree_tag_clear(&extent_root->cache_radix,
gang[i]->blocknr,
CTREE_EXTENT_PENDING_DEL);
@@ -230,7 +235,8 @@
/*
* remove an extent from the root, returns 0 on success
*/
-int btrfs_free_extent(struct btrfs_root *root, u64 blocknr, u64 num_blocks)
+int btrfs_free_extent(struct btrfs_root *root, u64 blocknr, u64 num_blocks,
+ int pin)
{
struct btrfs_root *extent_root = root->extent_root;
struct btrfs_buffer *t;
@@ -243,7 +249,7 @@
CTREE_EXTENT_PENDING_DEL);
return 0;
}
- ret = __free_extent(root, blocknr, num_blocks);
+ ret = __free_extent(root, blocknr, num_blocks, pin);
pending_ret = run_pending(root->extent_root);
return ret ? ret : pending_ret;
}
@@ -451,7 +457,7 @@
ret = lookup_block_ref(root, blocknr, &refs);
if (refs != 1 || *level == 1) {
path->slots[*level]++;
- ret = btrfs_free_extent(root, blocknr, 1);
+ ret = btrfs_free_extent(root, blocknr, 1, 1);
BUG_ON(ret);
continue;
}
@@ -464,7 +470,7 @@
path->slots[*level] = 0;
}
out:
- ret = btrfs_free_extent(root, path->nodes[*level]->blocknr, 1);
+ ret = btrfs_free_extent(root, path->nodes[*level]->blocknr, 1, 1);
btrfs_block_release(root, path->nodes[*level]);
path->nodes[*level] = NULL;
*level += 1;
@@ -492,7 +498,7 @@
return 0;
} else {
ret = btrfs_free_extent(root,
- path->nodes[*level]->blocknr, 1);
+ path->nodes[*level]->blocknr, 1, 1);
btrfs_block_release(root, path->nodes[*level]);
path->nodes[*level] = NULL;
*level = i + 1;