mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
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: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
parent
dc17ff8f11
commit
3063d29f2a
@ -2250,13 +2250,10 @@ fail_commit:
|
||||
|
||||
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)
|
||||
@ -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);
|
||||
|
@ -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,
|
||||
@ -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,
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
@ -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 {
|
||||
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user