diff options
-rw-r--r-- | fs/ext4/inode.c | 70 | ||||
-rw-r--r-- | fs/ext4/super.c | 10 | ||||
-rw-r--r-- | fs/fs-writeback.c | 62 | ||||
-rw-r--r-- | fs/gfs2/file.c | 4 | ||||
-rw-r--r-- | fs/inode.c | 106 | ||||
-rw-r--r-- | fs/jfs/file.c | 2 | ||||
-rw-r--r-- | fs/libfs.c | 2 | ||||
-rw-r--r-- | fs/proc_namespace.c | 1 | ||||
-rw-r--r-- | fs/sync.c | 8 | ||||
-rw-r--r-- | include/linux/backing-dev.h | 1 | ||||
-rw-r--r-- | include/linux/fs.h | 10 | ||||
-rw-r--r-- | include/trace/events/ext4.h | 30 | ||||
-rw-r--r-- | include/trace/events/writeback.h | 60 | ||||
-rw-r--r-- | include/uapi/linux/fs.h | 4 | ||||
-rw-r--r-- | mm/backing-dev.c | 10 |
15 files changed, 343 insertions, 37 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 28555f191b62..85404f15e53a 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -4174,6 +4174,65 @@ static int ext4_inode_blocks_set(handle_t *handle, | |||
4174 | return 0; | 4174 | return 0; |
4175 | } | 4175 | } |
4176 | 4176 | ||
4177 | struct other_inode { | ||
4178 | unsigned long orig_ino; | ||
4179 | struct ext4_inode *raw_inode; | ||
4180 | }; | ||
4181 | |||
4182 | static int other_inode_match(struct inode * inode, unsigned long ino, | ||
4183 | void *data) | ||
4184 | { | ||
4185 | struct other_inode *oi = (struct other_inode *) data; | ||
4186 | |||
4187 | if ((inode->i_ino != ino) || | ||
4188 | (inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW | | ||
4189 | I_DIRTY_SYNC | I_DIRTY_DATASYNC)) || | ||
4190 | ((inode->i_state & I_DIRTY_TIME) == 0)) | ||
4191 | return 0; | ||
4192 | spin_lock(&inode->i_lock); | ||
4193 | if (((inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW | | ||
4194 | I_DIRTY_SYNC | I_DIRTY_DATASYNC)) == 0) && | ||
4195 | (inode->i_state & I_DIRTY_TIME)) { | ||
4196 | struct ext4_inode_info *ei = EXT4_I(inode); | ||
4197 | |||
4198 | inode->i_state &= ~(I_DIRTY_TIME | I_DIRTY_TIME_EXPIRED); | ||
4199 | spin_unlock(&inode->i_lock); | ||
4200 | |||
4201 | spin_lock(&ei->i_raw_lock); | ||
4202 | EXT4_INODE_SET_XTIME(i_ctime, inode, oi->raw_inode); | ||
4203 | EXT4_INODE_SET_XTIME(i_mtime, inode, oi->raw_inode); | ||
4204 | EXT4_INODE_SET_XTIME(i_atime, inode, oi->raw_inode); | ||
4205 | ext4_inode_csum_set(inode, oi->raw_inode, ei); | ||
4206 | spin_unlock(&ei->i_raw_lock); | ||
4207 | trace_ext4_other_inode_update_time(inode, oi->orig_ino); | ||
4208 | return -1; | ||
4209 | } | ||
4210 | spin_unlock(&inode->i_lock); | ||
4211 | return -1; | ||
4212 | } | ||
4213 | |||
4214 | /* | ||
4215 | * Opportunistically update the other time fields for other inodes in | ||
4216 | * the same inode table block. | ||
4217 | */ | ||
4218 | static void ext4_update_other_inodes_time(struct super_block *sb, | ||
4219 | unsigned long orig_ino, char *buf) | ||
4220 | { | ||
4221 | struct other_inode oi; | ||
4222 | unsigned long ino; | ||
4223 | int i, inodes_per_block = EXT4_SB(sb)->s_inodes_per_block; | ||
4224 | int inode_size = EXT4_INODE_SIZE(sb); | ||
4225 | |||
4226 | oi.orig_ino = orig_ino; | ||
4227 | ino = orig_ino & ~(inodes_per_block - 1); | ||
4228 | for (i = 0; i < inodes_per_block; i++, ino++, buf += inode_size) { | ||
4229 | if (ino == orig_ino) | ||
4230 | continue; | ||
4231 | oi.raw_inode = (struct ext4_inode *) buf; | ||
4232 | (void) find_inode_nowait(sb, ino, other_inode_match, &oi); | ||
4233 | } | ||
4234 | } | ||
4235 | |||
4177 | /* | 4236 | /* |
4178 | * Post the struct inode info into an on-disk inode location in the | 4237 | * Post the struct inode info into an on-disk inode location in the |
4179 | * buffer-cache. This gobbles the caller's reference to the | 4238 | * buffer-cache. This gobbles the caller's reference to the |
@@ -4283,10 +4342,11 @@ static int ext4_do_update_inode(handle_t *handle, | |||
4283 | cpu_to_le16(ei->i_extra_isize); | 4342 | cpu_to_le16(ei->i_extra_isize); |
4284 | } | 4343 | } |
4285 | } | 4344 | } |
4286 | |||
4287 | ext4_inode_csum_set(inode, raw_inode, ei); | 4345 | ext4_inode_csum_set(inode, raw_inode, ei); |
4288 | |||
4289 | spin_unlock(&ei->i_raw_lock); | 4346 | spin_unlock(&ei->i_raw_lock); |
4347 | if (inode->i_sb->s_flags & MS_LAZYTIME) | ||
4348 | ext4_update_other_inodes_time(inode->i_sb, inode->i_ino, | ||
4349 | bh->b_data); | ||
4290 | 4350 | ||
4291 | BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata"); | 4351 | BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata"); |
4292 | rc = ext4_handle_dirty_metadata(handle, NULL, bh); | 4352 | rc = ext4_handle_dirty_metadata(handle, NULL, bh); |
@@ -4875,11 +4935,17 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode) | |||
4875 | * If the inode is marked synchronous, we don't honour that here - doing | 4935 | * If the inode is marked synchronous, we don't honour that here - doing |
4876 | * so would cause a commit on atime updates, which we don't bother doing. | 4936 | * so would cause a commit on atime updates, which we don't bother doing. |
4877 | * We handle synchronous inodes at the highest possible level. | 4937 | * We handle synchronous inodes at the highest possible level. |
4938 | * | ||
4939 | * If only the I_DIRTY_TIME flag is set, we can skip everything. If | ||
4940 | * I_DIRTY_TIME and I_DIRTY_SYNC is set, the only inode fields we need | ||
4941 | * to copy into the on-disk inode structure are the timestamp files. | ||
4878 | */ | 4942 | */ |
4879 | void ext4_dirty_inode(struct inode *inode, int flags) | 4943 | void ext4_dirty_inode(struct inode *inode, int flags) |
4880 | { | 4944 | { |
4881 | handle_t *handle; | 4945 | handle_t *handle; |
4882 | 4946 | ||
4947 | if (flags == I_DIRTY_TIME) | ||
4948 | return; | ||
4883 | handle = ext4_journal_start(inode, EXT4_HT_INODE, 2); | 4949 | handle = ext4_journal_start(inode, EXT4_HT_INODE, 2); |
4884 | if (IS_ERR(handle)) | 4950 | if (IS_ERR(handle)) |
4885 | goto out; | 4951 | goto out; |
diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 10e8c6b7ca08..1adac6868e6f 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c | |||
@@ -1126,6 +1126,7 @@ enum { | |||
1126 | Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err, | 1126 | Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err, |
1127 | Opt_usrquota, Opt_grpquota, Opt_i_version, Opt_dax, | 1127 | Opt_usrquota, Opt_grpquota, Opt_i_version, Opt_dax, |
1128 | Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_mblk_io_submit, | 1128 | Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_mblk_io_submit, |
1129 | Opt_lazytime, Opt_nolazytime, | ||
1129 | Opt_nomblk_io_submit, Opt_block_validity, Opt_noblock_validity, | 1130 | Opt_nomblk_io_submit, Opt_block_validity, Opt_noblock_validity, |
1130 | Opt_inode_readahead_blks, Opt_journal_ioprio, | 1131 | Opt_inode_readahead_blks, Opt_journal_ioprio, |
1131 | Opt_dioread_nolock, Opt_dioread_lock, | 1132 | Opt_dioread_nolock, Opt_dioread_lock, |
@@ -1190,6 +1191,8 @@ static const match_table_t tokens = { | |||
1190 | {Opt_dax, "dax"}, | 1191 | {Opt_dax, "dax"}, |
1191 | {Opt_stripe, "stripe=%u"}, | 1192 | {Opt_stripe, "stripe=%u"}, |
1192 | {Opt_delalloc, "delalloc"}, | 1193 | {Opt_delalloc, "delalloc"}, |
1194 | {Opt_lazytime, "lazytime"}, | ||
1195 | {Opt_nolazytime, "nolazytime"}, | ||
1193 | {Opt_nodelalloc, "nodelalloc"}, | 1196 | {Opt_nodelalloc, "nodelalloc"}, |
1194 | {Opt_removed, "mblk_io_submit"}, | 1197 | {Opt_removed, "mblk_io_submit"}, |
1195 | {Opt_removed, "nomblk_io_submit"}, | 1198 | {Opt_removed, "nomblk_io_submit"}, |
@@ -1448,6 +1451,12 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token, | |||
1448 | case Opt_i_version: | 1451 | case Opt_i_version: |
1449 | sb->s_flags |= MS_I_VERSION; | 1452 | sb->s_flags |= MS_I_VERSION; |
1450 | return 1; | 1453 | return 1; |
1454 | case Opt_lazytime: | ||
1455 | sb->s_flags |= MS_LAZYTIME; | ||
1456 | return 1; | ||
1457 | case Opt_nolazytime: | ||
1458 | sb->s_flags &= ~MS_LAZYTIME; | ||
1459 | return 1; | ||
1451 | } | 1460 | } |
1452 | 1461 | ||
1453 | for (m = ext4_mount_opts; m->token != Opt_err; m++) | 1462 | for (m = ext4_mount_opts; m->token != Opt_err; m++) |
@@ -5044,6 +5053,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) | |||
5044 | } | 5053 | } |
5045 | #endif | 5054 | #endif |
5046 | 5055 | ||
5056 | *flags = (*flags & ~MS_LAZYTIME) | (sb->s_flags & MS_LAZYTIME); | ||
5047 | ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data); | 5057 | ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data); |
5048 | kfree(orig_data); | 5058 | kfree(orig_data); |
5049 | return 0; | 5059 | return 0; |
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); |
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index ec9c2d33477a..3e32bb8e2d7e 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c | |||
@@ -654,7 +654,7 @@ static int gfs2_fsync(struct file *file, loff_t start, loff_t end, | |||
654 | { | 654 | { |
655 | struct address_space *mapping = file->f_mapping; | 655 | struct address_space *mapping = file->f_mapping; |
656 | struct inode *inode = mapping->host; | 656 | struct inode *inode = mapping->host; |
657 | int sync_state = inode->i_state & I_DIRTY; | 657 | int sync_state = inode->i_state & I_DIRTY_ALL; |
658 | struct gfs2_inode *ip = GFS2_I(inode); | 658 | struct gfs2_inode *ip = GFS2_I(inode); |
659 | int ret = 0, ret1 = 0; | 659 | int ret = 0, ret1 = 0; |
660 | 660 | ||
@@ -667,7 +667,7 @@ static int gfs2_fsync(struct file *file, loff_t start, loff_t end, | |||
667 | if (!gfs2_is_jdata(ip)) | 667 | if (!gfs2_is_jdata(ip)) |
668 | sync_state &= ~I_DIRTY_PAGES; | 668 | sync_state &= ~I_DIRTY_PAGES; |
669 | if (datasync) | 669 | if (datasync) |
670 | sync_state &= ~I_DIRTY_SYNC; | 670 | sync_state &= ~(I_DIRTY_SYNC | I_DIRTY_TIME); |
671 | 671 | ||
672 | if (sync_state) { | 672 | if (sync_state) { |
673 | ret = sync_inode_metadata(inode, 1); | 673 | ret = sync_inode_metadata(inode, 1); |
diff --git a/fs/inode.c b/fs/inode.c index 86bfaca724db..f00b16f45507 100644 --- a/fs/inode.c +++ b/fs/inode.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/buffer_head.h> /* for inode_has_buffers */ | 18 | #include <linux/buffer_head.h> /* for inode_has_buffers */ |
19 | #include <linux/ratelimit.h> | 19 | #include <linux/ratelimit.h> |
20 | #include <linux/list_lru.h> | 20 | #include <linux/list_lru.h> |
21 | #include <trace/events/writeback.h> | ||
21 | #include "internal.h" | 22 | #include "internal.h" |
22 | 23 | ||
23 | /* | 24 | /* |
@@ -30,7 +31,7 @@ | |||
30 | * inode_sb_list_lock protects: | 31 | * inode_sb_list_lock protects: |
31 | * sb->s_inodes, inode->i_sb_list | 32 | * sb->s_inodes, inode->i_sb_list |
32 | * bdi->wb.list_lock protects: | 33 | * bdi->wb.list_lock protects: |
33 | * bdi->wb.b_{dirty,io,more_io}, inode->i_wb_list | 34 | * bdi->wb.b_{dirty,io,more_io,dirty_time}, inode->i_wb_list |
34 | * inode_hash_lock protects: | 35 | * inode_hash_lock protects: |
35 | * inode_hashtable, inode->i_hash | 36 | * inode_hashtable, inode->i_hash |
36 | * | 37 | * |
@@ -403,7 +404,8 @@ static void inode_lru_list_add(struct inode *inode) | |||
403 | */ | 404 | */ |
404 | void inode_add_lru(struct inode *inode) | 405 | void inode_add_lru(struct inode *inode) |
405 | { | 406 | { |
406 | if (!(inode->i_state & (I_DIRTY | I_SYNC | I_FREEING | I_WILL_FREE)) && | 407 | if (!(inode->i_state & (I_DIRTY_ALL | I_SYNC | |
408 | I_FREEING | I_WILL_FREE)) && | ||
407 | !atomic_read(&inode->i_count) && inode->i_sb->s_flags & MS_ACTIVE) | 409 | !atomic_read(&inode->i_count) && inode->i_sb->s_flags & MS_ACTIVE) |
408 | inode_lru_list_add(inode); | 410 | inode_lru_list_add(inode); |
409 | } | 411 | } |
@@ -634,7 +636,7 @@ int invalidate_inodes(struct super_block *sb, bool kill_dirty) | |||
634 | spin_unlock(&inode->i_lock); | 636 | spin_unlock(&inode->i_lock); |
635 | continue; | 637 | continue; |
636 | } | 638 | } |
637 | if (inode->i_state & I_DIRTY && !kill_dirty) { | 639 | if (inode->i_state & I_DIRTY_ALL && !kill_dirty) { |
638 | spin_unlock(&inode->i_lock); | 640 | spin_unlock(&inode->i_lock); |
639 | busy = 1; | 641 | busy = 1; |
640 | continue; | 642 | continue; |
@@ -1268,6 +1270,56 @@ struct inode *ilookup(struct super_block *sb, unsigned long ino) | |||
1268 | } | 1270 | } |
1269 | EXPORT_SYMBOL(ilookup); | 1271 | EXPORT_SYMBOL(ilookup); |
1270 | 1272 | ||
1273 | /** | ||
1274 | * find_inode_nowait - find an inode in the inode cache | ||
1275 | * @sb: super block of file system to search | ||
1276 | * @hashval: hash value (usually inode number) to search for | ||
1277 | * @match: callback used for comparisons between inodes | ||
1278 | * @data: opaque data pointer to pass to @match | ||
1279 | * | ||
1280 | * Search for the inode specified by @hashval and @data in the inode | ||
1281 | * cache, where the helper function @match will return 0 if the inode | ||
1282 | * does not match, 1 if the inode does match, and -1 if the search | ||
1283 | * should be stopped. The @match function must be responsible for | ||
1284 | * taking the i_lock spin_lock and checking i_state for an inode being | ||
1285 | * freed or being initialized, and incrementing the reference count | ||
1286 | * before returning 1. It also must not sleep, since it is called with | ||
1287 | * the inode_hash_lock spinlock held. | ||
1288 | * | ||
1289 | * This is a even more generalized version of ilookup5() when the | ||
1290 | * function must never block --- find_inode() can block in | ||
1291 | * __wait_on_freeing_inode() --- or when the caller can not increment | ||
1292 | * the reference count because the resulting iput() might cause an | ||
1293 | * inode eviction. The tradeoff is that the @match funtion must be | ||
1294 | * very carefully implemented. | ||
1295 | */ | ||
1296 | struct inode *find_inode_nowait(struct super_block *sb, | ||
1297 | unsigned long hashval, | ||
1298 | int (*match)(struct inode *, unsigned long, | ||
1299 | void *), | ||
1300 | void *data) | ||
1301 | { | ||
1302 | struct hlist_head *head = inode_hashtable + hash(sb, hashval); | ||
1303 | struct inode *inode, *ret_inode = NULL; | ||
1304 | int mval; | ||
1305 | |||
1306 | spin_lock(&inode_hash_lock); | ||
1307 | hlist_for_each_entry(inode, head, i_hash) { | ||
1308 | if (inode->i_sb != sb) | ||
1309 | continue; | ||
1310 | mval = match(inode, hashval, data); | ||
1311 | if (mval == 0) | ||
1312 | continue; | ||
1313 | if (mval == 1) | ||
1314 | ret_inode = inode; | ||
1315 | goto out; | ||
1316 | } | ||
1317 | out: | ||
1318 | spin_unlock(&inode_hash_lock); | ||
1319 | return ret_inode; | ||
1320 | } | ||
1321 | EXPORT_SYMBOL(find_inode_nowait); | ||
1322 | |||
1271 | int insert_inode_locked(struct inode *inode) | 1323 | int insert_inode_locked(struct inode *inode) |
1272 | { | 1324 | { |
1273 | struct super_block *sb = inode->i_sb; | 1325 | struct super_block *sb = inode->i_sb; |
@@ -1418,11 +1470,20 @@ static void iput_final(struct inode *inode) | |||
1418 | */ | 1470 | */ |
1419 | void iput(struct inode *inode) | 1471 | void iput(struct inode *inode) |
1420 | { | 1472 | { |
1421 | if (inode) { | 1473 | if (!inode) |
1422 | BUG_ON(inode->i_state & I_CLEAR); | 1474 | return; |
1423 | 1475 | BUG_ON(inode->i_state & I_CLEAR); | |
1424 | if (atomic_dec_and_lock(&inode->i_count, &inode->i_lock)) | 1476 | retry: |
1425 | iput_final(inode); | 1477 | if (atomic_dec_and_lock(&inode->i_count, &inode->i_lock)) { |
1478 | if (inode->i_nlink && (inode->i_state & I_DIRTY_TIME)) { | ||
1479 | atomic_inc(&inode->i_count); | ||
1480 | inode->i_state &= ~I_DIRTY_TIME; | ||
1481 | spin_unlock(&inode->i_lock); | ||
1482 | trace_writeback_lazytime_iput(inode); | ||
1483 | mark_inode_dirty_sync(inode); | ||
1484 | goto retry; | ||
1485 | } | ||
1486 | iput_final(inode); | ||
1426 | } | 1487 | } |
1427 | } | 1488 | } |
1428 | EXPORT_SYMBOL(iput); | 1489 | EXPORT_SYMBOL(iput); |
@@ -1481,14 +1542,9 @@ static int relatime_need_update(struct vfsmount *mnt, struct inode *inode, | |||
1481 | return 0; | 1542 | return 0; |
1482 | } | 1543 | } |
1483 | 1544 | ||
1484 | /* | 1545 | int generic_update_time(struct inode *inode, struct timespec *time, int flags) |
1485 | * This does the actual work of updating an inodes time or version. Must have | ||
1486 | * had called mnt_want_write() before calling this. | ||
1487 | */ | ||
1488 | static int update_time(struct inode *inode, struct timespec *time, int flags) | ||
1489 | { | 1546 | { |
1490 | if (inode->i_op->update_time) | 1547 | int iflags = I_DIRTY_TIME; |
1491 | return inode->i_op->update_time(inode, time, flags); | ||
1492 | 1548 | ||
1493 | if (flags & S_ATIME) | 1549 | if (flags & S_ATIME) |
1494 | inode->i_atime = *time; | 1550 | inode->i_atime = *time; |
@@ -1498,9 +1554,27 @@ static int update_time(struct inode *inode, struct timespec *time, int flags) | |||
1498 | inode->i_ctime = *time; | 1554 | inode->i_ctime = *time; |
1499 | if (flags & S_MTIME) | 1555 | if (flags & S_MTIME) |
1500 | inode->i_mtime = *time; | 1556 | inode->i_mtime = *time; |
1501 | mark_inode_dirty_sync(inode); | 1557 | |
1558 | if (!(inode->i_sb->s_flags & MS_LAZYTIME) || (flags & S_VERSION)) | ||
1559 | iflags |= I_DIRTY_SYNC; | ||
1560 | __mark_inode_dirty(inode, iflags); | ||
1502 | return 0; | 1561 | return 0; |
1503 | } | 1562 | } |
1563 | EXPORT_SYMBOL(generic_update_time); | ||
1564 | |||
1565 | /* | ||
1566 | * This does the actual work of updating an inodes time or version. Must have | ||
1567 | * had called mnt_want_write() before calling this. | ||
1568 | */ | ||
1569 | static int update_time(struct inode *inode, struct timespec *time, int flags) | ||
1570 | { | ||
1571 | int (*update_time)(struct inode *, struct timespec *, int); | ||
1572 | |||
1573 | update_time = inode->i_op->update_time ? inode->i_op->update_time : | ||
1574 | generic_update_time; | ||
1575 | |||
1576 | return update_time(inode, time, flags); | ||
1577 | } | ||
1504 | 1578 | ||
1505 | /** | 1579 | /** |
1506 | * touch_atime - update the access time | 1580 | * touch_atime - update the access time |
diff --git a/fs/jfs/file.c b/fs/jfs/file.c index 33aa0cc1f8b8..10815f8dfd8b 100644 --- a/fs/jfs/file.c +++ b/fs/jfs/file.c | |||
@@ -39,7 +39,7 @@ int jfs_fsync(struct file *file, loff_t start, loff_t end, int datasync) | |||
39 | return rc; | 39 | return rc; |
40 | 40 | ||
41 | mutex_lock(&inode->i_mutex); | 41 | mutex_lock(&inode->i_mutex); |
42 | if (!(inode->i_state & I_DIRTY) || | 42 | if (!(inode->i_state & I_DIRTY_ALL) || |
43 | (datasync && !(inode->i_state & I_DIRTY_DATASYNC))) { | 43 | (datasync && !(inode->i_state & I_DIRTY_DATASYNC))) { |
44 | /* Make sure committed changes hit the disk */ | 44 | /* Make sure committed changes hit the disk */ |
45 | jfs_flush_journal(JFS_SBI(inode->i_sb)->log, 1); | 45 | jfs_flush_journal(JFS_SBI(inode->i_sb)->log, 1); |
diff --git a/fs/libfs.c b/fs/libfs.c index 005843ce5dbd..b2ffdb045be4 100644 --- a/fs/libfs.c +++ b/fs/libfs.c | |||
@@ -948,7 +948,7 @@ int __generic_file_fsync(struct file *file, loff_t start, loff_t end, | |||
948 | 948 | ||
949 | mutex_lock(&inode->i_mutex); | 949 | mutex_lock(&inode->i_mutex); |
950 | ret = sync_mapping_buffers(inode->i_mapping); | 950 | ret = sync_mapping_buffers(inode->i_mapping); |
951 | if (!(inode->i_state & I_DIRTY)) | 951 | if (!(inode->i_state & I_DIRTY_ALL)) |
952 | goto out; | 952 | goto out; |
953 | if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) | 953 | if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) |
954 | goto out; | 954 | goto out; |
diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index 0f96f71ab32b..8db932da4009 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c | |||
@@ -44,6 +44,7 @@ static int show_sb_opts(struct seq_file *m, struct super_block *sb) | |||
44 | { MS_SYNCHRONOUS, ",sync" }, | 44 | { MS_SYNCHRONOUS, ",sync" }, |
45 | { MS_DIRSYNC, ",dirsync" }, | 45 | { MS_DIRSYNC, ",dirsync" }, |
46 | { MS_MANDLOCK, ",mand" }, | 46 | { MS_MANDLOCK, ",mand" }, |
47 | { MS_LAZYTIME, ",lazytime" }, | ||
47 | { 0, NULL } | 48 | { 0, NULL } |
48 | }; | 49 | }; |
49 | const struct proc_fs_info *fs_infop; | 50 | const struct proc_fs_info *fs_infop; |
@@ -177,8 +177,16 @@ SYSCALL_DEFINE1(syncfs, int, fd) | |||
177 | */ | 177 | */ |
178 | int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync) | 178 | int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync) |
179 | { | 179 | { |
180 | struct inode *inode = file->f_mapping->host; | ||
181 | |||
180 | if (!file->f_op->fsync) | 182 | if (!file->f_op->fsync) |
181 | return -EINVAL; | 183 | return -EINVAL; |
184 | if (!datasync && (inode->i_state & I_DIRTY_TIME)) { | ||
185 | spin_lock(&inode->i_lock); | ||
186 | inode->i_state &= ~I_DIRTY_TIME; | ||
187 | spin_unlock(&inode->i_lock); | ||
188 | mark_inode_dirty_sync(inode); | ||
189 | } | ||
182 | return file->f_op->fsync(file, start, end, datasync); | 190 | return file->f_op->fsync(file, start, end, datasync); |
183 | } | 191 | } |
184 | EXPORT_SYMBOL(vfs_fsync_range); | 192 | EXPORT_SYMBOL(vfs_fsync_range); |
diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index d94077fea1f8..aff923ae8c4b 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h | |||
@@ -55,6 +55,7 @@ struct bdi_writeback { | |||
55 | struct list_head b_dirty; /* dirty inodes */ | 55 | struct list_head b_dirty; /* dirty inodes */ |
56 | struct list_head b_io; /* parked for writeback */ | 56 | struct list_head b_io; /* parked for writeback */ |
57 | struct list_head b_more_io; /* parked for more writeback */ | 57 | struct list_head b_more_io; /* parked for more writeback */ |
58 | struct list_head b_dirty_time; /* time stamps are dirty */ | ||
58 | spinlock_t list_lock; /* protects the b_* lists */ | 59 | spinlock_t list_lock; /* protects the b_* lists */ |
59 | }; | 60 | }; |
60 | 61 | ||
diff --git a/include/linux/fs.h b/include/linux/fs.h index 0b5b146d0490..447932aed1e1 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -1790,8 +1790,12 @@ struct super_operations { | |||
1790 | #define __I_DIO_WAKEUP 9 | 1790 | #define __I_DIO_WAKEUP 9 |
1791 | #define I_DIO_WAKEUP (1 << I_DIO_WAKEUP) | 1791 | #define I_DIO_WAKEUP (1 << I_DIO_WAKEUP) |
1792 | #define I_LINKABLE (1 << 10) | 1792 | #define I_LINKABLE (1 << 10) |
1793 | #define I_DIRTY_TIME (1 << 11) | ||
1794 | #define __I_DIRTY_TIME_EXPIRED 12 | ||
1795 | #define I_DIRTY_TIME_EXPIRED (1 << __I_DIRTY_TIME_EXPIRED) | ||
1793 | 1796 | ||
1794 | #define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES) | 1797 | #define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES) |
1798 | #define I_DIRTY_ALL (I_DIRTY | I_DIRTY_TIME) | ||
1795 | 1799 | ||
1796 | extern void __mark_inode_dirty(struct inode *, int); | 1800 | extern void __mark_inode_dirty(struct inode *, int); |
1797 | static inline void mark_inode_dirty(struct inode *inode) | 1801 | static inline void mark_inode_dirty(struct inode *inode) |
@@ -1954,6 +1958,7 @@ extern int current_umask(void); | |||
1954 | 1958 | ||
1955 | extern void ihold(struct inode * inode); | 1959 | extern void ihold(struct inode * inode); |
1956 | extern void iput(struct inode *); | 1960 | extern void iput(struct inode *); |
1961 | extern int generic_update_time(struct inode *, struct timespec *, int); | ||
1957 | 1962 | ||
1958 | static inline struct inode *file_inode(const struct file *f) | 1963 | static inline struct inode *file_inode(const struct file *f) |
1959 | { | 1964 | { |
@@ -2492,6 +2497,11 @@ extern struct inode *ilookup(struct super_block *sb, unsigned long ino); | |||
2492 | 2497 | ||
2493 | extern struct inode * iget5_locked(struct super_block *, unsigned long, int (*test)(struct inode *, void *), int (*set)(struct inode *, void *), void *); | 2498 | extern struct inode * iget5_locked(struct super_block *, unsigned long, int (*test)(struct inode *, void *), int (*set)(struct inode *, void *), void *); |
2494 | extern struct inode * iget_locked(struct super_block *, unsigned long); | 2499 | extern struct inode * iget_locked(struct super_block *, unsigned long); |
2500 | extern struct inode *find_inode_nowait(struct super_block *, | ||
2501 | unsigned long, | ||
2502 | int (*match)(struct inode *, | ||
2503 | unsigned long, void *), | ||
2504 | void *data); | ||
2495 | extern int insert_inode_locked4(struct inode *, unsigned long, int (*test)(struct inode *, void *), void *); | 2505 | extern int insert_inode_locked4(struct inode *, unsigned long, int (*test)(struct inode *, void *), void *); |
2496 | extern int insert_inode_locked(struct inode *); | 2506 | extern int insert_inode_locked(struct inode *); |
2497 | #ifdef CONFIG_DEBUG_LOCK_ALLOC | 2507 | #ifdef CONFIG_DEBUG_LOCK_ALLOC |
diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index 6cfb841fea7c..6e5abd6d38a2 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h | |||
@@ -73,6 +73,36 @@ struct extent_status; | |||
73 | { FALLOC_FL_ZERO_RANGE, "ZERO_RANGE"}) | 73 | { FALLOC_FL_ZERO_RANGE, "ZERO_RANGE"}) |
74 | 74 | ||
75 | 75 | ||
76 | TRACE_EVENT(ext4_other_inode_update_time, | ||
77 | TP_PROTO(struct inode *inode, ino_t orig_ino), | ||
78 | |||
79 | TP_ARGS(inode, orig_ino), | ||
80 | |||
81 | TP_STRUCT__entry( | ||
82 | __field( dev_t, dev ) | ||
83 | __field( ino_t, ino ) | ||
84 | __field( ino_t, orig_ino ) | ||
85 | __field( uid_t, uid ) | ||
86 | __field( gid_t, gid ) | ||
87 | __field( __u16, mode ) | ||
88 | ), | ||
89 | |||
90 | TP_fast_assign( | ||
91 | __entry->orig_ino = orig_ino; | ||
92 | __entry->dev = inode->i_sb->s_dev; | ||
93 | __entry->ino = inode->i_ino; | ||
94 | __entry->uid = i_uid_read(inode); | ||
95 | __entry->gid = i_gid_read(inode); | ||
96 | __entry->mode = inode->i_mode; | ||
97 | ), | ||
98 | |||
99 | TP_printk("dev %d,%d orig_ino %lu ino %lu mode 0%o uid %u gid %u", | ||
100 | MAJOR(__entry->dev), MINOR(__entry->dev), | ||
101 | (unsigned long) __entry->orig_ino, | ||
102 | (unsigned long) __entry->ino, __entry->mode, | ||
103 | __entry->uid, __entry->gid) | ||
104 | ); | ||
105 | |||
76 | TRACE_EVENT(ext4_free_inode, | 106 | TRACE_EVENT(ext4_free_inode, |
77 | TP_PROTO(struct inode *inode), | 107 | TP_PROTO(struct inode *inode), |
78 | 108 | ||
diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index 0e9310905413..5a14ead59696 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h | |||
@@ -18,6 +18,8 @@ | |||
18 | {I_FREEING, "I_FREEING"}, \ | 18 | {I_FREEING, "I_FREEING"}, \ |
19 | {I_CLEAR, "I_CLEAR"}, \ | 19 | {I_CLEAR, "I_CLEAR"}, \ |
20 | {I_SYNC, "I_SYNC"}, \ | 20 | {I_SYNC, "I_SYNC"}, \ |
21 | {I_DIRTY_TIME, "I_DIRTY_TIME"}, \ | ||
22 | {I_DIRTY_TIME_EXPIRED, "I_DIRTY_TIME_EXPIRED"}, \ | ||
21 | {I_REFERENCED, "I_REFERENCED"} \ | 23 | {I_REFERENCED, "I_REFERENCED"} \ |
22 | ) | 24 | ) |
23 | 25 | ||
@@ -68,6 +70,7 @@ DECLARE_EVENT_CLASS(writeback_dirty_inode_template, | |||
68 | TP_STRUCT__entry ( | 70 | TP_STRUCT__entry ( |
69 | __array(char, name, 32) | 71 | __array(char, name, 32) |
70 | __field(unsigned long, ino) | 72 | __field(unsigned long, ino) |
73 | __field(unsigned long, state) | ||
71 | __field(unsigned long, flags) | 74 | __field(unsigned long, flags) |
72 | ), | 75 | ), |
73 | 76 | ||
@@ -78,16 +81,25 @@ DECLARE_EVENT_CLASS(writeback_dirty_inode_template, | |||
78 | strncpy(__entry->name, | 81 | strncpy(__entry->name, |
79 | bdi->dev ? dev_name(bdi->dev) : "(unknown)", 32); | 82 | bdi->dev ? dev_name(bdi->dev) : "(unknown)", 32); |
80 | __entry->ino = inode->i_ino; | 83 | __entry->ino = inode->i_ino; |
84 | __entry->state = inode->i_state; | ||
81 | __entry->flags = flags; | 85 | __entry->flags = flags; |
82 | ), | 86 | ), |
83 | 87 | ||
84 | TP_printk("bdi %s: ino=%lu flags=%s", | 88 | TP_printk("bdi %s: ino=%lu state=%s flags=%s", |
85 | __entry->name, | 89 | __entry->name, |
86 | __entry->ino, | 90 | __entry->ino, |
91 | show_inode_state(__entry->state), | ||
87 | show_inode_state(__entry->flags) | 92 | show_inode_state(__entry->flags) |
88 | ) | 93 | ) |
89 | ); | 94 | ); |
90 | 95 | ||
96 | DEFINE_EVENT(writeback_dirty_inode_template, writeback_mark_inode_dirty, | ||
97 | |||
98 | TP_PROTO(struct inode *inode, int flags), | ||
99 | |||
100 | TP_ARGS(inode, flags) | ||
101 | ); | ||
102 | |||
91 | DEFINE_EVENT(writeback_dirty_inode_template, writeback_dirty_inode_start, | 103 | DEFINE_EVENT(writeback_dirty_inode_template, writeback_dirty_inode_start, |
92 | 104 | ||
93 | TP_PROTO(struct inode *inode, int flags), | 105 | TP_PROTO(struct inode *inode, int flags), |
@@ -596,6 +608,52 @@ DEFINE_EVENT(writeback_single_inode_template, writeback_single_inode, | |||
596 | TP_ARGS(inode, wbc, nr_to_write) | 608 | TP_ARGS(inode, wbc, nr_to_write) |
597 | ); | 609 | ); |
598 | 610 | ||
611 | DECLARE_EVENT_CLASS(writeback_lazytime_template, | ||
612 | TP_PROTO(struct inode *inode), | ||
613 | |||
614 | TP_ARGS(inode), | ||
615 | |||
616 | TP_STRUCT__entry( | ||
617 | __field( dev_t, dev ) | ||
618 | __field(unsigned long, ino ) | ||
619 | __field(unsigned long, state ) | ||
620 | __field( __u16, mode ) | ||
621 | __field(unsigned long, dirtied_when ) | ||
622 | ), | ||
623 | |||
624 | TP_fast_assign( | ||
625 | __entry->dev = inode->i_sb->s_dev; | ||
626 | __entry->ino = inode->i_ino; | ||
627 | __entry->state = inode->i_state; | ||
628 | __entry->mode = inode->i_mode; | ||
629 | __entry->dirtied_when = inode->dirtied_when; | ||
630 | ), | ||
631 | |||
632 | TP_printk("dev %d,%d ino %lu dirtied %lu state %s mode 0%o", | ||
633 | MAJOR(__entry->dev), MINOR(__entry->dev), | ||
634 | __entry->ino, __entry->dirtied_when, | ||
635 | show_inode_state(__entry->state), __entry->mode) | ||
636 | ); | ||
637 | |||
638 | DEFINE_EVENT(writeback_lazytime_template, writeback_lazytime, | ||
639 | TP_PROTO(struct inode *inode), | ||
640 | |||
641 | TP_ARGS(inode) | ||
642 | ); | ||
643 | |||
644 | DEFINE_EVENT(writeback_lazytime_template, writeback_lazytime_iput, | ||
645 | TP_PROTO(struct inode *inode), | ||
646 | |||
647 | TP_ARGS(inode) | ||
648 | ); | ||
649 | |||
650 | DEFINE_EVENT(writeback_lazytime_template, writeback_dirty_inode_enqueue, | ||
651 | |||
652 | TP_PROTO(struct inode *inode), | ||
653 | |||
654 | TP_ARGS(inode) | ||
655 | ); | ||
656 | |||
599 | #endif /* _TRACE_WRITEBACK_H */ | 657 | #endif /* _TRACE_WRITEBACK_H */ |
600 | 658 | ||
601 | /* This part must be outside protection */ | 659 | /* This part must be outside protection */ |
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index 3735fa0a6784..9b964a5920af 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h | |||
@@ -90,6 +90,7 @@ struct inodes_stat_t { | |||
90 | #define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */ | 90 | #define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */ |
91 | #define MS_I_VERSION (1<<23) /* Update inode I_version field */ | 91 | #define MS_I_VERSION (1<<23) /* Update inode I_version field */ |
92 | #define MS_STRICTATIME (1<<24) /* Always perform atime updates */ | 92 | #define MS_STRICTATIME (1<<24) /* Always perform atime updates */ |
93 | #define MS_LAZYTIME (1<<25) /* Update the on-disk [acm]times lazily */ | ||
93 | 94 | ||
94 | /* These sb flags are internal to the kernel */ | 95 | /* These sb flags are internal to the kernel */ |
95 | #define MS_NOSEC (1<<28) | 96 | #define MS_NOSEC (1<<28) |
@@ -100,7 +101,8 @@ struct inodes_stat_t { | |||
100 | /* | 101 | /* |
101 | * Superblock flags that can be altered by MS_REMOUNT | 102 | * Superblock flags that can be altered by MS_REMOUNT |
102 | */ | 103 | */ |
103 | #define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION) | 104 | #define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|\ |
105 | MS_LAZYTIME) | ||
104 | 106 | ||
105 | /* | 107 | /* |
106 | * Old magic mount flag and mask | 108 | * Old magic mount flag and mask |
diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 7690ec77c722..6dc4580df2af 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c | |||
@@ -49,10 +49,10 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) | |||
49 | unsigned long background_thresh; | 49 | unsigned long background_thresh; |
50 | unsigned long dirty_thresh; | 50 | unsigned long dirty_thresh; |
51 | unsigned long bdi_thresh; | 51 | unsigned long bdi_thresh; |
52 | unsigned long nr_dirty, nr_io, nr_more_io; | 52 | unsigned long nr_dirty, nr_io, nr_more_io, nr_dirty_time; |
53 | struct inode *inode; | 53 | struct inode *inode; |
54 | 54 | ||
55 | nr_dirty = nr_io = nr_more_io = 0; | 55 | nr_dirty = nr_io = nr_more_io = nr_dirty_time = 0; |
56 | spin_lock(&wb->list_lock); | 56 | spin_lock(&wb->list_lock); |
57 | list_for_each_entry(inode, &wb->b_dirty, i_wb_list) | 57 | list_for_each_entry(inode, &wb->b_dirty, i_wb_list) |
58 | nr_dirty++; | 58 | nr_dirty++; |
@@ -60,6 +60,9 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) | |||
60 | nr_io++; | 60 | nr_io++; |
61 | list_for_each_entry(inode, &wb->b_more_io, i_wb_list) | 61 | list_for_each_entry(inode, &wb->b_more_io, i_wb_list) |
62 | nr_more_io++; | 62 | nr_more_io++; |
63 | list_for_each_entry(inode, &wb->b_dirty_time, i_wb_list) | ||
64 | if (inode->i_state & I_DIRTY_TIME) | ||
65 | nr_dirty_time++; | ||
63 | spin_unlock(&wb->list_lock); | 66 | spin_unlock(&wb->list_lock); |
64 | 67 | ||
65 | global_dirty_limits(&background_thresh, &dirty_thresh); | 68 | global_dirty_limits(&background_thresh, &dirty_thresh); |
@@ -78,6 +81,7 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) | |||
78 | "b_dirty: %10lu\n" | 81 | "b_dirty: %10lu\n" |
79 | "b_io: %10lu\n" | 82 | "b_io: %10lu\n" |
80 | "b_more_io: %10lu\n" | 83 | "b_more_io: %10lu\n" |
84 | "b_dirty_time: %10lu\n" | ||
81 | "bdi_list: %10u\n" | 85 | "bdi_list: %10u\n" |
82 | "state: %10lx\n", | 86 | "state: %10lx\n", |
83 | (unsigned long) K(bdi_stat(bdi, BDI_WRITEBACK)), | 87 | (unsigned long) K(bdi_stat(bdi, BDI_WRITEBACK)), |
@@ -91,6 +95,7 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) | |||
91 | nr_dirty, | 95 | nr_dirty, |
92 | nr_io, | 96 | nr_io, |
93 | nr_more_io, | 97 | nr_more_io, |
98 | nr_dirty_time, | ||
94 | !list_empty(&bdi->bdi_list), bdi->state); | 99 | !list_empty(&bdi->bdi_list), bdi->state); |
95 | #undef K | 100 | #undef K |
96 | 101 | ||
@@ -380,6 +385,7 @@ static void bdi_wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi) | |||
380 | INIT_LIST_HEAD(&wb->b_dirty); | 385 | INIT_LIST_HEAD(&wb->b_dirty); |
381 | INIT_LIST_HEAD(&wb->b_io); | 386 | INIT_LIST_HEAD(&wb->b_io); |
382 | INIT_LIST_HEAD(&wb->b_more_io); | 387 | INIT_LIST_HEAD(&wb->b_more_io); |
388 | INIT_LIST_HEAD(&wb->b_dirty_time); | ||
383 | spin_lock_init(&wb->list_lock); | 389 | spin_lock_init(&wb->list_lock); |
384 | INIT_DELAYED_WORK(&wb->dwork, bdi_writeback_workfn); | 390 | INIT_DELAYED_WORK(&wb->dwork, bdi_writeback_workfn); |
385 | } | 391 | } |