aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Morton <akpm@linux-foundation.org>2007-10-17 02:30:32 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-17 11:43:01 -0400
commit6610a0bc8dcc120daa1d93807d470d5cbf777c39 (patch)
treee98afd34fa859877286c9f67d2b99fc7022c94af
parent2b0172e1c6e6aea0a4efcfaaab38d4a02046f45a (diff)
writeback: fix time-ordering of the per-superblock dirty-inode lists
When writeback has finished writing back an inode it looks to see if that inode is still dirty. If it is, that means that a process redirtied the inode while its writeback was in progress. What we need to do here is to refile the redirtied inode onto the s_dirty list. But we're doing that wrongly: it could be that this inode was redirtied _before_ the last inode on s_dirty. We're blindly appending this inode to the list, after an inode which might be less-recently-dirtied, thus violating the list's ordering. So we must either insertion-sort this inode into the correct place, or we must update this inode's dirtied_when field when appending it to the reverse-sorted s_dirty list, to preserve the reverse-time-ordering. This patch does the latter: if this inode was dirtied less recently than the tail inode then copy the tail inode's timestamp into this inode. This means that in rare circumstances, some inodes will be writen back later than they should have been. But the time slip will be small. 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.c26
1 files changed, 25 insertions, 1 deletions
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 8d23b0b38717..08b9f83b645e 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -141,6 +141,30 @@ static int write_inode(struct inode *inode, int sync)
141} 141}
142 142
143/* 143/*
144 * Redirty an inode: set its when-it-was dirtied timestamp and move it to the
145 * furthest end of its superblock's dirty-inode list.
146 *
147 * Before stamping the inode's ->dirtied_when, we check to see whether it is
148 * already the most-recently-dirtied inode on the s_dirty list. If that is
149 * the case then the inode must have been redirtied while it was being written
150 * out and we don't reset its dirtied_when.
151 */
152static void redirty_tail(struct inode *inode)
153{
154 struct super_block *sb = inode->i_sb;
155
156 if (!list_empty(&sb->s_dirty)) {
157 struct inode *tail_inode;
158
159 tail_inode = list_entry(sb->s_dirty.next, struct inode, i_list);
160 if (!time_after_eq(inode->dirtied_when,
161 tail_inode->dirtied_when))
162 inode->dirtied_when = jiffies;
163 }
164 list_move(&inode->i_list, &sb->s_dirty);
165}
166
167/*
144 * Write a single inode's dirty pages and inode data out to disk. 168 * Write a single inode's dirty pages and inode data out to disk.
145 * If `wait' is set, wait on the writeout. 169 * If `wait' is set, wait on the writeout.
146 * 170 *
@@ -219,7 +243,7 @@ __sync_single_inode(struct inode *inode, struct writeback_control *wbc)
219 * Someone redirtied the inode while were writing back 243 * Someone redirtied the inode while were writing back
220 * the pages. 244 * the pages.
221 */ 245 */
222 list_move(&inode->i_list, &sb->s_dirty); 246 redirty_tail(inode);
223 } else if (atomic_read(&inode->i_count)) { 247 } else if (atomic_read(&inode->i_count)) {
224 /* 248 /*
225 * The inode is clean, inuse 249 * The inode is clean, inuse