aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>2009-06-14 17:57:10 -0400
committerTheodore Ts'o <tytso@mit.edu>2009-06-14 17:57:10 -0400
commitc364b22c9580a885e0f8c0d0f9710d67dc448958 (patch)
tree6a714e767afb7d08a648f8e23e4acca414896699
parentb767e78a179e5ab30fdbff1686d074ac270471eb (diff)
ext4: Fix mmap/truncate race when blocksize < pagesize && delayed allocation
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>
-rw-r--r--fs/ext4/inode.c23
1 files changed, 8 insertions, 15 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index b87b68cd3241..1275f34589c7 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -2305,15 +2305,9 @@ flush_it:
2305 return; 2305 return;
2306} 2306}
2307 2307
2308static int ext4_bh_unmapped_or_delay(handle_t *handle, struct buffer_head *bh) 2308static int ext4_bh_delay_or_unwritten(handle_t *handle, struct buffer_head *bh)
2309{ 2309{
2310 /* 2310 return (buffer_delay(bh) || buffer_unwritten(bh)) && buffer_dirty(bh);
2311 * unmapped buffer is possible for holes.
2312 * delay buffer is possible with delayed allocation.
2313 * We also need to consider unwritten buffer as unmapped.
2314 */
2315 return (!buffer_mapped(bh) || buffer_delay(bh) ||
2316 buffer_unwritten(bh)) && buffer_dirty(bh);
2317} 2311}
2318 2312
2319/* 2313/*
@@ -2400,7 +2394,7 @@ static int __mpage_da_writepage(struct page *page,
2400 * Otherwise we won't make progress 2394 * Otherwise we won't make progress
2401 * with the page in ext4_da_writepage 2395 * with the page in ext4_da_writepage
2402 */ 2396 */
2403 if (ext4_bh_unmapped_or_delay(NULL, bh)) { 2397 if (ext4_bh_delay_or_unwritten(NULL, bh)) {
2404 mpage_add_bh_to_extent(mpd, logical, 2398 mpage_add_bh_to_extent(mpd, logical,
2405 bh->b_size, 2399 bh->b_size,
2406 bh->b_state); 2400 bh->b_state);
@@ -2517,7 +2511,6 @@ static int noalloc_get_block_write(struct inode *inode, sector_t iblock,
2517 * so call get_block_wrap with create = 0 2511 * so call get_block_wrap with create = 0
2518 */ 2512 */
2519 ret = ext4_get_blocks(NULL, inode, iblock, max_blocks, bh_result, 0); 2513 ret = ext4_get_blocks(NULL, inode, iblock, max_blocks, bh_result, 0);
2520 BUG_ON(create && ret == 0);
2521 if (ret > 0) { 2514 if (ret > 0) {
2522 bh_result->b_size = (ret << inode->i_blkbits); 2515 bh_result->b_size = (ret << inode->i_blkbits);
2523 ret = 0; 2516 ret = 0;
@@ -2533,7 +2526,7 @@ static int noalloc_get_block_write(struct inode *inode, sector_t iblock,
2533 * - grab_page_cache when doing write_begin (have journal handle) 2526 * - grab_page_cache when doing write_begin (have journal handle)
2534 */ 2527 */
2535static int ext4_da_writepage(struct page *page, 2528static int ext4_da_writepage(struct page *page,
2536 struct writeback_control *wbc) 2529 struct writeback_control *wbc)
2537{ 2530{
2538 int ret = 0; 2531 int ret = 0;
2539 loff_t size; 2532 loff_t size;
@@ -2551,7 +2544,7 @@ static int ext4_da_writepage(struct page *page,
2551 if (page_has_buffers(page)) { 2544 if (page_has_buffers(page)) {
2552 page_bufs = page_buffers(page); 2545 page_bufs = page_buffers(page);
2553 if (walk_page_buffers(NULL, page_bufs, 0, len, NULL, 2546 if (walk_page_buffers(NULL, page_bufs, 0, len, NULL,
2554 ext4_bh_unmapped_or_delay)) { 2547 ext4_bh_delay_or_unwritten)) {
2555 /* 2548 /*
2556 * We don't want to do block allocation 2549 * We don't want to do block allocation
2557 * So redirty the page and return 2550 * So redirty the page and return
@@ -2584,7 +2577,7 @@ static int ext4_da_writepage(struct page *page,
2584 page_bufs = page_buffers(page); 2577 page_bufs = page_buffers(page);
2585 /* check whether all are mapped and non delay */ 2578 /* check whether all are mapped and non delay */
2586 if (walk_page_buffers(NULL, page_bufs, 0, len, NULL, 2579 if (walk_page_buffers(NULL, page_bufs, 0, len, NULL,
2587 ext4_bh_unmapped_or_delay)) { 2580 ext4_bh_delay_or_unwritten)) {
2588 redirty_page_for_writepage(wbc, page); 2581 redirty_page_for_writepage(wbc, page);
2589 unlock_page(page); 2582 unlock_page(page);
2590 return 0; 2583 return 0;
@@ -3232,7 +3225,7 @@ static int ext4_normal_writepage(struct page *page,
3232 * happily proceed with mapping them and writing the page. 3225 * happily proceed with mapping them and writing the page.
3233 */ 3226 */
3234 BUG_ON(walk_page_buffers(NULL, page_buffers(page), 0, len, NULL, 3227 BUG_ON(walk_page_buffers(NULL, page_buffers(page), 0, len, NULL,
3235 ext4_bh_unmapped_or_delay)); 3228 ext4_bh_delay_or_unwritten));
3236 } 3229 }
3237 3230
3238 if (!ext4_journal_current_handle()) 3231 if (!ext4_journal_current_handle())
@@ -3322,7 +3315,7 @@ static int ext4_journalled_writepage(struct page *page,
3322 * happily proceed with mapping them and writing the page. 3315 * happily proceed with mapping them and writing the page.
3323 */ 3316 */
3324 BUG_ON(walk_page_buffers(NULL, page_buffers(page), 0, len, NULL, 3317 BUG_ON(walk_page_buffers(NULL, page_buffers(page), 0, len, NULL,
3325 ext4_bh_unmapped_or_delay)); 3318 ext4_bh_delay_or_unwritten));
3326 } 3319 }
3327 3320
3328 if (ext4_journal_current_handle()) 3321 if (ext4_journal_current_handle())