summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/btrfs/ctree.h1
-rw-r--r--fs/btrfs/disk-io.c1
-rw-r--r--fs/btrfs/file.c2
-rw-r--r--fs/btrfs/inode.c10
4 files changed, 13 insertions, 1 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index cd75c906048f..03e1c4ad341b 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -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;
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index c25ef0a68f18..6c953a0e0aa3 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -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;
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index acef8cb7fb79..77f1950b0aa0 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -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;
}
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 6b3e4404dc6a..1ace6d11e097 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -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;
}