Commit 8d4261a0 authored by Andrea Arcangeli's avatar Andrea Arcangeli Committed by Linus Torvalds

[PATCH] writepage fs corruption fix

Fix a data loss bug in mpage_writepages(), triggerable under extreme memory
pressure on ext2, JFS, hfs and hfsplus:

The bug is the marking of the bh clean despite we could still run into the
"confused" path.  After that the confused path really becomes confused and it
writes nothing and fs corruption triggers silenty (the reugular writepage only
writes bh that are marked dirty, it never attempts to submit_bh anything
marked clean).  The mpage-writepage code must never mark the bh clean as far
as it wants to still fallback in the regular writepage which depends on the bh
to be dirty (i.e.  the "goto confused" path).  This could only triggers with
memory pressure (it also needs buffer_heads_over_limit == 0, and that is
frequent under mm pressure).

Thanks a lot to Chris for his fine debugging that localized the problem in the
writepage code.
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 2713346c
...@@ -518,6 +518,17 @@ mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block, ...@@ -518,6 +518,17 @@ mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block,
goto confused; 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 * 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. * sure to only clean buffers which we know we'll be writing.
...@@ -538,12 +549,6 @@ mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block, ...@@ -538,12 +549,6 @@ mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block,
try_to_free_buffers(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)); BUG_ON(PageWriteback(page));
set_page_writeback(page); set_page_writeback(page);
unlock_page(page); unlock_page(page);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment