aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2005-10-16 20:36:06 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2005-10-16 20:36:06 -0400
commit3d80636a0d5f056ffc26472d05b6027a7a9f6e1c (patch)
tree39b3dbae53938728c55b2312190561b858431d92 /mm
parent688ce17b8599abc548b406c00e4d18ae0dec954f (diff)
Fix memory ordering bug in page reclaim
As noticed by Nick Piggin, we need to make sure that we check the page count before we check for PageDirty, since the dirty check is only valid if the count implies that we're the only possible ones holding the page. We always did do this, but the code needs a read-memory-barrier to make sure that the orderign is also honored by the CPU. (The writer side is ordered due to the atomic decrement and test on the page count, see the discussion on linux-kernel) Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/vmscan.c13
1 files changed, 9 insertions, 4 deletions
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 0ea71e887bb6..64f9570cff56 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -511,10 +511,11 @@ static int shrink_list(struct list_head *page_list, struct scan_control *sc)
511 * PageDirty _after_ making sure that the page is freeable and 511 * PageDirty _after_ making sure that the page is freeable and
512 * not in use by anybody. (pagecache + us == 2) 512 * not in use by anybody. (pagecache + us == 2)
513 */ 513 */
514 if (page_count(page) != 2 || PageDirty(page)) { 514 if (unlikely(page_count(page) != 2))
515 write_unlock_irq(&mapping->tree_lock); 515 goto cannot_free;
516 goto keep_locked; 516 smp_rmb();
517 } 517 if (unlikely(PageDirty(page)))
518 goto cannot_free;
518 519
519#ifdef CONFIG_SWAP 520#ifdef CONFIG_SWAP
520 if (PageSwapCache(page)) { 521 if (PageSwapCache(page)) {
@@ -538,6 +539,10 @@ free_it:
538 __pagevec_release_nonlru(&freed_pvec); 539 __pagevec_release_nonlru(&freed_pvec);
539 continue; 540 continue;
540 541
542cannot_free:
543 write_unlock_irq(&mapping->tree_lock);
544 goto keep_locked;
545
541activate_locked: 546activate_locked:
542 SetPageActive(page); 547 SetPageActive(page);
543 pgactivate++; 548 pgactivate++;