diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-26 20:58:44 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-26 20:58:44 -0400 |
commit | 426e1f5cec4821945642230218876b0e89aafab1 (patch) | |
tree | 2728ace018d0698886989da586210ef1543a7098 /fs/fs-writeback.c | |
parent | 9e5fca251f44832cb996961048ea977f80faf6ea (diff) | |
parent | 63997e98a3be68d7cec806d22bf9b02b2e1daabb (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6: (52 commits)
split invalidate_inodes()
fs: skip I_FREEING inodes in writeback_sb_inodes
fs: fold invalidate_list into invalidate_inodes
fs: do not drop inode_lock in dispose_list
fs: inode split IO and LRU lists
fs: switch bdev inode bdi's correctly
fs: fix buffer invalidation in invalidate_list
fsnotify: use dget_parent
smbfs: use dget_parent
exportfs: use dget_parent
fs: use RCU read side protection in d_validate
fs: clean up dentry lru modification
fs: split __shrink_dcache_sb
fs: improve DCACHE_REFERENCED usage
fs: use percpu counter for nr_dentry and nr_dentry_unused
fs: simplify __d_free
fs: take dcache_lock inside __d_path
fs: do not assign default i_ino in new_inode
fs: introduce a per-cpu last_ino allocator
new helper: ihold()
...
Diffstat (limited to 'fs/fs-writeback.c')
-rw-r--r-- | fs/fs-writeback.c | 80 |
1 files changed, 51 insertions, 29 deletions
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 9e46aec10d1a..aed881a76b22 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c | |||
@@ -79,6 +79,11 @@ static inline struct backing_dev_info *inode_to_bdi(struct inode *inode) | |||
79 | return sb->s_bdi; | 79 | return sb->s_bdi; |
80 | } | 80 | } |
81 | 81 | ||
82 | static inline struct inode *wb_inode(struct list_head *head) | ||
83 | { | ||
84 | return list_entry(head, struct inode, i_wb_list); | ||
85 | } | ||
86 | |||
82 | static void bdi_queue_work(struct backing_dev_info *bdi, | 87 | static void bdi_queue_work(struct backing_dev_info *bdi, |
83 | struct wb_writeback_work *work) | 88 | struct wb_writeback_work *work) |
84 | { | 89 | { |
@@ -172,11 +177,11 @@ static void redirty_tail(struct inode *inode) | |||
172 | if (!list_empty(&wb->b_dirty)) { | 177 | if (!list_empty(&wb->b_dirty)) { |
173 | struct inode *tail; | 178 | struct inode *tail; |
174 | 179 | ||
175 | tail = list_entry(wb->b_dirty.next, struct inode, i_list); | 180 | tail = wb_inode(wb->b_dirty.next); |
176 | if (time_before(inode->dirtied_when, tail->dirtied_when)) | 181 | if (time_before(inode->dirtied_when, tail->dirtied_when)) |
177 | inode->dirtied_when = jiffies; | 182 | inode->dirtied_when = jiffies; |
178 | } | 183 | } |
179 | list_move(&inode->i_list, &wb->b_dirty); | 184 | list_move(&inode->i_wb_list, &wb->b_dirty); |
180 | } | 185 | } |
181 | 186 | ||
182 | /* | 187 | /* |
@@ -186,7 +191,7 @@ static void requeue_io(struct inode *inode) | |||
186 | { | 191 | { |
187 | struct bdi_writeback *wb = &inode_to_bdi(inode)->wb; | 192 | struct bdi_writeback *wb = &inode_to_bdi(inode)->wb; |
188 | 193 | ||
189 | list_move(&inode->i_list, &wb->b_more_io); | 194 | list_move(&inode->i_wb_list, &wb->b_more_io); |
190 | } | 195 | } |
191 | 196 | ||
192 | static void inode_sync_complete(struct inode *inode) | 197 | static void inode_sync_complete(struct inode *inode) |
@@ -227,14 +232,14 @@ static void move_expired_inodes(struct list_head *delaying_queue, | |||
227 | int do_sb_sort = 0; | 232 | int do_sb_sort = 0; |
228 | 233 | ||
229 | while (!list_empty(delaying_queue)) { | 234 | while (!list_empty(delaying_queue)) { |
230 | inode = list_entry(delaying_queue->prev, struct inode, i_list); | 235 | inode = wb_inode(delaying_queue->prev); |
231 | if (older_than_this && | 236 | if (older_than_this && |
232 | inode_dirtied_after(inode, *older_than_this)) | 237 | inode_dirtied_after(inode, *older_than_this)) |
233 | break; | 238 | break; |
234 | if (sb && sb != inode->i_sb) | 239 | if (sb && sb != inode->i_sb) |
235 | do_sb_sort = 1; | 240 | do_sb_sort = 1; |
236 | sb = inode->i_sb; | 241 | sb = inode->i_sb; |
237 | list_move(&inode->i_list, &tmp); | 242 | list_move(&inode->i_wb_list, &tmp); |
238 | } | 243 | } |
239 | 244 | ||
240 | /* just one sb in list, splice to dispatch_queue and we're done */ | 245 | /* just one sb in list, splice to dispatch_queue and we're done */ |
@@ -245,12 +250,11 @@ static void move_expired_inodes(struct list_head *delaying_queue, | |||
245 | 250 | ||
246 | /* Move inodes from one superblock together */ | 251 | /* Move inodes from one superblock together */ |
247 | while (!list_empty(&tmp)) { | 252 | while (!list_empty(&tmp)) { |
248 | inode = list_entry(tmp.prev, struct inode, i_list); | 253 | sb = wb_inode(tmp.prev)->i_sb; |
249 | sb = inode->i_sb; | ||
250 | list_for_each_prev_safe(pos, node, &tmp) { | 254 | list_for_each_prev_safe(pos, node, &tmp) { |
251 | inode = list_entry(pos, struct inode, i_list); | 255 | inode = wb_inode(pos); |
252 | if (inode->i_sb == sb) | 256 | if (inode->i_sb == sb) |
253 | list_move(&inode->i_list, dispatch_queue); | 257 | list_move(&inode->i_wb_list, dispatch_queue); |
254 | } | 258 | } |
255 | } | 259 | } |
256 | } | 260 | } |
@@ -408,16 +412,13 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc) | |||
408 | * completion. | 412 | * completion. |
409 | */ | 413 | */ |
410 | redirty_tail(inode); | 414 | redirty_tail(inode); |
411 | } else if (atomic_read(&inode->i_count)) { | ||
412 | /* | ||
413 | * The inode is clean, inuse | ||
414 | */ | ||
415 | list_move(&inode->i_list, &inode_in_use); | ||
416 | } else { | 415 | } else { |
417 | /* | 416 | /* |
418 | * The inode is clean, unused | 417 | * The inode is clean. At this point we either have |
418 | * a reference to the inode or it's on it's way out. | ||
419 | * No need to add it back to the LRU. | ||
419 | */ | 420 | */ |
420 | list_move(&inode->i_list, &inode_unused); | 421 | list_del_init(&inode->i_wb_list); |
421 | } | 422 | } |
422 | } | 423 | } |
423 | inode_sync_complete(inode); | 424 | inode_sync_complete(inode); |
@@ -465,8 +466,7 @@ static int writeback_sb_inodes(struct super_block *sb, struct bdi_writeback *wb, | |||
465 | { | 466 | { |
466 | while (!list_empty(&wb->b_io)) { | 467 | while (!list_empty(&wb->b_io)) { |
467 | long pages_skipped; | 468 | long pages_skipped; |
468 | struct inode *inode = list_entry(wb->b_io.prev, | 469 | struct inode *inode = wb_inode(wb->b_io.prev); |
469 | struct inode, i_list); | ||
470 | 470 | ||
471 | if (inode->i_sb != sb) { | 471 | if (inode->i_sb != sb) { |
472 | if (only_this_sb) { | 472 | if (only_this_sb) { |
@@ -487,10 +487,16 @@ static int writeback_sb_inodes(struct super_block *sb, struct bdi_writeback *wb, | |||
487 | return 0; | 487 | return 0; |
488 | } | 488 | } |
489 | 489 | ||
490 | if (inode->i_state & (I_NEW | I_WILL_FREE)) { | 490 | /* |
491 | * Don't bother with new inodes or inodes beeing freed, first | ||
492 | * kind does not need peridic writeout yet, and for the latter | ||
493 | * kind writeout is handled by the freer. | ||
494 | */ | ||
495 | if (inode->i_state & (I_NEW | I_FREEING | I_WILL_FREE)) { | ||
491 | requeue_io(inode); | 496 | requeue_io(inode); |
492 | continue; | 497 | continue; |
493 | } | 498 | } |
499 | |||
494 | /* | 500 | /* |
495 | * Was this inode dirtied after sync_sb_inodes was called? | 501 | * Was this inode dirtied after sync_sb_inodes was called? |
496 | * This keeps sync from extra jobs and livelock. | 502 | * This keeps sync from extra jobs and livelock. |
@@ -498,7 +504,6 @@ static int writeback_sb_inodes(struct super_block *sb, struct bdi_writeback *wb, | |||
498 | if (inode_dirtied_after(inode, wbc->wb_start)) | 504 | if (inode_dirtied_after(inode, wbc->wb_start)) |
499 | return 1; | 505 | return 1; |
500 | 506 | ||
501 | BUG_ON(inode->i_state & I_FREEING); | ||
502 | __iget(inode); | 507 | __iget(inode); |
503 | pages_skipped = wbc->pages_skipped; | 508 | pages_skipped = wbc->pages_skipped; |
504 | writeback_single_inode(inode, wbc); | 509 | writeback_single_inode(inode, wbc); |
@@ -536,8 +541,7 @@ void writeback_inodes_wb(struct bdi_writeback *wb, | |||
536 | queue_io(wb, wbc->older_than_this); | 541 | queue_io(wb, wbc->older_than_this); |
537 | 542 | ||
538 | while (!list_empty(&wb->b_io)) { | 543 | while (!list_empty(&wb->b_io)) { |
539 | struct inode *inode = list_entry(wb->b_io.prev, | 544 | struct inode *inode = wb_inode(wb->b_io.prev); |
540 | struct inode, i_list); | ||
541 | struct super_block *sb = inode->i_sb; | 545 | struct super_block *sb = inode->i_sb; |
542 | 546 | ||
543 | if (!pin_sb_for_writeback(sb)) { | 547 | if (!pin_sb_for_writeback(sb)) { |
@@ -675,8 +679,7 @@ static long wb_writeback(struct bdi_writeback *wb, | |||
675 | */ | 679 | */ |
676 | spin_lock(&inode_lock); | 680 | spin_lock(&inode_lock); |
677 | if (!list_empty(&wb->b_more_io)) { | 681 | if (!list_empty(&wb->b_more_io)) { |
678 | inode = list_entry(wb->b_more_io.prev, | 682 | inode = wb_inode(wb->b_more_io.prev); |
679 | struct inode, i_list); | ||
680 | trace_wbc_writeback_wait(&wbc, wb->bdi); | 683 | trace_wbc_writeback_wait(&wbc, wb->bdi); |
681 | inode_wait_for_writeback(inode); | 684 | inode_wait_for_writeback(inode); |
682 | } | 685 | } |
@@ -727,7 +730,7 @@ static long wb_check_old_data_flush(struct bdi_writeback *wb) | |||
727 | */ | 730 | */ |
728 | nr_pages = global_page_state(NR_FILE_DIRTY) + | 731 | nr_pages = global_page_state(NR_FILE_DIRTY) + |
729 | global_page_state(NR_UNSTABLE_NFS) + | 732 | global_page_state(NR_UNSTABLE_NFS) + |
730 | (inodes_stat.nr_inodes - inodes_stat.nr_unused); | 733 | get_nr_dirty_inodes(); |
731 | 734 | ||
732 | if (nr_pages) { | 735 | if (nr_pages) { |
733 | struct wb_writeback_work work = { | 736 | struct wb_writeback_work work = { |
@@ -966,7 +969,7 @@ void __mark_inode_dirty(struct inode *inode, int flags) | |||
966 | * dirty list. Add blockdev inodes as well. | 969 | * dirty list. Add blockdev inodes as well. |
967 | */ | 970 | */ |
968 | if (!S_ISBLK(inode->i_mode)) { | 971 | if (!S_ISBLK(inode->i_mode)) { |
969 | if (hlist_unhashed(&inode->i_hash)) | 972 | if (inode_unhashed(inode)) |
970 | goto out; | 973 | goto out; |
971 | } | 974 | } |
972 | if (inode->i_state & I_FREEING) | 975 | if (inode->i_state & I_FREEING) |
@@ -994,7 +997,7 @@ void __mark_inode_dirty(struct inode *inode, int flags) | |||
994 | } | 997 | } |
995 | 998 | ||
996 | inode->dirtied_when = jiffies; | 999 | inode->dirtied_when = jiffies; |
997 | list_move(&inode->i_list, &bdi->wb.b_dirty); | 1000 | list_move(&inode->i_wb_list, &bdi->wb.b_dirty); |
998 | } | 1001 | } |
999 | } | 1002 | } |
1000 | out: | 1003 | out: |
@@ -1094,8 +1097,7 @@ void writeback_inodes_sb(struct super_block *sb) | |||
1094 | 1097 | ||
1095 | WARN_ON(!rwsem_is_locked(&sb->s_umount)); | 1098 | WARN_ON(!rwsem_is_locked(&sb->s_umount)); |
1096 | 1099 | ||
1097 | work.nr_pages = nr_dirty + nr_unstable + | 1100 | work.nr_pages = nr_dirty + nr_unstable + get_nr_dirty_inodes(); |
1098 | (inodes_stat.nr_inodes - inodes_stat.nr_unused); | ||
1099 | 1101 | ||
1100 | bdi_queue_work(sb->s_bdi, &work); | 1102 | bdi_queue_work(sb->s_bdi, &work); |
1101 | wait_for_completion(&done); | 1103 | wait_for_completion(&done); |
@@ -1202,3 +1204,23 @@ int sync_inode(struct inode *inode, struct writeback_control *wbc) | |||
1202 | return ret; | 1204 | return ret; |
1203 | } | 1205 | } |
1204 | EXPORT_SYMBOL(sync_inode); | 1206 | EXPORT_SYMBOL(sync_inode); |
1207 | |||
1208 | /** | ||
1209 | * sync_inode - write an inode to disk | ||
1210 | * @inode: the inode to sync | ||
1211 | * @wait: wait for I/O to complete. | ||
1212 | * | ||
1213 | * Write an inode to disk and adjust it's dirty state after completion. | ||
1214 | * | ||
1215 | * Note: only writes the actual inode, no associated data or other metadata. | ||
1216 | */ | ||
1217 | int sync_inode_metadata(struct inode *inode, int wait) | ||
1218 | { | ||
1219 | struct writeback_control wbc = { | ||
1220 | .sync_mode = wait ? WB_SYNC_ALL : WB_SYNC_NONE, | ||
1221 | .nr_to_write = 0, /* metadata-only */ | ||
1222 | }; | ||
1223 | |||
1224 | return sync_inode(inode, &wbc); | ||
1225 | } | ||
1226 | EXPORT_SYMBOL(sync_inode_metadata); | ||