Btrfs: Add more synchronization before creating a snapshot
authorChris Mason <chris.mason@oracle.com>
Mon, 10 Sep 2007 23:58:36 +0000 (19:58 -0400)
committerDavid Woodhouse <dwmw2@hera.kernel.org>
Mon, 10 Sep 2007 23:58:36 +0000 (19:58 -0400)
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: Chris Mason <chris.mason@oracle.com>
fs/btrfs/ctree.h
fs/btrfs/disk-io.c
fs/btrfs/file.c
fs/btrfs/inode.c

index cd75c906048f288396b96d4c8cf0bfb85302745e..03e1c4ad341b03ad8a5c0a71795809e2f3200a74 100644 (file)
@@ -333,6 +333,7 @@ struct btrfs_root {
        struct inode *inode;
        struct kobject root_kobj;
        struct completion kobj_unregister;
+       struct rw_semaphore snap_sem;
        u64 objectid;
        u64 last_trans;
        u32 blocksize;
index c25ef0a68f18dce2c2cb18218348db76aeba6ad5..6c953a0e0aa3d14d19265112f814ecf5265ee121 100644 (file)
@@ -300,6 +300,7 @@ static int __setup_root(int blocksize,
        memset(&root->defrag_progress, 0, sizeof(root->defrag_progress));
        memset(&root->root_kobj, 0, sizeof(root->root_kobj));
        init_completion(&root->kobj_unregister);
+       init_rwsem(&root->snap_sem);
        root->defrag_running = 0;
        root->defrag_level = 0;
        root->root_key.objectid = objectid;
index acef8cb7fb791253c4de3a2bb31c73eeb2b83679..77f1950b0aa02c89fc1aec4c786ce7d279dd164c 100644 (file)
@@ -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) >>
                        inode->i_blkbits;
 
+       down_read(&BTRFS_I(inode)->root->snap_sem);
        end_of_last_block = start_pos + (num_blocks << inode->i_blkbits) - 1;
        lock_extent(em_tree, start_pos, end_of_last_block, GFP_NOFS);
        mutex_lock(&root->fs_info->fs_mutex);
@@ -250,6 +251,7 @@ out_unlock:
        mutex_unlock(&root->fs_info->fs_mutex);
        unlock_extent(em_tree, start_pos, end_of_last_block, GFP_NOFS);
        free_extent_map(em);
+       up_read(&BTRFS_I(inode)->root->snap_sem);
        return err;
 }
 
index 6b3e4404dc6a0e088a1224cf6357de84886c62fb..1ace6d11e09766a9294f8a3210f18ba0d49cab10 100644 (file)
@@ -686,6 +686,7 @@ static int btrfs_truncate_page(struct address_space *mapping, loff_t from)
        if ((offset & (blocksize - 1)) == 0)
                goto out;
 
+       down_read(&BTRFS_I(inode)->root->snap_sem);
        ret = -ENOMEM;
        page = grab_cache_page(mapping, index);
        if (!page)
@@ -704,6 +705,7 @@ static int btrfs_truncate_page(struct address_space *mapping, loff_t from)
 
        unlock_page(page);
        page_cache_release(page);
+       up_read(&BTRFS_I(inode)->root->snap_sem);
 out:
        return ret;
 }
@@ -1668,6 +1670,7 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct page *page)
        int ret = -EINVAL;
        u64 page_start;
 
+       down_read(&BTRFS_I(inode)->root->snap_sem);
        lock_page(page);
        wait_on_page_writeback(page);
        size = i_size_read(inode);
@@ -1688,6 +1691,7 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct page *page)
        ret = btrfs_cow_one_page(inode, page, end);
 
 out_unlock:
+       up_read(&BTRFS_I(inode)->root->snap_sem);
        unlock_page(page);
        return ret;
 }
@@ -1851,6 +1855,10 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen)
        if (!root->ref_cows)
                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);
        trans = btrfs_start_transaction(root, 1);
        BUG_ON(!trans);
@@ -1894,12 +1902,12 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen)
        ret = btrfs_inc_root_ref(trans, root);
        if (ret)
                goto fail;
-
 fail:
        err = btrfs_commit_transaction(trans, root);
        if (err && !ret)
                ret = err;
        mutex_unlock(&root->fs_info->fs_mutex);
+       up_write(&root->snap_sem);
        btrfs_btree_balance_dirty(root);
        return ret;
 }