aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Morton <akpm@linux-foundation.org>2007-10-17 02:30:34 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-17 11:43:02 -0400
commitc986d1e2a460cbce79d631c51519ae82c778c6c5 (patch)
treed7303b22dfea805dba8db7d147294d1aa53ba7ce
parentf57b9b7b4f68e1723ca99381dc10c8bc07d6df14 (diff)
writeback: fix time ordering of the per superblock dirty inode lists 4
When the kupdate function has tried to write back an expired inode it will then check to see whether some of the inode's pages are still dirty. This can happen when the filesystem decided to not write a page for some reason. But it does _not_ occur due to redirtyings: a redirtying will set I_DIRTY_PAGES. What we need to do here is to set I_DIRTY_PAGES to reflect reality and to then put the inode onto the _head_ of s_dirty for consideration on the next kupdate pass, in five seconds time. Problem is, the code failed to modify the inode's timestamp when pushing the inode onto thehead of s_dirty. The patch: If there are no other inodes on s_dirty then we leave the inode's timestamp alone: it is already expired. If there _are_ other inodes on s_dirty then we arrange for this inode to get the same timestamp as the inode which is at the head of s_dirty, thus preserving the s_dirty ordering. But we only need to do this if this inode purports to have been dirtied before the one at head-of-list. Cc: Mike Waychison <mikew@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/fs-writeback.c24
1 files changed, 23 insertions, 1 deletions
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 66889b06d00f..eb8dc1f22775 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -165,6 +165,28 @@ static void redirty_tail(struct inode *inode)
165} 165}
166 166
167/* 167/*
168 * Redirty an inode, but mark it as the very next-to-be-written inode on its
169 * superblock's dirty-inode list.
170 * We need to preserve s_dirty's reverse-time-orderedness, so we cheat by
171 * setting this inode's dirtied_when to the same value as that of the inode
172 * which is presently head-of-list, if present head-of-list is newer than this
173 * inode. (head-of-list is the least-recently-dirtied inode: the oldest one).
174 */
175static void redirty_head(struct inode *inode)
176{
177 struct super_block *sb = inode->i_sb;
178
179 if (!list_empty(&sb->s_dirty)) {
180 struct inode *head_inode;
181
182 head_inode = list_entry(sb->s_dirty.prev, struct inode, i_list);
183 if (time_after(inode->dirtied_when, head_inode->dirtied_when))
184 inode->dirtied_when = head_inode->dirtied_when;
185 }
186 list_move_tail(&inode->i_list, &sb->s_dirty);
187}
188
189/*
168 * Write a single inode's dirty pages and inode data out to disk. 190 * Write a single inode's dirty pages and inode data out to disk.
169 * If `wait' is set, wait on the writeout. 191 * If `wait' is set, wait on the writeout.
170 * 192 *
@@ -225,7 +247,7 @@ __sync_single_inode(struct inode *inode, struct writeback_control *wbc)
225 * uncongested. 247 * uncongested.
226 */ 248 */
227 inode->i_state |= I_DIRTY_PAGES; 249 inode->i_state |= I_DIRTY_PAGES;
228 list_move_tail(&inode->i_list, &sb->s_dirty); 250 redirty_head(inode);
229 } else { 251 } else {
230 /* 252 /*
231 * Otherwise fully redirty the inode so that 253 * Otherwise fully redirty the inode so that