Commit eb657e38 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] fix umount dataloss problem

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