aboutsummaryrefslogtreecommitdiffstats
path: root/fs/buffer.c
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2011-05-23 18:23:35 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2011-05-26 07:26:45 -0400
commitea13a86463fd0c26c2c209c53dc46b8eff81bad4 (patch)
tree405ca6ff2b6ca3078a1023814c1ea7525cf03797 /fs/buffer.c
parent24da4fab5a617ecbf0f0c64e7ba7703383faa411 (diff)
vfs: Block mmapped writes while the fs is frozen
We should not allow file modification via mmap while the filesystem is frozen. So block in block_page_mkwrite() while the filesystem is frozen. We cannot do the blocking wait in __block_page_mkwrite() since e.g. ext4 will want to call that function with transaction started in some cases and that would deadlock. But we can at least do the non-blocking reliable check in __block_page_mkwrite() which is the hardest part anyway. We have to check for frozen filesystem with the page marked dirty and under page lock with which we then return from ->page_mkwrite(). Only that way we cannot race with writeback done by freezing code - either we mark the page dirty after the writeback has started, see freezing in progress and block, or writeback will wait for our page lock which is released only when the fault is done and then writeback will writeout and writeprotect the page again. Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/buffer.c')
-rw-r--r--fs/buffer.c24
1 files changed, 23 insertions, 1 deletions
diff --git a/fs/buffer.c b/fs/buffer.c
index f6ad8f9b8fa5..b0675bfe8207 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -2331,6 +2331,9 @@ EXPORT_SYMBOL(block_commit_write);
2331 * page lock we can determine safely if the page is beyond EOF. If it is not 2331 * page lock we can determine safely if the page is beyond EOF. If it is not
2332 * beyond EOF, then the page is guaranteed safe against truncation until we 2332 * beyond EOF, then the page is guaranteed safe against truncation until we
2333 * unlock the page. 2333 * unlock the page.
2334 *
2335 * Direct callers of this function should call vfs_check_frozen() so that page
2336 * fault does not busyloop until the fs is thawed.
2334 */ 2337 */
2335int __block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, 2338int __block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
2336 get_block_t get_block) 2339 get_block_t get_block)
@@ -2362,6 +2365,18 @@ int __block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
2362 2365
2363 if (unlikely(ret < 0)) 2366 if (unlikely(ret < 0))
2364 goto out_unlock; 2367 goto out_unlock;
2368 /*
2369 * Freezing in progress? We check after the page is marked dirty and
2370 * with page lock held so if the test here fails, we are sure freezing
2371 * code will wait during syncing until the page fault is done - at that
2372 * point page will be dirty and unlocked so freezing code will write it
2373 * and writeprotect it again.
2374 */
2375 set_page_dirty(page);
2376 if (inode->i_sb->s_frozen != SB_UNFROZEN) {
2377 ret = -EAGAIN;
2378 goto out_unlock;
2379 }
2365 return 0; 2380 return 0;
2366out_unlock: 2381out_unlock:
2367 unlock_page(page); 2382 unlock_page(page);
@@ -2372,8 +2387,15 @@ EXPORT_SYMBOL(__block_page_mkwrite);
2372int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, 2387int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
2373 get_block_t get_block) 2388 get_block_t get_block)
2374{ 2389{
2375 int ret = __block_page_mkwrite(vma, vmf, get_block); 2390 int ret;
2391 struct super_block *sb = vma->vm_file->f_path.dentry->d_inode->i_sb;
2376 2392
2393 /*
2394 * This check is racy but catches the common case. The check in
2395 * __block_page_mkwrite() is reliable.
2396 */
2397 vfs_check_frozen(sb, SB_FREEZE_WRITE);
2398 ret = __block_page_mkwrite(vma, vmf, get_block);
2377 return block_page_mkwrite_return(ret); 2399 return block_page_mkwrite_return(ret);
2378} 2400}
2379EXPORT_SYMBOL(block_page_mkwrite); 2401EXPORT_SYMBOL(block_page_mkwrite);