fs: Protect write paths by sb_start_write - sb_end_write

There are several entry points which dirty pages in a filesystem.  mmap
(handled by block_page_mkwrite()), buffered write (handled by
__generic_file_aio_write()), splice write (generic_file_splice_write),
truncate, and fallocate (these can dirty last partial page - handled inside
each filesystem separately). Protect these places with sb_start_write() and
sb_end_write().

->page_mkwrite() calls are particularly complex since they are called with
mmap_sem held and thus we cannot use standard sb_start_write() due to lock
ordering constraints. We solve the problem by using a special freeze protection
sb_start_pagefault() which ranks below mmap_sem.

BugLink: https://bugs.launchpad.net/bugs/897421
Tested-by: Kamal Mostafa <kamal@canonical.com>
Tested-by: Peter M. Petrakis <peter.petrakis@canonical.com>
Tested-by: Dann Frazier <dann.frazier@canonical.com>
Tested-by: Massimo Morana <massimo.morana@canonical.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Jan Kara
2012-06-12 16:20:37 +02:00
committed by Al Viro
parent 5d37e9e6de
commit 14da920014
5 changed files with 26 additions and 23 deletions

View File

@@ -2306,8 +2306,8 @@ EXPORT_SYMBOL(block_commit_write);
* beyond EOF, then the page is guaranteed safe against truncation until we
* unlock the page.
*
* Direct callers of this function should call vfs_check_frozen() so that page
* fault does not busyloop until the fs is thawed.
* Direct callers of this function should protect against filesystem freezing
* using sb_start_write() - sb_end_write() functions.
*/
int __block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
get_block_t get_block)
@@ -2345,18 +2345,7 @@ int __block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
if (unlikely(ret < 0))
goto out_unlock;
/*
* Freezing in progress? We check after the page is marked dirty and
* with page lock held so if the test here fails, we are sure freezing
* code will wait during syncing until the page fault is done - at that
* point page will be dirty and unlocked so freezing code will write it
* and writeprotect it again.
*/
set_page_dirty(page);
if (inode->i_sb->s_frozen != SB_UNFROZEN) {
ret = -EAGAIN;
goto out_unlock;
}
wait_on_page_writeback(page);
return 0;
out_unlock:
@@ -2371,12 +2360,9 @@ int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
int ret;
struct super_block *sb = vma->vm_file->f_path.dentry->d_inode->i_sb;
/*
* This check is racy but catches the common case. The check in
* __block_page_mkwrite() is reliable.
*/
vfs_check_frozen(sb, SB_FREEZE_WRITE);
sb_start_pagefault(sb);
ret = __block_page_mkwrite(vma, vmf, get_block);
sb_end_pagefault(sb);
return block_page_mkwrite_return(ret);
}
EXPORT_SYMBOL(block_page_mkwrite);