>From ee1f2f8cdea23cf19b34e51b4f78e040ce898976 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 3 May 2011 17:00:35 +0200 Subject: [PATCH 3/3] ext4: Block mmapped writes while the fs is frozen We should not allow file modification via mmap while the filesystem is frozen. So block in ext4_page_mkwrite() while the filesystem is frozen. We have to check for frozen filesystem under page lock with which we then return from ext4_page_mkwrite(). Only that way we cannot race with writeback done by freezing code - either we lock the page 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. Signed-off-by: Jan Kara --- fs/ext4/inode.c | 41 ++++++++++++++++++++++++----------------- 1 files changed, 24 insertions(+), 17 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 377fed0..6faadaf 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5788,13 +5788,6 @@ static int ext4_bh_unmapped(handle_t *handle, struct buffer_head *bh) return !buffer_mapped(bh); } -static int ext4_journalled_fault_fn(handle_t *handle, struct buffer_head *bh) -{ - if (!buffer_dirty(bh)) - return 0; - return ext4_handle_dirty_metadata(handle, NULL, bh); -} - int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { struct page *page = vmf->page; @@ -5804,10 +5797,16 @@ int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) struct file *file = vma->vm_file; struct inode *inode = file->f_path.dentry->d_inode; struct address_space *mapping = inode->i_mapping; - handle_t handle; - get_block_t get_block; + handle_t *handle; + get_block_t *get_block; int retries = 0; +restart: + /* + * This check is racy but catches the common case. The check at the + * end of this function is reliable. + */ + vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE); /* Delalloc case is easy... */ if (test_opt(inode->i_sb, DELALLOC) && !ext4_should_journal_data(inode) && @@ -5834,10 +5833,8 @@ int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) else len = PAGE_CACHE_SIZE; /* - * Return if we have all the buffers mapped. This avoid - * the need to call write_begin/write_end which does a - * journal_start/journal_stop which can block and take - * long time + * Return if we have all the buffers mapped. This avoids the need to do + * journal_start/journal_stop which can block and take a long time */ if (page_has_buffers(page)) { if (!walk_page_buffers(NULL, page_buffers(page), 0, len, NULL, @@ -5852,7 +5849,7 @@ int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) get_block = ext4_get_block_write; else get_block = ext4_get_block; -retry: +retry_alloc: handle = ext4_journal_start(inode, ext4_writepage_trans_blocks(inode)); if (IS_ERR(handle)) { ret = VM_FAULT_SIGBUS; @@ -5861,16 +5858,16 @@ retry: ret = __block_page_mkwrite(vma, vmf, get_block); if (ret == VM_FAULT_LOCKED && ext4_should_journal_data(inode)) { if (walk_page_buffers(handle, page_buffers(page), 0, - PAGE_CACHE_SIZE, NULL, ext4_journalled_fault_fn)) { + PAGE_CACHE_SIZE, NULL, do_journal_get_write_access)) { unlock_page(page); ret = VM_FAULT_SIGBUS; goto out; } ext4_set_inode_state(inode, EXT4_STATE_JDATA); } - ext4_journal_end(handle); + ext4_journal_stop(handle); if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) - goto retry; + goto retry_alloc; out_ret: if (ret < 0) { if (ret == -ENOMEM) @@ -5879,5 +5876,15 @@ out_ret: ret = VM_FAULT_SIGBUS; } out: + /* + * Freezing in progress? We check with page lock held so if the test + * here fails, we are sure freezing code will wait until the page + * fault is done - at that point page will be dirty and unlocked so + * freezing code will writeprotect it again. + */ + if (ret == VM_FAULT_LOCKED && inode->i_sb->s_frozen != SB_UNFROZEN) { + unlock_page(page); + goto restart; + } return ret; } -- 1.7.1