diff options
author | Jan Kara <jack@suse.cz> | 2011-06-27 19:18:10 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-06-27 21:00:13 -0400 |
commit | 08142579b6ca35883c1ed066a2681de6f6917062 (patch) | |
tree | 00735ed37753533f3b645714770b4fb036b5f7e0 /mm | |
parent | 9b679320a5fbf46454011e5c62e0b8991b0956d1 (diff) |
mm: fix assertion mapping->nrpages == 0 in end_writeback()
Under heavy memory and filesystem load, users observe the assertion
mapping->nrpages == 0 in end_writeback() trigger. This can be caused by
page reclaim reclaiming the last page from a mapping in the following
race:
CPU0 CPU1
...
shrink_page_list()
__remove_mapping()
__delete_from_page_cache()
radix_tree_delete()
evict_inode()
truncate_inode_pages()
truncate_inode_pages_range()
pagevec_lookup() - finds nothing
end_writeback()
mapping->nrpages != 0 -> BUG
page->mapping = NULL
mapping->nrpages--
Fix the problem by doing a reliable check of mapping->nrpages under
mapping->tree_lock in end_writeback().
Analyzed by Jay <jinshan.xiong@whamcloud.com>, lost in LKML, and dug out
by Miklos Szeredi <mszeredi@suse.de>.
Cc: Jay <jinshan.xiong@whamcloud.com>
Cc: Miklos Szeredi <mszeredi@suse.de>
Signed-off-by: Jan Kara <jack@suse.cz>
Cc: <stable@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/truncate.c | 5 |
1 files changed, 5 insertions, 0 deletions
diff --git a/mm/truncate.c b/mm/truncate.c index 29a9b8a5a31a..e13f22efaad7 100644 --- a/mm/truncate.c +++ b/mm/truncate.c | |||
@@ -304,6 +304,11 @@ EXPORT_SYMBOL(truncate_inode_pages_range); | |||
304 | * @lstart: offset from which to truncate | 304 | * @lstart: offset from which to truncate |
305 | * | 305 | * |
306 | * Called under (and serialised by) inode->i_mutex. | 306 | * Called under (and serialised by) inode->i_mutex. |
307 | * | ||
308 | * Note: When this function returns, there can be a page in the process of | ||
309 | * deletion (inside __delete_from_page_cache()) in the specified range. Thus | ||
310 | * mapping->nrpages can be non-zero when this function returns even after | ||
311 | * truncation of the whole mapping. | ||
307 | */ | 312 | */ |
308 | void truncate_inode_pages(struct address_space *mapping, loff_t lstart) | 313 | void truncate_inode_pages(struct address_space *mapping, loff_t lstart) |
309 | { | 314 | { |