diff options
-rw-r--r-- | fs/fs-writeback.c | 26 |
1 files changed, 22 insertions, 4 deletions
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index f81f9e71871e..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? */ |