aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorAndrew Morton <akpm@osdl.org>2006-10-01 02:29:29 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-10-01 03:39:33 -0400
commitbd4c8ce41a2e2f0c5bf54343ab54e8e09faec021 (patch)
treee8a7d3cfafbe6ee35672953718ba4223e450d938 /mm
parentd025c9db7f31fc0554ce7fb2dfc78d35a77f3487 (diff)
[PATCH] invalidate_inode_pages2(): ignore page refcounts
The recent fix to invalidate_inode_pages() (git commit 016eb4a) managed to unfix invalidate_inode_pages2(). The problem is that various bits of code in the kernel can take transient refs on pages: the page scanner will do this when inspecting a batch of pages, and the lru_cache_add() batching pagevecs also hold a ref. Net result is transient failures in invalidate_inode_pages2(). This affects NFS directory invalidation (observed) and presumably also block-backed direct-io (not yet reported). Fix it by reverting invalidate_inode_pages2() back to the old version which ignores the page refcounts. We may come up with something more clever later, but for now we need a 2.6.18 fix for NFS. Cc: Chuck Lever <cel@citi.umich.edu> Cc: Nick Piggin <nickpiggin@yahoo.com.au> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: <stable@kernel.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/truncate.c34
1 files changed, 32 insertions, 2 deletions
diff --git a/mm/truncate.c b/mm/truncate.c
index 8fde6580657e..f4edbc179d14 100644
--- a/mm/truncate.c
+++ b/mm/truncate.c
@@ -287,9 +287,39 @@ unsigned long invalidate_inode_pages(struct address_space *mapping)
287{ 287{
288 return invalidate_mapping_pages(mapping, 0, ~0UL); 288 return invalidate_mapping_pages(mapping, 0, ~0UL);
289} 289}
290
291EXPORT_SYMBOL(invalidate_inode_pages); 290EXPORT_SYMBOL(invalidate_inode_pages);
292 291
292/*
293 * This is like invalidate_complete_page(), except it ignores the page's
294 * refcount. We do this because invalidate_inode_pages2() needs stronger
295 * invalidation guarantees, and cannot afford to leave pages behind because
296 * shrink_list() has a temp ref on them, or because they're transiently sitting
297 * in the lru_cache_add() pagevecs.
298 */
299static int
300invalidate_complete_page2(struct address_space *mapping, struct page *page)
301{
302 if (page->mapping != mapping)
303 return 0;
304
305 if (PagePrivate(page) && !try_to_release_page(page, 0))
306 return 0;
307
308 write_lock_irq(&mapping->tree_lock);
309 if (PageDirty(page))
310 goto failed;
311
312 BUG_ON(PagePrivate(page));
313 __remove_from_page_cache(page);
314 write_unlock_irq(&mapping->tree_lock);
315 ClearPageUptodate(page);
316 page_cache_release(page); /* pagecache ref */
317 return 1;
318failed:
319 write_unlock_irq(&mapping->tree_lock);
320 return 0;
321}
322
293/** 323/**
294 * invalidate_inode_pages2_range - remove range of pages from an address_space 324 * invalidate_inode_pages2_range - remove range of pages from an address_space
295 * @mapping: the address_space 325 * @mapping: the address_space
@@ -356,7 +386,7 @@ int invalidate_inode_pages2_range(struct address_space *mapping,
356 } 386 }
357 } 387 }
358 was_dirty = test_clear_page_dirty(page); 388 was_dirty = test_clear_page_dirty(page);
359 if (!invalidate_complete_page(mapping, page)) { 389 if (!invalidate_complete_page2(mapping, page)) {
360 if (was_dirty) 390 if (was_dirty)
361 set_page_dirty(page); 391 set_page_dirty(page);
362 ret = -EIO; 392 ret = -EIO;