diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-17 19:12:34 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-17 19:12:34 -0500 |
commit | 038911597e17017cee55fe93d521164a27056866 (patch) | |
tree | 8f279a91de8237ce370a14d745940cccfd78ea07 /fs/fs-writeback.c | |
parent | 66dc830d14a222c9214a8557e9feb1e4a67a3857 (diff) | |
parent | a26f49926da938f47561f386be56a83dd37a496d (diff) |
Merge branch 'lazytime' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull lazytime mount option support from Al Viro:
"Lazytime stuff from tytso"
* 'lazytime' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
ext4: add optimization for the lazytime mount option
vfs: add find_inode_nowait() function
vfs: add support for a lazytime mount option
Diffstat (limited to 'fs/fs-writeback.c')
-rw-r--r-- | fs/fs-writeback.c | 62 |
1 files changed, 51 insertions, 11 deletions
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index c399152de397..073657f755d4 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c | |||
@@ -253,14 +253,19 @@ static bool inode_dirtied_after(struct inode *inode, unsigned long t) | |||
253 | return ret; | 253 | return ret; |
254 | } | 254 | } |
255 | 255 | ||
256 | #define EXPIRE_DIRTY_ATIME 0x0001 | ||
257 | |||
256 | /* | 258 | /* |
257 | * Move expired (dirtied before work->older_than_this) dirty inodes from | 259 | * Move expired (dirtied before work->older_than_this) dirty inodes from |
258 | * @delaying_queue to @dispatch_queue. | 260 | * @delaying_queue to @dispatch_queue. |
259 | */ | 261 | */ |
260 | static int move_expired_inodes(struct list_head *delaying_queue, | 262 | static int move_expired_inodes(struct list_head *delaying_queue, |
261 | struct list_head *dispatch_queue, | 263 | struct list_head *dispatch_queue, |
264 | int flags, | ||
262 | struct wb_writeback_work *work) | 265 | struct wb_writeback_work *work) |
263 | { | 266 | { |
267 | unsigned long *older_than_this = NULL; | ||
268 | unsigned long expire_time; | ||
264 | LIST_HEAD(tmp); | 269 | LIST_HEAD(tmp); |
265 | struct list_head *pos, *node; | 270 | struct list_head *pos, *node; |
266 | struct super_block *sb = NULL; | 271 | struct super_block *sb = NULL; |
@@ -268,13 +273,21 @@ static int move_expired_inodes(struct list_head *delaying_queue, | |||
268 | int do_sb_sort = 0; | 273 | int do_sb_sort = 0; |
269 | int moved = 0; | 274 | int moved = 0; |
270 | 275 | ||
276 | if ((flags & EXPIRE_DIRTY_ATIME) == 0) | ||
277 | older_than_this = work->older_than_this; | ||
278 | else if ((work->reason == WB_REASON_SYNC) == 0) { | ||
279 | expire_time = jiffies - (HZ * 86400); | ||
280 | older_than_this = &expire_time; | ||
281 | } | ||
271 | while (!list_empty(delaying_queue)) { | 282 | while (!list_empty(delaying_queue)) { |
272 | inode = wb_inode(delaying_queue->prev); | 283 | inode = wb_inode(delaying_queue->prev); |
273 | if (work->older_than_this && | 284 | if (older_than_this && |
274 | inode_dirtied_after(inode, *work->older_than_this)) | 285 | inode_dirtied_after(inode, *older_than_this)) |
275 | break; | 286 | break; |
276 | list_move(&inode->i_wb_list, &tmp); | 287 | list_move(&inode->i_wb_list, &tmp); |
277 | moved++; | 288 | moved++; |
289 | if (flags & EXPIRE_DIRTY_ATIME) | ||
290 | set_bit(__I_DIRTY_TIME_EXPIRED, &inode->i_state); | ||
278 | if (sb_is_blkdev_sb(inode->i_sb)) | 291 | if (sb_is_blkdev_sb(inode->i_sb)) |
279 | continue; | 292 | continue; |
280 | if (sb && sb != inode->i_sb) | 293 | if (sb && sb != inode->i_sb) |
@@ -315,9 +328,12 @@ out: | |||
315 | static void queue_io(struct bdi_writeback *wb, struct wb_writeback_work *work) | 328 | static void queue_io(struct bdi_writeback *wb, struct wb_writeback_work *work) |
316 | { | 329 | { |
317 | int moved; | 330 | int moved; |
331 | |||
318 | assert_spin_locked(&wb->list_lock); | 332 | assert_spin_locked(&wb->list_lock); |
319 | list_splice_init(&wb->b_more_io, &wb->b_io); | 333 | list_splice_init(&wb->b_more_io, &wb->b_io); |
320 | moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, work); | 334 | moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, 0, work); |
335 | moved += move_expired_inodes(&wb->b_dirty_time, &wb->b_io, | ||
336 | EXPIRE_DIRTY_ATIME, work); | ||
321 | trace_writeback_queue_io(wb, work, moved); | 337 | trace_writeback_queue_io(wb, work, moved); |
322 | } | 338 | } |
323 | 339 | ||
@@ -441,6 +457,8 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb, | |||
441 | * updates after data IO completion. | 457 | * updates after data IO completion. |
442 | */ | 458 | */ |
443 | redirty_tail(inode, wb); | 459 | redirty_tail(inode, wb); |
460 | } else if (inode->i_state & I_DIRTY_TIME) { | ||
461 | list_move(&inode->i_wb_list, &wb->b_dirty_time); | ||
444 | } else { | 462 | } else { |
445 | /* The inode is clean. Remove from writeback lists. */ | 463 | /* The inode is clean. Remove from writeback lists. */ |
446 | list_del_init(&inode->i_wb_list); | 464 | list_del_init(&inode->i_wb_list); |
@@ -487,7 +505,13 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) | |||
487 | spin_lock(&inode->i_lock); | 505 | spin_lock(&inode->i_lock); |
488 | 506 | ||
489 | dirty = inode->i_state & I_DIRTY; | 507 | dirty = inode->i_state & I_DIRTY; |
490 | inode->i_state &= ~I_DIRTY; | 508 | if (((dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) && |
509 | (inode->i_state & I_DIRTY_TIME)) || | ||
510 | (inode->i_state & I_DIRTY_TIME_EXPIRED)) { | ||
511 | dirty |= I_DIRTY_TIME | I_DIRTY_TIME_EXPIRED; | ||
512 | trace_writeback_lazytime(inode); | ||
513 | } | ||
514 | inode->i_state &= ~dirty; | ||
491 | 515 | ||
492 | /* | 516 | /* |
493 | * Paired with smp_mb() in __mark_inode_dirty(). This allows | 517 | * Paired with smp_mb() in __mark_inode_dirty(). This allows |
@@ -507,8 +531,10 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) | |||
507 | 531 | ||
508 | spin_unlock(&inode->i_lock); | 532 | spin_unlock(&inode->i_lock); |
509 | 533 | ||
534 | if (dirty & I_DIRTY_TIME) | ||
535 | mark_inode_dirty_sync(inode); | ||
510 | /* Don't write the inode if only I_DIRTY_PAGES was set */ | 536 | /* Don't write the inode if only I_DIRTY_PAGES was set */ |
511 | if (dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) { | 537 | if (dirty & ~I_DIRTY_PAGES) { |
512 | int err = write_inode(inode, wbc); | 538 | int err = write_inode(inode, wbc); |
513 | if (ret == 0) | 539 | if (ret == 0) |
514 | ret = err; | 540 | ret = err; |
@@ -556,7 +582,7 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb, | |||
556 | * make sure inode is on some writeback list and leave it there unless | 582 | * make sure inode is on some writeback list and leave it there unless |
557 | * we have completely cleaned the inode. | 583 | * we have completely cleaned the inode. |
558 | */ | 584 | */ |
559 | if (!(inode->i_state & I_DIRTY) && | 585 | if (!(inode->i_state & I_DIRTY_ALL) && |
560 | (wbc->sync_mode != WB_SYNC_ALL || | 586 | (wbc->sync_mode != WB_SYNC_ALL || |
561 | !mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK))) | 587 | !mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK))) |
562 | goto out; | 588 | goto out; |
@@ -571,7 +597,7 @@ writeback_single_inode(struct inode *inode, struct bdi_writeback *wb, | |||
571 | * If inode is clean, remove it from writeback lists. Otherwise don't | 597 | * If inode is clean, remove it from writeback lists. Otherwise don't |
572 | * touch it. See comment above for explanation. | 598 | * touch it. See comment above for explanation. |
573 | */ | 599 | */ |
574 | if (!(inode->i_state & I_DIRTY)) | 600 | if (!(inode->i_state & I_DIRTY_ALL)) |
575 | list_del_init(&inode->i_wb_list); | 601 | list_del_init(&inode->i_wb_list); |
576 | spin_unlock(&wb->list_lock); | 602 | spin_unlock(&wb->list_lock); |
577 | inode_sync_complete(inode); | 603 | inode_sync_complete(inode); |
@@ -713,7 +739,7 @@ static long writeback_sb_inodes(struct super_block *sb, | |||
713 | wrote += write_chunk - wbc.nr_to_write; | 739 | wrote += write_chunk - wbc.nr_to_write; |
714 | spin_lock(&wb->list_lock); | 740 | spin_lock(&wb->list_lock); |
715 | spin_lock(&inode->i_lock); | 741 | spin_lock(&inode->i_lock); |
716 | if (!(inode->i_state & I_DIRTY)) | 742 | if (!(inode->i_state & I_DIRTY_ALL)) |
717 | wrote++; | 743 | wrote++; |
718 | requeue_inode(inode, wb, &wbc); | 744 | requeue_inode(inode, wb, &wbc); |
719 | inode_sync_complete(inode); | 745 | inode_sync_complete(inode); |
@@ -1151,16 +1177,20 @@ static noinline void block_dump___mark_inode_dirty(struct inode *inode) | |||
1151 | * page->mapping->host, so the page-dirtying time is recorded in the internal | 1177 | * page->mapping->host, so the page-dirtying time is recorded in the internal |
1152 | * blockdev inode. | 1178 | * blockdev inode. |
1153 | */ | 1179 | */ |
1180 | #define I_DIRTY_INODE (I_DIRTY_SYNC | I_DIRTY_DATASYNC) | ||
1154 | void __mark_inode_dirty(struct inode *inode, int flags) | 1181 | void __mark_inode_dirty(struct inode *inode, int flags) |
1155 | { | 1182 | { |
1156 | struct super_block *sb = inode->i_sb; | 1183 | struct super_block *sb = inode->i_sb; |
1157 | struct backing_dev_info *bdi = NULL; | 1184 | struct backing_dev_info *bdi = NULL; |
1185 | int dirtytime; | ||
1186 | |||
1187 | trace_writeback_mark_inode_dirty(inode, flags); | ||
1158 | 1188 | ||
1159 | /* | 1189 | /* |
1160 | * Don't do this for I_DIRTY_PAGES - that doesn't actually | 1190 | * Don't do this for I_DIRTY_PAGES - that doesn't actually |
1161 | * dirty the inode itself | 1191 | * dirty the inode itself |
1162 | */ | 1192 | */ |
1163 | if (flags & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) { | 1193 | if (flags & (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_TIME)) { |
1164 | trace_writeback_dirty_inode_start(inode, flags); | 1194 | trace_writeback_dirty_inode_start(inode, flags); |
1165 | 1195 | ||
1166 | if (sb->s_op->dirty_inode) | 1196 | if (sb->s_op->dirty_inode) |
@@ -1168,6 +1198,9 @@ void __mark_inode_dirty(struct inode *inode, int flags) | |||
1168 | 1198 | ||
1169 | trace_writeback_dirty_inode(inode, flags); | 1199 | trace_writeback_dirty_inode(inode, flags); |
1170 | } | 1200 | } |
1201 | if (flags & I_DIRTY_INODE) | ||
1202 | flags &= ~I_DIRTY_TIME; | ||
1203 | dirtytime = flags & I_DIRTY_TIME; | ||
1171 | 1204 | ||
1172 | /* | 1205 | /* |
1173 | * Paired with smp_mb() in __writeback_single_inode() for the | 1206 | * Paired with smp_mb() in __writeback_single_inode() for the |
@@ -1175,16 +1208,21 @@ void __mark_inode_dirty(struct inode *inode, int flags) | |||
1175 | */ | 1208 | */ |
1176 | smp_mb(); | 1209 | smp_mb(); |
1177 | 1210 | ||
1178 | if ((inode->i_state & flags) == flags) | 1211 | if (((inode->i_state & flags) == flags) || |
1212 | (dirtytime && (inode->i_state & I_DIRTY_INODE))) | ||
1179 | return; | 1213 | return; |
1180 | 1214 | ||
1181 | if (unlikely(block_dump)) | 1215 | if (unlikely(block_dump)) |
1182 | block_dump___mark_inode_dirty(inode); | 1216 | block_dump___mark_inode_dirty(inode); |
1183 | 1217 | ||
1184 | spin_lock(&inode->i_lock); | 1218 | spin_lock(&inode->i_lock); |
1219 | if (dirtytime && (inode->i_state & I_DIRTY_INODE)) | ||
1220 | goto out_unlock_inode; | ||
1185 | if ((inode->i_state & flags) != flags) { | 1221 | if ((inode->i_state & flags) != flags) { |
1186 | const int was_dirty = inode->i_state & I_DIRTY; | 1222 | const int was_dirty = inode->i_state & I_DIRTY; |
1187 | 1223 | ||
1224 | if (flags & I_DIRTY_INODE) | ||
1225 | inode->i_state &= ~I_DIRTY_TIME; | ||
1188 | inode->i_state |= flags; | 1226 | inode->i_state |= flags; |
1189 | 1227 | ||
1190 | /* | 1228 | /* |
@@ -1231,8 +1269,10 @@ void __mark_inode_dirty(struct inode *inode, int flags) | |||
1231 | } | 1269 | } |
1232 | 1270 | ||
1233 | inode->dirtied_when = jiffies; | 1271 | inode->dirtied_when = jiffies; |
1234 | list_move(&inode->i_wb_list, &bdi->wb.b_dirty); | 1272 | list_move(&inode->i_wb_list, dirtytime ? |
1273 | &bdi->wb.b_dirty_time : &bdi->wb.b_dirty); | ||
1235 | spin_unlock(&bdi->wb.list_lock); | 1274 | spin_unlock(&bdi->wb.list_lock); |
1275 | trace_writeback_dirty_inode_enqueue(inode); | ||
1236 | 1276 | ||
1237 | if (wakeup_bdi) | 1277 | if (wakeup_bdi) |
1238 | bdi_wakeup_thread_delayed(bdi); | 1278 | bdi_wakeup_thread_delayed(bdi); |