diff options
author | Andrew Morton <akpm@osdl.org> | 2006-12-21 14:00:33 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.osdl.org> | 2006-12-21 14:17:26 -0500 |
commit | 3e67c0987d7567ad666641164a153dca9a43b11d (patch) | |
tree | e8bd583fc8fe6e9e2e8533319a30577c0081f70e | |
parent | 921320210bd2ec4f17053d283355b73048ac0e56 (diff) |
[PATCH] truncate: clear page dirtiness before running try_to_free_buffers()
truncate presently invalidates the dirty page's buffer_heads then shoots down
the page. But try_to_free_buffers() will now bale out because the page is
dirty.
Net effect: the LRU gets filled with dirty pages which have invalidated
buffer_heads attached. They have no ->mapping and hence cannot be cleaned.
The machine leaks memory at an enormous rate.
Fix this by cleaning the page before running try_to_free_buffers(), so
try_to_free_buffers() can do its work.
Also, remember to do dirty-page-acoounting in cancel_dirty_page() so the
machine won't wedge up trying to write non-existent dirty pages.
Probably still wrong, but now less so.
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | mm/truncate.c | 9 |
1 files changed, 5 insertions, 4 deletions
diff --git a/mm/truncate.c b/mm/truncate.c index bf9e2965d666..89a5c359b6e8 100644 --- a/mm/truncate.c +++ b/mm/truncate.c | |||
@@ -60,11 +60,12 @@ void cancel_dirty_page(struct page *page, unsigned int account_size) | |||
60 | WARN_ON(++warncount < 5); | 60 | WARN_ON(++warncount < 5); |
61 | } | 61 | } |
62 | 62 | ||
63 | if (TestClearPageDirty(page) && account_size) | 63 | if (TestClearPageDirty(page) && account_size) { |
64 | dec_zone_page_state(page, NR_FILE_DIRTY); | ||
64 | task_io_account_cancelled_write(account_size); | 65 | task_io_account_cancelled_write(account_size); |
66 | } | ||
65 | } | 67 | } |
66 | 68 | ||
67 | |||
68 | /* | 69 | /* |
69 | * If truncate cannot remove the fs-private metadata from the page, the page | 70 | * If truncate cannot remove the fs-private metadata from the page, the page |
70 | * becomes anonymous. It will be left on the LRU and may even be mapped into | 71 | * becomes anonymous. It will be left on the LRU and may even be mapped into |
@@ -81,11 +82,11 @@ truncate_complete_page(struct address_space *mapping, struct page *page) | |||
81 | if (page->mapping != mapping) | 82 | if (page->mapping != mapping) |
82 | return; | 83 | return; |
83 | 84 | ||
85 | cancel_dirty_page(page, PAGE_CACHE_SIZE); | ||
86 | |||
84 | if (PagePrivate(page)) | 87 | if (PagePrivate(page)) |
85 | do_invalidatepage(page, 0); | 88 | do_invalidatepage(page, 0); |
86 | 89 | ||
87 | cancel_dirty_page(page, PAGE_CACHE_SIZE); | ||
88 | |||
89 | ClearPageUptodate(page); | 90 | ClearPageUptodate(page); |
90 | ClearPageMappedToDisk(page); | 91 | ClearPageMappedToDisk(page); |
91 | remove_from_page_cache(page); | 92 | remove_from_page_cache(page); |