diff options
Diffstat (limited to 'fs/fs-writeback.c')
-rw-r--r-- | fs/fs-writeback.c | 29 |
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 | ||
223 | static 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) |