Skip to content

Commit

Permalink
Btrfs: Move snapshot creation to commit time
Browse files Browse the repository at this point in the history
It is very difficult to create a consistent snapshot of the btree when
other writers may update the btree before the commit is done.

This changes the snapshot creation to happen during the commit, while
no other updates are possible.

Signed-off-by: Chris Mason <chris.mason@oracle.com>
  • Loading branch information
Chris Mason committed Sep 25, 2008
1 parent dc17ff8 commit 3063d29
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 67 deletions.
79 changes: 15 additions & 64 deletions fs/btrfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -2250,13 +2250,10 @@ static int noinline create_subvol(struct btrfs_root *root, char *name,

static int create_snapshot(struct btrfs_root *root, char *name, int namelen)
{
struct btrfs_pending_snapshot *pending_snapshot;
struct btrfs_trans_handle *trans;
struct btrfs_key key;
struct btrfs_root_item new_root_item;
struct extent_buffer *tmp;
int ret;
int err;
u64 objectid;
unsigned long nr = 0;

if (!root->ref_cows)
Expand All @@ -2267,72 +2264,26 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen)
if (ret)
goto fail_unlock;

pending_snapshot = kmalloc(sizeof(*pending_snapshot), GFP_NOFS);
if (!pending_snapshot) {
ret = -ENOMEM;
goto fail_unlock;
}
pending_snapshot->name = kstrndup(name, namelen, GFP_NOFS);
if (!pending_snapshot->name) {
ret = -ENOMEM;
kfree(pending_snapshot);
goto fail_unlock;
}
trans = btrfs_start_transaction(root, 1);
BUG_ON(!trans);
err = btrfs_commit_transaction(trans, root);

trans = btrfs_start_transaction(root, 1);

pending_snapshot->root = root;
list_add(&pending_snapshot->list,
&trans->transaction->pending_snapshots);
ret = btrfs_update_inode(trans, root, root->inode);
if (ret)
goto fail;

ret = btrfs_find_free_objectid(trans, root->fs_info->tree_root,
0, &objectid);
if (ret)
goto fail; memcpy(&new_root_item, &root->root_item,
sizeof(new_root_item));

key.objectid = objectid;
key.offset = 1;
btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);

extent_buffer_get(root->node);
btrfs_cow_block(trans, root, root->node, NULL, 0, &tmp);
free_extent_buffer(tmp);

/* write the ordered inodes to force all delayed allocations to
* be filled. Once this is done, we can copy the root
*/
mutex_lock(&root->fs_info->trans_mutex);
btrfs_write_ordered_inodes(trans, root);
mutex_unlock(&root->fs_info->trans_mutex);

btrfs_copy_root(trans, root, root->node, &tmp, objectid);

btrfs_set_root_bytenr(&new_root_item, tmp->start);
btrfs_set_root_level(&new_root_item, btrfs_header_level(tmp));
ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key,
&new_root_item);
printk("new root %Lu node %Lu\n", objectid, tmp->start);
free_extent_buffer(tmp);
if (ret)
goto fail;

/*
* insert the directory item
*/
key.offset = (u64)-1;
ret = btrfs_insert_dir_item(trans, root->fs_info->tree_root,
name, namelen,
root->fs_info->sb->s_root->d_inode->i_ino,
&key, BTRFS_FT_DIR);

if (ret)
goto fail;

ret = btrfs_insert_inode_ref(trans, root->fs_info->tree_root,
name, namelen, objectid,
root->fs_info->sb->s_root->d_inode->i_ino);

if (ret)
goto fail;
fail:
nr = trans->blocks_used;
err = btrfs_commit_transaction(trans, root);

