Commit 3063d29f authored by Chris Mason's avatar Chris Mason

Btrfs: Move snapshot creation to commit time

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: default avatarChris Mason <chris.mason@oracle.com>
parent dc17ff8f
...@@ -2250,13 +2250,10 @@ static int noinline create_subvol(struct btrfs_root *root, char *name, ...@@ -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) 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_trans_handle *trans;
struct btrfs_key key;
struct btrfs_root_item new_root_item;
struct extent_buffer *tmp;
int ret; int ret;
int err; int err;
u64 objectid;
unsigned long nr = 0; unsigned long nr = 0;
if (!root->ref_cows) if (!root->ref_cows)
...@@ -2267,72 +2264,26 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen) ...@@ -2267,72 +2264,26 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen)
if (ret) if (ret)
goto fail_unlock; 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); trans = btrfs_start_transaction(root, 1);
BUG_ON(!trans); 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); 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); err = btrfs_commit_transaction(trans, root);
if (err && !ret)
ret = err;
fail_unlock: fail_unlock:
mutex_unlock(&root->fs_info->fs_mutex); mutex_unlock(&root->fs_info->fs_mutex);
btrfs_btree_balance_dirty(root, nr); btrfs_btree_balance_dirty(root, nr);
......
...@@ -66,6 +66,7 @@ static int join_transaction(struct btrfs_root *root) ...@@ -66,6 +66,7 @@ static int join_transaction(struct btrfs_root *root)
cur_trans->use_count = 1; cur_trans->use_count = 1;
cur_trans->commit_done = 0; cur_trans->commit_done = 0;
cur_trans->start_time = get_seconds(); cur_trans->start_time = get_seconds();
INIT_LIST_HEAD(&cur_trans->pending_snapshots);
list_add_tail(&cur_trans->list, &root->fs_info->trans_list); list_add_tail(&cur_trans->list, &root->fs_info->trans_list);
btrfs_ordered_inode_tree_init(&cur_trans->ordered_inode_tree); btrfs_ordered_inode_tree_init(&cur_trans->ordered_inode_tree);
extent_map_tree_init(&cur_trans->dirty_pages, extent_map_tree_init(&cur_trans->dirty_pages,
...@@ -481,10 +482,8 @@ int btrfs_write_ordered_inodes(struct btrfs_trans_handle *trans, ...@@ -481,10 +482,8 @@ int btrfs_write_ordered_inodes(struct btrfs_trans_handle *trans,
struct inode *inode; struct inode *inode;
u64 root_objectid = 0; u64 root_objectid = 0;
u64 objectid = 0; u64 objectid = 0;
u64 transid = trans->transid;
int ret; int ret;
printk("write ordered trans %Lu\n", transid);
while(1) { while(1) {
ret = btrfs_find_first_ordered_inode( ret = btrfs_find_first_ordered_inode(
&cur_trans->ordered_inode_tree, &cur_trans->ordered_inode_tree,
...@@ -524,7 +523,80 @@ printk("write ordered trans %Lu\n", transid); ...@@ -524,7 +523,80 @@ printk("write ordered trans %Lu\n", transid);
mutex_lock(&root->fs_info->fs_mutex); mutex_lock(&root->fs_info->fs_mutex);
mutex_lock(&root->fs_info->trans_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; return 0;
} }
...@@ -610,6 +682,9 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, ...@@ -610,6 +682,9 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
} while (cur_trans->num_writers > 1 || } while (cur_trans->num_writers > 1 ||
(cur_trans->num_joined != joined)); (cur_trans->num_joined != joined));
ret = create_pending_snapshots(trans, root->fs_info);
BUG_ON(ret);
WARN_ON(cur_trans != trans->transaction); WARN_ON(cur_trans != trans->transaction);
ret = add_dirty_roots(trans, &root->fs_info->fs_roots_radix, ret = add_dirty_roots(trans, &root->fs_info->fs_roots_radix,
......
...@@ -34,6 +34,7 @@ struct btrfs_transaction { ...@@ -34,6 +34,7 @@ struct btrfs_transaction {
struct btrfs_ordered_inode_tree ordered_inode_tree; struct btrfs_ordered_inode_tree ordered_inode_tree;
wait_queue_head_t writer_wait; wait_queue_head_t writer_wait;
wait_queue_head_t commit_wait; wait_queue_head_t commit_wait;
struct list_head pending_snapshots;
}; };
struct btrfs_trans_handle { struct btrfs_trans_handle {
...@@ -46,6 +47,12 @@ struct btrfs_trans_handle { ...@@ -46,6 +47,12 @@ struct btrfs_trans_handle {
u64 alloc_exclude_nr; 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, static inline void btrfs_set_trans_block_group(struct btrfs_trans_handle *trans,
struct inode *inode) struct inode *inode)
......
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