**cow_ret)
{
struct btrfs_buffer *cow;
+ u64 root_gen;
if (!list_empty(&buf->dirty)) {
*cow_ret = buf;
*cow_ret = cow;
btrfs_inc_ref(trans, root, buf);
if (buf == root->node) {
+ root_gen = btrfs_header_generation(&buf->node.header);
root->node = cow;
cow->count++;
if (buf != root->commit_root)
btrfs_free_extent(trans, root, buf->bytenr,
- buf->size, 1);
+ buf->size, root->root_key.objectid,
+ root_gen, 0, 0, 1);
btrfs_block_release(root, buf);
} else {
+ root_gen = btrfs_header_generation(&parent->node.header);
btrfs_set_node_blockptr(&parent->node, parent_slot,
cow->bytenr);
btrfs_set_node_ptr_generation(&parent->node, parent_slot,
trans->transid);
BUG_ON(list_empty(&parent->dirty));
- btrfs_free_extent(trans, root, buf->bytenr, buf->size, 1);
+ btrfs_free_extent(trans, root, buf->bytenr, buf->size,
+ root->root_key.objectid, root_gen, 0, 0, 1);
}
btrfs_block_release(root, buf);
return 0;
/* once for the root ptr */
btrfs_block_release(root, mid_buf);
clean_tree_block(trans, root, mid_buf);
- return btrfs_free_extent(trans, root, bytenr,
- root->nodesize, 1);
+ return btrfs_free_extent(trans, root, bytenr, root->nodesize,
+ root->root_key.objectid,
+ btrfs_header_generation(&mid->header),
+ 0, 0, 1);
}
parent = &parent_buf->node;
if (wret < 0)
ret = wret;
if (btrfs_header_nritems(&right->header) == 0) {
+ u64 generation;
u64 bytenr = right_buf->bytenr;
+
+ generation = btrfs_header_generation(&parent->header);
btrfs_block_release(root, right_buf);
clean_tree_block(trans, root, right_buf);
right_buf = NULL;
if (wret)
ret = wret;
wret = btrfs_free_extent(trans, root, bytenr,
- root->nodesize, 1);
+ root->nodesize,
+ root->root_key.objectid,
+ generation, 0, 0, 1);
if (wret)
ret = wret;
} else {
if (btrfs_header_nritems(&mid->header) == 0) {
/* we've managed to empty the middle node, drop it */
u64 bytenr = mid_buf->bytenr;
+ u64 generation;
+
+ generation = btrfs_header_generation(&parent->header);
btrfs_block_release(root, mid_buf);
clean_tree_block(trans, root, mid_buf);
mid_buf = NULL;
wret = del_ptr(trans, root, path, level + 1, pslot);
if (wret)
ret = wret;
- wret = btrfs_free_extent(trans, root, bytenr,
- root->nodesize, 1);
+ wret = btrfs_free_extent(trans, root, bytenr, root->nodesize,
+ root->root_key.objectid,
+ generation, 0, 0, 1);
if (wret)
ret = wret;
} else {
btrfs_set_header_level(&leaf->header, 0);
BUG_ON(list_empty(&leaf_buf->dirty));
} else {
+ u64 generation =
+ btrfs_header_generation(&path->nodes[1]->node.header);
+
clean_tree_block(trans, root, leaf_buf);
wret = del_ptr(trans, root, path, 1, path->slots[1]);
if (wret)
ret = wret;
- wret = btrfs_free_extent(trans, root,
- leaf_buf->bytenr,
- leaf_buf->size, 1);
+ wret = btrfs_free_extent(trans, root, leaf_buf->bytenr,
+ leaf_buf->size,
+ root->root_key.objectid,
+ generation, 0, 0, 1);
if (wret)
ret = wret;
}
}
if (btrfs_header_nritems(&leaf->header) == 0) {
u64 bytenr = leaf_buf->bytenr;
+ struct btrfs_buffer *parent = path->nodes[1];
+ u64 generation =
+ btrfs_header_generation(&parent->node.header);
+
clean_tree_block(trans, root, leaf_buf);
wret = del_ptr(trans, root, path, 1, slot);
if (wret)
ret = wret;
wret = btrfs_free_extent(trans, root, bytenr,
- leaf_buf->size, 1);
+ leaf_buf->size,
+ root->root_key.objectid,
+ generation, 0, 0, 1);
btrfs_block_release(root, leaf_buf);
if (wret)
ret = wret;
*/
struct btrfs_extent_item {
__le32 refs;
- __le64 owner;
+} __attribute__ ((__packed__));
+
+struct btrfs_extent_ref {
+ __le64 root;
+ __le64 generation;
+ __le64 objectid;
+ __le64 offset;
} __attribute__ ((__packed__));
struct btrfs_inode_timespec {
* are used, and how many references there are to each block
*/
#define BTRFS_EXTENT_ITEM_KEY 33
+#define BTRFS_EXTENT_REF_KEY 34
/*
* block groups give us hints into the extent allocation trees. Which
* blocks are free etc etc
*/
-#define BTRFS_BLOCK_GROUP_ITEM_KEY 34
+#define BTRFS_BLOCK_GROUP_ITEM_KEY 50
/*
* string items are for debugging. They just store a short string of
*/
#define BTRFS_STRING_ITEM_KEY 253
+#define BTRFS_SETGET_STACK_FUNCS(name, type, member, bits) \
+static inline u##bits btrfs_##name(type *s) \
+{ \
+ return le##bits##_to_cpu(s->member); \
+} \
+static inline void btrfs_set_##name(type *s, u##bits val) \
+{ \
+ s->member = cpu_to_le##bits(val); \
+}
static inline u64 btrfs_block_group_used(struct btrfs_block_group_item *bi)
{
ts->nsec = cpu_to_le32(val);
}
-static inline u32 btrfs_extent_refs(struct btrfs_extent_item *ei)
-{
- return le32_to_cpu(ei->refs);
-}
-
-static inline void btrfs_set_extent_refs(struct btrfs_extent_item *ei, u32 val)
-{
- ei->refs = cpu_to_le32(val);
-}
+BTRFS_SETGET_STACK_FUNCS(extent_refs, struct btrfs_extent_item, refs, 32);
-static inline u64 btrfs_extent_owner(struct btrfs_extent_item *ei)
-{
- return le64_to_cpu(ei->owner);
-}
-
-static inline void btrfs_set_extent_owner(struct btrfs_extent_item *ei, u64 val)
-{
- ei->owner = cpu_to_le64(val);
-}
+BTRFS_SETGET_STACK_FUNCS(ref_root, struct btrfs_extent_ref, root, 64);
+BTRFS_SETGET_STACK_FUNCS(ref_generation, struct btrfs_extent_ref,
+ generation, 64);
+BTRFS_SETGET_STACK_FUNCS(ref_objectid, struct btrfs_extent_ref, objectid, 64);
+BTRFS_SETGET_STACK_FUNCS(ref_offset, struct btrfs_extent_ref, offset, 64);
static inline u64 btrfs_node_blockptr(struct btrfs_node *n, int nr)
{
int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct btrfs_buffer *buf);
int btrfs_inc_root_ref(struct btrfs_trans_handle *trans,
- struct btrfs_root *root);
+ struct btrfs_root *root, u64 owner_objectid);
int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
- *root, u64 bytenr, u64 num_bytes, int pin);
+ *root, u64 bytenr, u64 num_bytes,
+ u64 root_objectid, u64 root_generation,
+ u64 owner, u64 owner_offset, int pin);
int btrfs_cow_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_buffer *buf,
struct btrfs_buffer *parent, int parent_slot,
#include "disk-io.h"
#include "print-tree.h"
#include "transaction.h"
+#include "crc32c.h"
static int finish_current_insert(struct btrfs_trans_handle *trans, struct
btrfs_root *extent_root);
static int run_pending(struct btrfs_trans_handle *trans, struct btrfs_root
*extent_root);
+static u64 hash_extent_ref(u64 root_objectid, u64 ref_generation,
+ u64 owner, u64 owner_offset)
+{
+ u32 high_crc = ~(u32)0;
+ u32 low_crc = ~(u32)0;
+ __le64 lenum;
+
+ lenum = cpu_to_le64(root_objectid);
+ high_crc = crc32c(high_crc, &lenum, sizeof(lenum));
+ lenum = cpu_to_le64(ref_generation);
+ low_crc = crc32c(low_crc, &lenum, sizeof(lenum));
+
+#if 0
+ lenum = cpu_to_le64(owner);
+ low_crc = crc32c(low_crc, &lenum, sizeof(lenum));
+ lenum = cpu_to_le64(owner_offset);
+ low_crc = crc32c(low_crc, &lenum, sizeof(lenum));
+#endif
+ return ((u64)high_crc << 32) | (u64)low_crc;
+}
+
+static int match_extent_ref(struct btrfs_extent_ref *disk_ref,
+ struct btrfs_extent_ref *cpu_ref)
+{
+ int ret = memcmp(cpu_ref, disk_ref, sizeof(*cpu_ref));
+ return ret == 0;
+}
+
+static int lookup_extent_backref(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path, u64 bytenr,
+ u64 root_objectid, u64 ref_generation,
+ u64 owner, u64 owner_offset, int del)
+{
+ u64 hash;
+ struct btrfs_key key;
+ struct btrfs_key found_key;
+ struct btrfs_extent_ref ref;
+ struct btrfs_buffer *leaf;
+ struct btrfs_extent_ref *disk_ref;
+ int ret;
+ int ret2;
+
+ btrfs_set_ref_root(&ref, root_objectid);
+ btrfs_set_ref_generation(&ref, ref_generation);
+ btrfs_set_ref_objectid(&ref, owner);
+ btrfs_set_ref_offset(&ref, owner_offset);
+
+ hash = hash_extent_ref(root_objectid, ref_generation, owner,
+ owner_offset);
+ key.offset = hash;
+ key.objectid = bytenr;
+ key.type = BTRFS_EXTENT_REF_KEY;
+
+ while (1) {
+ ret = btrfs_search_slot(trans, root, &key, path,
+ del ? -1 : 0, del);
+ if (ret < 0)
+ goto out;
+ leaf = path->nodes[0];
+ if (ret != 0) {
+ u32 nritems = btrfs_header_nritems(&leaf->node.header);
+ if (path->slots[0] >= nritems) {
+ ret2 = btrfs_next_leaf(root, path);
+ if (ret2)
+ goto out;
+ leaf = path->nodes[0];
+ }
+ btrfs_disk_key_to_cpu(&found_key,
+ &leaf->leaf.items[path->slots[0]].key);
+ if (found_key.objectid != bytenr ||
+ found_key.type != BTRFS_EXTENT_REF_KEY)
+ goto out;
+ key.offset = found_key.offset;
+ if (del) {
+ btrfs_release_path(root, path);
+ continue;
+ }
+ }
+ disk_ref = btrfs_item_ptr(&path->nodes[0]->leaf,
+ path->slots[0],
+ struct btrfs_extent_ref);
+ if (match_extent_ref(disk_ref, &ref)) {
+ ret = 0;
+ goto out;
+ }
+ btrfs_disk_key_to_cpu(&found_key,
+ &leaf->leaf.items[path->slots[0]].key);
+ key.offset = found_key.offset + 1;
+ btrfs_release_path(root, path);
+ }
+out:
+ return ret;
+}
+
+static int insert_extent_backref(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path, u64 bytenr,
+ u64 root_objectid, u64 ref_generation,
+ u64 owner, u64 owner_offset)
+{
+ u64 hash;
+ struct btrfs_key key;
+ struct btrfs_extent_ref ref;
+ struct btrfs_extent_ref *disk_ref;
+ int ret;
+
+ btrfs_set_ref_root(&ref, root_objectid);
+ btrfs_set_ref_generation(&ref, ref_generation);
+ btrfs_set_ref_objectid(&ref, owner);
+ btrfs_set_ref_offset(&ref, owner_offset);
+
+ hash = hash_extent_ref(root_objectid, ref_generation, owner,
+ owner_offset);
+ key.offset = hash;
+ key.objectid = bytenr;
+ key.type = BTRFS_EXTENT_REF_KEY;
+
+ ret = btrfs_insert_empty_item(trans, root, path, &key, sizeof(ref));
+ while (ret == -EEXIST) {
+ disk_ref = btrfs_item_ptr(&path->nodes[0]->leaf, path->slots[0],
+ struct btrfs_extent_ref);
+ if (match_extent_ref(disk_ref, &ref))
+ goto out;
+ key.offset++;
+ ret = btrfs_insert_empty_item(trans, root, path, &key,
+ sizeof(ref));
+ }
+ if (ret)
+ goto out;
+ disk_ref = btrfs_item_ptr(&path->nodes[0]->leaf, path->slots[0],
+ struct btrfs_extent_ref);
+ memcpy(disk_ref, &ref, sizeof(ref));
+ dirty_tree_block(trans, root, path->nodes[0]);
+out:
+ btrfs_release_path(root, path);
+ return ret;
+}
+
static int inc_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root
- *root, u64 bytenr, u32 blocksize)
+ *root, u64 bytenr, u32 blocksize,
+ u64 root_objectid, u64 ref_generation,
+ u64 owner, u64 owner_offset)
{
struct btrfs_path path;
int ret;
BUG_ON(list_empty(&path.nodes[0]->dirty));
btrfs_release_path(root->fs_info->extent_root, &path);
+
+ ret = insert_extent_backref(trans, root->fs_info->extent_root, &path,
+ bytenr, root_objectid, ref_generation,
+ owner, owner_offset);
+ BUG_ON(ret);
+
finish_current_insert(trans, root->fs_info->extent_root);
run_pending(trans, root->fs_info->extent_root);
return 0;
struct btrfs_key key;
struct btrfs_leaf *l;
struct btrfs_extent_item *item;
+
btrfs_init_path(&path);
+
key.objectid = bytenr;
key.offset = blocksize;
btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
for (i = 0; i < btrfs_header_nritems(&buf->node.header); i++) {
bytenr = btrfs_node_blockptr(&buf->node, i);
- inc_block_ref(trans, root, bytenr, blocksize);
+ inc_block_ref(trans, root, bytenr, blocksize,
+ root->root_key.objectid, trans->transid, 0, 0);
}
return 0;
}
int btrfs_inc_root_ref(struct btrfs_trans_handle *trans,
- struct btrfs_root *root)
+ struct btrfs_root *root, u64 owner_objectid)
{
return inc_block_ref(trans, root, root->node->bytenr,
- root->node->size);
+ root->node->size, owner_objectid,
+ trans->transid, 0, 0);
}
static int write_one_cache_group(struct btrfs_trans_handle *trans,
struct cache_extent *pe;
struct cache_extent *next;
struct cache_tree *pending_tree = &info->pending_tree;
+ struct btrfs_path path;
+ btrfs_init_path(&path);
btrfs_set_extent_refs(&extent_item, 1);
- btrfs_set_extent_owner(&extent_item, extent_root->root_key.objectid);
ins.offset = 1;
btrfs_set_key_type(&ins, BTRFS_EXTENT_ITEM_KEY);
pe = find_first_cache_extent(pending_tree, 0);
btrfs_print_tree(extent_root, extent_root->node);
}
BUG_ON(ret);
+
+ ret = insert_extent_backref(trans, extent_root, &path,
+ ins.objectid,
+ extent_root->root_key.objectid,
+ 0, 0, 0);
+ BUG_ON(ret);
}
return 0;
}
* remove an extent from the root, returns 0 on success
*/
static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
- *root, u64 bytenr, u64 num_bytes, int pin)
+ *root, u64 bytenr, u64 num_bytes,
+ u64 root_objectid, u64 ref_generation,
+ u64 owner_objectid, u64 owner_offset, int pin)
{
struct btrfs_path path;
struct btrfs_key key;
key.offset = num_bytes;
btrfs_init_path(&path);
+
+ ret = lookup_extent_backref(trans, extent_root, &path,
+ bytenr, root_objectid,
+ ref_generation,
+ owner_objectid, owner_offset, 1);
+ if (ret == 0) {
+ ret = btrfs_del_item(trans, extent_root, &path);
+ } else {
+ // FIXME deal with missing references here
+ }
+
+ btrfs_release_path(extent_root, &path);
+
ret = btrfs_search_slot(trans, extent_root, &key, &path, -1, 1);
if (ret) {
btrfs_print_tree(extent_root, extent_root->node);
while(pe) {
remove_cache_extent(del_pending, pe);
ret = __free_extent(trans, extent_root,
- pe->start, pe->size, 1);
+ pe->start, pe->size,
+ extent_root->root_key.objectid,
+ 0, 0, 0, 1);
BUG_ON(ret);
next = next_cache_extent(pe);
if (!next)
* remove an extent from the root, returns 0 on success
*/
int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
- *root, u64 bytenr, u64 num_bytes, int pin)
+ *root, u64 bytenr, u64 num_bytes,
+ u64 root_objectid, u64 root_generation,
+ u64 owner_objectid, u64 owner_offset, int pin)
{
struct btrfs_root *extent_root = root->fs_info->extent_root;
int pending_ret;
int ret;
+ if (!root->ref_cows)
+ root_generation = 0;
+
if (root == extent_root) {
ret = insert_cache_extent(&root->fs_info->del_pending,
bytenr, num_bytes);
BUG_ON(ret);
return 0;
}
- ret = __free_extent(trans, root, bytenr, num_bytes, pin);
+ ret = __free_extent(trans, root, bytenr, num_bytes,
+ root_objectid, root_generation, owner_objectid,
+ owner_offset, pin);
pending_ret = run_pending(trans, root->fs_info->extent_root);
return ret ? ret : pending_ret;
}
* returns 0 if everything worked, non-zero otherwise.
*/
static int alloc_extent(struct btrfs_trans_handle *trans,
- struct btrfs_root *root, u64 owner,
- u64 num_bytes, u64 search_start,
+ struct btrfs_root *root, u64 num_bytes,
+ u64 root_objectid, u64 ref_generation, u64 owner,
+ u64 owner_offset, u64 search_start,
u64 search_end, struct btrfs_key *ins)
{
int ret;
struct btrfs_fs_info *info = root->fs_info;
struct btrfs_root *extent_root = info->extent_root;
struct btrfs_extent_item extent_item;
+ struct btrfs_path path;
+
+ btrfs_init_path(&path);
btrfs_set_extent_refs(&extent_item, 1);
- btrfs_set_extent_owner(&extent_item, owner);
ret = find_free_extent(trans, root, num_bytes, search_start,
search_end, ins);
ret = btrfs_insert_item(trans, extent_root, ins, &extent_item,
sizeof(extent_item));
+ BUG_ON(ret);
+ ret = insert_extent_backref(trans, extent_root, &path, ins->objectid,
+ root_objectid, ref_generation,
+ owner, owner_offset);
+ BUG_ON(ret);
+
finish_current_insert(trans, extent_root);
pending_ret = run_pending(trans, extent_root);
if (ret)
struct btrfs_root *root,
u32 blocksize)
{
+ u64 ref_generation;
struct btrfs_key ins;
int ret;
struct btrfs_buffer *buf;
- ret = alloc_extent(trans, root, root->root_key.objectid,
- blocksize, 0, (u64)-1, &ins);
+
+ if (root->ref_cows)
+ ref_generation = trans->transid;
+ else
+ ref_generation = 0;
+
+ ret = alloc_extent(trans, root, blocksize,
+ root->root_key.objectid, ref_generation,
+ 0, 0, 0, (u64)-1, &ins);
if (ret) {
BUG();
return NULL;
{
struct btrfs_buffer *next;
struct btrfs_buffer *cur;
+ struct btrfs_buffer *parent;
+ u64 root_owner;
+ u64 root_gen;
u64 bytenr;
int ret;
u32 refs;
bytenr = btrfs_node_blockptr(&cur->node, path->slots[*level]);
ret = lookup_block_ref(trans, root, bytenr, size, &refs);
if (refs != 1 || *level == 1) {
+ parent = path->nodes[*level];
+ root_owner = btrfs_header_owner(&parent->node.header);
+ root_gen =
+ btrfs_header_generation(&parent->node.header);
path->slots[*level]++;
- ret = btrfs_free_extent(trans, root, bytenr, size, 1);
+ ret = btrfs_free_extent(trans, root, bytenr, size,
+ root_owner, root_gen, 0, 0, 1);
BUG_ON(ret);
continue;
}
path->slots[*level] = 0;
}
out:
+ if (*level == BTRFS_MAX_LEVEL - 1 || !path->nodes[*level + 1])
+ parent = path->nodes[*level];
+ else
+ parent = path->nodes[*level + 1];
+
+ root_owner = btrfs_header_owner(&parent->node.header);
+ root_gen = btrfs_header_generation(&parent->node.header);
ret = btrfs_free_extent(trans, root, path->nodes[*level]->bytenr,
- btrfs_level_size(root, *level), 1);
+ btrfs_level_size(root, *level),
+ root_owner, root_gen, 0, 0, 1);
btrfs_block_release(root, path->nodes[*level]);
path->nodes[*level] = NULL;
*level += 1;
int i;
int slot;
int ret;
+ u64 root_owner;
+ u64 root_gen;
+ struct btrfs_buffer *parent;
for(i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) {
slot = path->slots[i];
if (slot <
*level = i;
return 0;
} else {
+ if (path->nodes[*level] == root->node)
+ parent = path->nodes[*level];
+ else
+ parent = path->nodes[*level + 1];
+
+ root_owner = btrfs_header_owner(&parent->node.header);
+ root_gen =
+ btrfs_header_generation(&parent->node.header);
ret = btrfs_free_extent(trans, root,
path->nodes[*level]->bytenr,
- btrfs_level_size(root, *level), 1);
+ btrfs_level_size(root, *level),
+ root_owner, root_gen, 0, 0, 1);
btrfs_block_release(root, path->nodes[*level]);
path->nodes[*level] = NULL;
*level = i + 1;