Skip to content

Commit

Permalink
ext4: Fix mmap/truncate race when blocksize < pagesize && delayed all…
Browse files Browse the repository at this point in the history
…ocation

It is possible to see buffer_heads which are not mapped in the
writepage callback in the following scneario (where the fs blocksize
is 1k and the page size is 4k):

1) truncate(f, 1024)
2) mmap(f, 0, 4096)
3) a[0] = 'a'
4) truncate(f, 4096)
5) writepage(...)

Now if we get a writepage callback immediately after (4) and before an
attempt to write at any other offset via mmap address (which implies we
are yet to get a pagefault and do a get_block) what we would have is the
page which is dirty have first block allocated and the other three
buffer_heads unmapped.

In the above case the writepage should go ahead and try to write the
first blocks and clear the page_dirty flag. Further attempts to write
to the page will again create a fault and result in allocating blocks
and marking page dirty.  If we don't write any other offset via mmap
address we would still have written the first block to the disk and
rest of the space will be considered as a hole.

So to address this, we change all of the places where we look for
delayed, unmapped, or unwritten buffer heads, and only check for
delayed or unwritten buffer heads instead.

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Acked-by: Jan Kara <jack@suse.cz>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
  • Loading branch information
Aneesh Kumar K.V authored and Theodore Ts'o committed Jun 14, 2009
1 parent b767e78 commit c364b22
Showing 1 changed file with 8 additions and 15 deletions.
23 changes: 8 additions & 15 deletions fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -2305,15 +2305,9 @@ static void mpage_add_bh_to_extent(struct mpage_da_data *mpd,
return;
}

static int ext4_bh_unmapped_or_delay(handle_t *handle, struct buffer_head *bh)
static int ext4_bh_delay_or_unwritten(handle_t *handle, struct buffer_head *bh)
{
/*
* unmapped buffer is possible for holes.
* delay buffer is possible with delayed allocation.
* We also need to consider unwritten buffer as unmapped.
*/
return (!buffer_mapped(bh) || buffer_delay(bh) ||
buffer_unwritten(bh)) && buffer_dirty(bh);
return (buffer_delay(bh) || buffer_unwritten(bh)) && buffer_dirty(bh);
}

/*
Expand Down Expand Up @@ -2400,7 +2394,7 @@ static int __mpage_da_writepage(struct page *page,
* Otherwise we won't make progress
* with the page in ext4_da_writepage
*/
if (ext4_bh_unmapped_or_delay(NULL, bh)) {
if (ext4_bh_delay_or_unwritten(NULL, bh)) {
mpage_add_bh_to_extent(mpd, logical,
bh->b_size,
bh->b_state);
Expand Down Expand Up @@ -2517,7 +2511,6 @@ static int noalloc_get_block_write(struct inode *inode, sector_t iblock,
* so call get_block_wrap with create = 0
*/
ret = ext4_get_blocks(NULL, inode, iblock, max_blocks, bh_result, 0);
BUG_ON(create && ret == 0);
if (ret > 0) {
bh_result->b_size = (ret << inode->i_blkbits);
ret = 0;
Expand All @@ -2533,7 +2526,7 @@ static int noalloc_get_block_write(struct inode *inode, sector_t iblock,
* - grab_page_cache when doing write_begin (have journal handle)
*/
static int ext4_da_writepage(struct page *page,
struct writeback_control *wbc)
struct writeback_control *wbc)
{
int ret = 0;
loff_t size;
Expand All @@ -2551,7 +2544,7 @@ static int ext4_da_writepage(struct page *page,
if (page_has_buffers(page)) {
page_bufs = page_buffers(page);
if (walk_page_buffers(NULL, page_bufs, 0, len, NULL,
ext4_bh_unmapped_or_delay)) {
ext4_bh_delay_or_unwritten)) {
/*
* We don't want to do block allocation
* So redirty the page and return
Expand Down Expand Up @@ -2584,7 +2577,7 @@ static int ext4_da_writepage(struct page *page,
page_bufs = page_buffers(page);
/* check whether all are mapped and non delay */
if (walk_page_buffers(NULL, page_bufs, 0, len, NULL,
ext4_bh_unmapped_or_delay)) {
ext4_bh_delay_or_unwritten)) {
redirty_page_for_writepage(wbc, page);
unlock_page(page);
return 0;
Expand Down Expand Up @@ -3232,7 +3225,7 @@ static int ext4_normal_writepage(struct page *page,
* happily proceed with mapping them and writing the page.
*/
BUG_ON(walk_page_buffers(NULL, page_buffers(page), 0, len, NULL,
ext4_bh_unmapped_or_delay));
ext4_bh_delay_or_unwritten));
}

if (!ext4_journal_current_handle())
Expand Down Expand Up @@ -3322,7 +3315,7 @@ static int ext4_journalled_writepage(struct page *page,
* happily proceed with mapping them and writing the page.
*/
BUG_ON(walk_page_buffers(NULL, page_buffers(page), 0, len, NULL,
ext4_bh_unmapped_or_delay));
ext4_bh_delay_or_unwritten));
}

if (ext4_journal_current_handle())
Expand Down

0 comments on commit c364b22

Please sign in to comment.