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 e3fe9918faa..eed48063990 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) |
