]> git.hungrycats.org Git - linux/commitdiff
[PATCH] writepage fs corruption fix
authorAndrea Arcangeli <andrea@suse.de>
Sun, 11 Jul 2004 02:37:57 +0000 (19:37 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Sun, 11 Jul 2004 02:37:57 +0000 (19:37 -0700)
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 <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/mpage.c

index a22fc8f0ba9b05c85550413549719fc8daa4669e..ca517fc569410a964eef473ce7ea0df6f392647f 100644 (file)
@@ -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);