if (err && !ret)
ret = err;
fail_unlock:
mutex_unlock(&root->fs_info->fs_mutex);
btrfs_btree_balance_dirty(root, nr);
Expand Down
81 changes: 78 additions & 3 deletions fs/btrfs/transaction.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ static int join_transaction(struct btrfs_root *root)
cur_trans->use_count = 1;
cur_trans->commit_done = 0;
cur_trans->start_time = get_seconds();
INIT_LIST_HEAD(&cur_trans->pending_snapshots);
list_add_tail(&cur_trans->list, &root->fs_info->trans_list);
btrfs_ordered_inode_tree_init(&cur_trans->ordered_inode_tree);
extent_map_tree_init(&cur_trans->dirty_pages,
Expand Down Expand Up @@ -481,10 +482,8 @@ int btrfs_write_ordered_inodes(struct btrfs_trans_handle *trans,
struct inode *inode;
u64 root_objectid = 0;
u64 objectid = 0;
u64 transid = trans->transid;
int ret;

printk("write ordered trans %Lu\n", transid);
while(1) {
ret = btrfs_find_first_ordered_inode(
&cur_trans->ordered_inode_tree,
Expand Down Expand Up @@ -524,7 +523,80 @@ printk("write ordered trans %Lu\n", transid);
mutex_lock(&root->fs_info->fs_mutex);
mutex_lock(&root->fs_info->trans_mutex);
}
printk("done write ordered trans %Lu\n", transid);
return 0;
}

static int create_pending_snapshot(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info,
struct btrfs_pending_snapshot *pending)
{
struct btrfs_key key;
struct btrfs_root_item new_root_item;
struct btrfs_root *tree_root = fs_info->tree_root;
struct btrfs_root *root = pending->root;
struct extent_buffer *tmp;
int ret;
u64 objectid;

ret = btrfs_find_free_objectid(trans, tree_root, 0, &objectid);
if (ret)
goto fail;

memcpy(&new_root_item, &root->root_item, sizeof(new_root_item));

key.objectid = objectid;
key.offset = 1;
btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);

extent_buffer_get(root->node);
btrfs_cow_block(trans, root, root->node, NULL, 0, &tmp);
free_extent_buffer(tmp);

btrfs_copy_root(trans, root, root->node, &tmp, objectid);

btrfs_set_root_bytenr(&new_root_item, tmp->start);
btrfs_set_root_level(&new_root_item, btrfs_header_level(tmp));
ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key,
&new_root_item);
free_extent_buffer(tmp);
if (ret)
goto fail;

/*
* insert the directory item
*/
key.offset = (u64)-1;
ret = btrfs_insert_dir_item(trans, root->fs_info->tree_root,
pending->name, strlen(pending->name),
root->fs_info->sb->s_root->d_inode->i_ino,
&key, BTRFS_FT_DIR);

if (ret)
goto fail;

ret = btrfs_insert_inode_ref(trans, root->fs_info->tree_root,
pending->name, strlen(pending->name), objectid,
root->fs_info->sb->s_root->d_inode->i_ino);
fail:
return ret;
}

static int create_pending_snapshots(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info)
{
struct btrfs_pending_snapshot *pending;
struct list_head *head = &trans->transaction->pending_snapshots;
int ret;

while(!list_empty(head)) {
pending = list_entry(head->next,
struct btrfs_pending_snapshot, list);
ret = create_pending_snapshot(trans, fs_info, pending);
BUG_ON(ret);
list_del(&pending->list);
kfree(pending->name);
kfree(pending);
}
return 0;
}

Expand Down Expand Up @@ -610,6 +682,9 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
} while (cur_trans->num_writers > 1 ||
(cur_trans->num_joined != joined));

ret = create_pending_snapshots(trans, root->fs_info);
BUG_ON(ret);

WARN_ON(cur_trans != trans->transaction);

ret = add_dirty_roots(trans, &root->fs_info->fs_roots_radix,
Expand Down
7 changes: 7 additions & 0 deletions fs/btrfs/transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ struct btrfs_transaction {
struct btrfs_ordered_inode_tree ordered_inode_tree;
wait_queue_head_t writer_wait;
wait_queue_head_t commit_wait;
struct list_head pending_snapshots;
};

struct btrfs_trans_handle {
Expand All @@ -46,6 +47,12 @@ struct btrfs_trans_handle {
u64 alloc_exclude_nr;
};

struct btrfs_pending_snapshot {
struct btrfs_root *root;
char *name;
struct list_head list;
};


static inline void btrfs_set_trans_block_group(struct btrfs_trans_handle *trans,
struct inode *inode)
Expand Down

0 comments on commit 3063d29

Please sign in to comment.