Commit 011410bd authored by Chris Mason's avatar Chris Mason Committed by David Woodhouse

Btrfs: Add more synchronization before creating a snapshot

File data checksums are only done during writepage, so we have to make sure
all pages are written when the snapshot is taken.  This also adds some
locking so that new writes don't race in and add new dirty pages.
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 86479a04
...@@ -333,6 +333,7 @@ struct btrfs_root { ...@@ -333,6 +333,7 @@ struct btrfs_root {
struct inode *inode; struct inode *inode;
struct kobject root_kobj; struct kobject root_kobj;
struct completion kobj_unregister; struct completion kobj_unregister;
struct rw_semaphore snap_sem;
u64 objectid; u64 objectid;
u64 last_trans; u64 last_trans;
u32 blocksize; u32 blocksize;
......
...@@ -300,6 +300,7 @@ static int __setup_root(int blocksize, ...@@ -300,6 +300,7 @@ static int __setup_root(int blocksize,
memset(&root->defrag_progress, 0, sizeof(root->defrag_progress)); memset(&root->defrag_progress, 0, sizeof(root->defrag_progress));
memset(&root->root_kobj, 0, sizeof(root->root_kobj)); memset(&root->root_kobj, 0, sizeof(root->root_kobj));
init_completion(&root->kobj_unregister); init_completion(&root->kobj_unregister);
init_rwsem(&root->snap_sem);
root->defrag_running = 0; root->defrag_running = 0;
root->defrag_level = 0; root->defrag_level = 0;
root->root_key.objectid = objectid; root->root_key.objectid = objectid;
......
...@@ -160,6 +160,7 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans, ...@@ -160,6 +160,7 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
num_blocks = (write_bytes + pos - start_pos + root->blocksize - 1) >> num_blocks = (write_bytes + pos - start_pos + root->blocksize - 1) >>
inode->i_blkbits; inode->i_blkbits;
down_read(&BTRFS_I(inode)->root->snap_sem);
end_of_last_block = start_pos + (num_blocks << inode->i_blkbits) - 1; end_of_last_block = start_pos + (num_blocks << inode->i_blkbits) - 1;
lock_extent(em_tree, start_pos, end_of_last_block, GFP_NOFS); lock_extent(em_tree, start_pos, end_of_last_block, GFP_NOFS);
mutex_lock(&root->fs_info->fs_mutex); mutex_lock(&root->fs_info->fs_mutex);
...@@ -250,6 +251,7 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans, ...@@ -250,6 +251,7 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
mutex_unlock(&root->fs_info->fs_mutex); mutex_unlock(&root->fs_info->fs_mutex);
unlock_extent(em_tree, start_pos, end_of_last_block, GFP_NOFS); unlock_extent(em_tree, start_pos, end_of_last_block, GFP_NOFS);
free_extent_map(em); free_extent_map(em);
up_read(&BTRFS_I(inode)->root->snap_sem);
return err; return err;
} }
......
...@@ -686,6 +686,7 @@ static int btrfs_truncate_page(struct address_space *mapping, loff_t from) ...@@ -686,6 +686,7 @@ static int btrfs_truncate_page(struct address_space *mapping, loff_t from)
if ((offset & (blocksize - 1)) == 0) if ((offset & (blocksize - 1)) == 0)
goto out; goto out;
down_read(&BTRFS_I(inode)->root->snap_sem);
ret = -ENOMEM; ret = -ENOMEM;
page = grab_cache_page(mapping, index); page = grab_cache_page(mapping, index);
if (!page) if (!page)
...@@ -704,6 +705,7 @@ static int btrfs_truncate_page(struct address_space *mapping, loff_t from) ...@@ -704,6 +705,7 @@ static int btrfs_truncate_page(struct address_space *mapping, loff_t from)
unlock_page(page); unlock_page(page);
page_cache_release(page); page_cache_release(page);
up_read(&BTRFS_I(inode)->root->snap_sem);
out: out:
return ret; return ret;
} }
...@@ -1668,6 +1670,7 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct page *page) ...@@ -1668,6 +1670,7 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct page *page)
int ret = -EINVAL; int ret = -EINVAL;
u64 page_start; u64 page_start;
down_read(&BTRFS_I(inode)->root->snap_sem);
lock_page(page); lock_page(page);
wait_on_page_writeback(page); wait_on_page_writeback(page);
size = i_size_read(inode); size = i_size_read(inode);
...@@ -1688,6 +1691,7 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct page *page) ...@@ -1688,6 +1691,7 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct page *page)
ret = btrfs_cow_one_page(inode, page, end); ret = btrfs_cow_one_page(inode, page, end);
out_unlock: out_unlock:
up_read(&BTRFS_I(inode)->root->snap_sem);
unlock_page(page); unlock_page(page);
return ret; return ret;
} }
...@@ -1851,6 +1855,10 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen) ...@@ -1851,6 +1855,10 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen)
if (!root->ref_cows) if (!root->ref_cows)
return -EINVAL; return -EINVAL;
down_write(&root->snap_sem);
freeze_bdev(root->fs_info->sb->s_bdev);
thaw_bdev(root->fs_info->sb->s_bdev, root->fs_info->sb);
mutex_lock(&root->fs_info->fs_mutex); mutex_lock(&root->fs_info->fs_mutex);
trans = btrfs_start_transaction(root, 1); trans = btrfs_start_transaction(root, 1);
BUG_ON(!trans); BUG_ON(!trans);
...@@ -1894,12 +1902,12 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen) ...@@ -1894,12 +1902,12 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen)
ret = btrfs_inc_root_ref(trans, root); ret = btrfs_inc_root_ref(trans, root);
if (ret) if (ret)
goto fail; goto fail;
fail: fail:
err = btrfs_commit_transaction(trans, root); err = btrfs_commit_transaction(trans, root);
if (err && !ret) if (err && !ret)
ret = err; ret = err;
mutex_unlock(&root->fs_info->fs_mutex); mutex_unlock(&root->fs_info->fs_mutex);
up_write(&root->snap_sem);
btrfs_btree_balance_dirty(root); btrfs_btree_balance_dirty(root);
return ret; return ret;
} }
......
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