]> git.hungrycats.org Git - linux/commitdiff
[PATCH] fix umount dataloss problem
authorAndrew Morton <akpm@osdl.org>
Mon, 1 Mar 2004 15:00:02 +0000 (07:00 -0800)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Mon, 1 Mar 2004 15:00:02 +0000 (07:00 -0800)
address_spaces have a `dirtied_when' jiffies field which records the time at
which the mapping was first dirtied.  This is used for the periodic writeback
(kupdate) function.  It is also used to prevent livelocks in the writeback
code: don't try to write back objects which were dirtied after
sync_sb_inodes() was called.

It used to be the case that dirtied_when == 0 had magical properties, so
there is code in there which avoids accidentally setting dirtied_when to zero
at jiffy wrap time.  We just set it to jiffies|1.

Unfortunately, jiffies|1 is in the future.  So under some rare timing
circumstances (inode dirtied within one jiffy of umount) the livelock
avoidance code in sync_sb_inodes() can accidentally trigger and we fail to
write an inode out, resulting in filesytem corruption on ext2-style
filesystems.  Normally, nobody dirties a file within a millisecond of umount,
so it was not noticed.

It is no longer the case that (address_space.dirtied_when == 0) has special
meaning, so we can just remove all that code and fix the bug.

fs/fs-writeback.c

index 938d1ad5428c0b41be9cdfd99681c0b6ac043733..8bc4e9d00301aa2265ebb0c8afe33dc053ce8e5a 100644 (file)
@@ -99,7 +99,7 @@ void __mark_inode_dirty(struct inode *inode, int flags)
                 * reposition it (that would break s_dirty time-ordering).
                 */
                if (!was_dirty) {
-                       mapping->dirtied_when = jiffies|1; /* 0 is special */
+                       mapping->dirtied_when = jiffies;
                        list_move(&inode->i_list, &sb->s_dirty);
                }
        }
@@ -176,17 +176,15 @@ __sync_single_inode(struct inode *inode, struct writeback_control *wbc)
                } else if (!list_empty(&mapping->dirty_pages)) {
                        /* Redirtied */
                        inode->i_state |= I_DIRTY_PAGES;
-                       mapping->dirtied_when = jiffies|1;
+                       mapping->dirtied_when = jiffies;
                        list_move(&inode->i_list, &sb->s_dirty);
                } else if (inode->i_state & I_DIRTY) {
                        /* Redirtied */
-                       mapping->dirtied_when = jiffies|1;
+                       mapping->dirtied_when = jiffies;
                        list_move(&inode->i_list, &sb->s_dirty);
                } else if (atomic_read(&inode->i_count)) {
-                       mapping->dirtied_when = 0;
                        list_move(&inode->i_list, &inode_in_use);
                } else {
-                       mapping->dirtied_when = 0;
                        list_move(&inode->i_list, &inode_unused);
                }
        }
@@ -310,7 +308,7 @@ sync_sb_inodes(struct super_block *sb, struct writeback_control *wbc)
                __iget(inode);
                __writeback_single_inode(inode, wbc);
                if (wbc->sync_mode == WB_SYNC_HOLD) {
-                       mapping->dirtied_when = jiffies|1;
+                       mapping->dirtied_when = jiffies;
                        list_move(&inode->i_list, &sb->s_dirty);
                }
                if (current_is_pdflush())