From 8d4261a050d08359ae0d656dff3e23785da817ea Mon Sep 17 00:00:00 2001 From: Andrea Arcangeli Date: Sat, 10 Jul 2004 19:37:57 -0700 Subject: [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: Andrew Morton Signed-off-by: Linus Torvalds --- fs/mpage.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/fs/mpage.c b/fs/mpage.c index a22fc8f0ba9b..ca517fc56941 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -518,6 +518,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. @@ -538,12 +549,6 @@ alloc_new: 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); -- cgit v1.2.3