X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fmpage.c;h=71c7ca3a455de3687f1147152cf0f76b87f3a1f9;hb=9bf4aaab3e101692164d49b7ca357651eb691cb6;hp=a22fc8f0ba9b05c85550413549719fc8daa4669e;hpb=db216c3d5e4c040e557a50f8f5d35d5c415e8c1c;p=linux-2.6.git diff --git a/fs/mpage.c b/fs/mpage.c index a22fc8f0b..71c7ca3a4 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -404,6 +404,7 @@ mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block, struct block_device *boundary_bdev = NULL; int length; struct buffer_head map_bh; + loff_t i_size = i_size_read(inode); if (page_has_buffers(page)) { struct buffer_head *head = page_buffers(page); @@ -460,7 +461,7 @@ mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block, */ BUG_ON(!PageUptodate(page)); block_in_file = page->index << (PAGE_CACHE_SHIFT - blkbits); - last_block = (i_size_read(inode) - 1) >> blkbits; + last_block = (i_size - 1) >> blkbits; map_bh.b_page = page; for (page_block = 0; page_block < blocks_per_page; ) { @@ -489,9 +490,18 @@ mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block, first_unmapped = page_block; - end_index = i_size_read(inode) >> PAGE_CACHE_SHIFT; +page_is_mapped: + end_index = i_size >> PAGE_CACHE_SHIFT; if (page->index >= end_index) { - unsigned offset = i_size_read(inode) & (PAGE_CACHE_SIZE - 1); + /* + * The page straddles i_size. It must be zeroed out on each + * and every writepage invokation because it may be mmapped. + * "A file is mapped in multiples of the page size. For a file + * that is not a multiple of the page size, the remaining memory + * is zeroed when mapped, and writes to that region are not + * written out to the file." + */ + unsigned offset = i_size & (PAGE_CACHE_SIZE - 1); char *kaddr; if (page->index > end_index || !offset) @@ -502,8 +512,6 @@ mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block, kunmap_atomic(kaddr, KM_USER0); } -page_is_mapped: - /* * This page will go to BIO. Do we need to send this BIO off first? */ @@ -518,6 +526,17 @@ alloc_new: goto confused; } + /* + * Must try to add the page before marking the buffer clean or + * the confused fail path above (OOM) will be very confused when + * it finds all bh marked clean (i.e. it will not write anything) + */ + length = first_unmapped << blkbits; + if (bio_add_page(bio, page, length, 0) < length) { + bio = mpage_bio_submit(WRITE, bio); + goto alloc_new; + } + /* * OK, we have our BIO, so we can now mark the buffers clean. Make * sure to only clean buffers which we know we'll be writing. @@ -534,16 +553,15 @@ alloc_new: bh = bh->b_this_page; } while (bh != head); - if (buffer_heads_over_limit) + /* + * we cannot drop the bh if the page is not uptodate + * or a concurrent readpage would fail to serialize with the bh + * and it would read from disk before we reach the platter. + */ + if (buffer_heads_over_limit && PageUptodate(page)) try_to_free_buffers(page); } - length = first_unmapped << blkbits; - if (bio_add_page(bio, page, length, 0) < length) { - bio = mpage_bio_submit(WRITE, bio); - goto alloc_new; - } - BUG_ON(PageWriteback(page)); set_page_writeback(page); unlock_page(page);