aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fs-writeback.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fs-writeback.c')
-rw-r--r--fs/fs-writeback.c29
1 files changed, 24 insertions, 5 deletions
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index e3fe9918faaf..eed480639902 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -196,7 +196,7 @@ static void redirty_tail(struct inode *inode)
196 struct inode *tail_inode; 196 struct inode *tail_inode;
197 197
198 tail_inode = list_entry(sb->s_dirty.next, struct inode, i_list); 198 tail_inode = list_entry(sb->s_dirty.next, struct inode, i_list);
199 if (!time_after_eq(inode->dirtied_when, 199 if (time_before(inode->dirtied_when,
200 tail_inode->dirtied_when)) 200 tail_inode->dirtied_when))
201 inode->dirtied_when = jiffies; 201 inode->dirtied_when = jiffies;
202 } 202 }
@@ -220,6 +220,21 @@ static void inode_sync_complete(struct inode *inode)
220 wake_up_bit(&inode->i_state, __I_SYNC); 220 wake_up_bit(&inode->i_state, __I_SYNC);
221} 221}
222 222
223static bool inode_dirtied_after(struct inode *inode, unsigned long t)
224{
225 bool ret = time_after(inode->dirtied_when, t);
226#ifndef CONFIG_64BIT
227 /*
228 * For inodes being constantly redirtied, dirtied_when can get stuck.
229 * It _appears_ to be in the future, but is actually in distant past.
230 * This test is necessary to prevent such wrapped-around relative times
231 * from permanently stopping the whole pdflush writeback.
232 */
233 ret = ret && time_before_eq(inode->dirtied_when, jiffies);
234#endif
235 return ret;
236}
237
223/* 238/*
224 * Move expired dirty inodes from @delaying_queue to @dispatch_queue. 239 * Move expired dirty inodes from @delaying_queue to @dispatch_queue.
225 */ 240 */
@@ -231,7 +246,7 @@ static void move_expired_inodes(struct list_head *delaying_queue,
231 struct inode *inode = list_entry(delaying_queue->prev, 246 struct inode *inode = list_entry(delaying_queue->prev,
232 struct inode, i_list); 247 struct inode, i_list);
233 if (older_than_this && 248 if (older_than_this &&
234 time_after(inode->dirtied_when, *older_than_this)) 249 inode_dirtied_after(inode, *older_than_this))
235 break; 250 break;
236 list_move(&inode->i_list, dispatch_queue); 251 list_move(&inode->i_list, dispatch_queue);
237 } 252 }
@@ -492,8 +507,11 @@ void generic_sync_sb_inodes(struct super_block *sb,
492 continue; /* blockdev has wrong queue */ 507 continue; /* blockdev has wrong queue */
493 } 508 }
494 509
495 /* Was this inode dirtied after sync_sb_inodes was called? */ 510 /*
496 if (time_after(inode->dirtied_when, start)) 511 * Was this inode dirtied after sync_sb_inodes was called?
512 * This keeps sync from extra jobs and livelock.
513 */
514 if (inode_dirtied_after(inode, start))
497 break; 515 break;
498 516
499 /* Is another pdflush already flushing this queue? */ 517 /* Is another pdflush already flushing this queue? */
@@ -538,7 +556,8 @@ void generic_sync_sb_inodes(struct super_block *sb,
538 list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { 556 list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
539 struct address_space *mapping; 557 struct address_space *mapping;
540 558
541 if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) 559 if (inode->i_state &
560 (I_FREEING|I_CLEAR|I_WILL_FREE|I_NEW))
542 continue; 561 continue;
543 mapping = inode->i_mapping; 562 mapping = inode->i_mapping;
544 if (mapping->nrpages == 0) 563 if (mapping->nrpages == 0)