Commit 5f44f4a9 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Fix data loss problem due to sys_sync

In 2.5.52 I broke sys_sync() for ext2 in subtle ways.

sys_sync() will set mapping->dirtied_when non-zero against a clean inode.
Later, in (say) __iget(), that inode gets moved over to inode_unused or
inode_in_use.  But because it has non-zero ->dirtied_when,
__mark_inode_dirty() thinks that the inode must still be on sb->s_dirty.

But it isn't.  It's on inode_in_use.  It (and its pages) never get written
out and the data gets thrown away on unmount.

The patch ceases to use ->dirtied_when as an indicator of inode dirtiness.
Not sure why I even did that :(
parent 6a3354a9
...@@ -67,6 +67,7 @@ void __mark_inode_dirty(struct inode *inode, int flags) ...@@ -67,6 +67,7 @@ void __mark_inode_dirty(struct inode *inode, int flags)
spin_lock(&inode_lock); spin_lock(&inode_lock);
if ((inode->i_state & flags) != flags) { if ((inode->i_state & flags) != flags) {
const int was_dirty = inode->i_state & I_DIRTY;
struct address_space *mapping = inode->i_mapping; struct address_space *mapping = inode->i_mapping;
inode->i_state |= flags; inode->i_state |= flags;
...@@ -90,7 +91,7 @@ void __mark_inode_dirty(struct inode *inode, int flags) ...@@ -90,7 +91,7 @@ void __mark_inode_dirty(struct inode *inode, int flags)
* If the inode was already on s_dirty or s_io, don't * If the inode was already on s_dirty or s_io, don't
* reposition it (that would break s_dirty time-ordering). * reposition it (that would break s_dirty time-ordering).
*/ */
if (!mapping->dirtied_when) { if (!was_dirty) {
mapping->dirtied_when = jiffies|1; /* 0 is special */ mapping->dirtied_when = jiffies|1; /* 0 is special */
list_move(&inode->i_list, &sb->s_dirty); list_move(&inode->i_list, &sb->s_dirty);
} }
...@@ -280,7 +281,7 @@ sync_sb_inodes(struct super_block *sb, struct writeback_control *wbc) ...@@ -280,7 +281,7 @@ sync_sb_inodes(struct super_block *sb, struct writeback_control *wbc)
__iget(inode); __iget(inode);
__writeback_single_inode(inode, really_sync, wbc); __writeback_single_inode(inode, really_sync, wbc);
if (wbc->sync_mode == WB_SYNC_HOLD) { if (wbc->sync_mode == WB_SYNC_HOLD) {
mapping->dirtied_when = jiffies; mapping->dirtied_when = jiffies|1;
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