diff options
author | Andrew Morton <akpm@osdl.org> | 2006-10-01 02:29:29 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-10-01 03:39:33 -0400 |
commit | bd4c8ce41a2e2f0c5bf54343ab54e8e09faec021 (patch) | |
tree | e8a7d3cfafbe6ee35672953718ba4223e450d938 | |
parent | d025c9db7f31fc0554ce7fb2dfc78d35a77f3487 (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>
-rw-r--r-- | mm/truncate.c | 34 |
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 | |||
291 | EXPORT_SYMBOL(invalidate_inode_pages); | 290 | EXPORT_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 | */ | ||
299 | static int | ||
300 | invalidate_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; | ||
318 | failed: | ||
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; |