Commit 97c8087c authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] ext3: explicitly free truncated pages

With data=ordered it is often the case that a quick write-and-truncate will
leave large numbers of pages on the page LRU with no ->mapping, and attached
buffers.  Because ext3 was not ready to let the pages go at the time of
truncation.

These pages are trivially reclaimable, but their seeming absence makes the VM
overcommit accounting confused (they don't count as "free", nor as
pagecache).  And they make the /proc/meminfo stats look odd.

So what we do here is to try to strip the buffers from these pages as the
buffers exit the journal commit.
parent 6e1b8d42
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
#include <linux/jbd.h> #include <linux/jbd.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
/* /*
...@@ -33,6 +35,49 @@ static void journal_end_buffer_io_sync(struct buffer_head *bh, int uptodate) ...@@ -33,6 +35,49 @@ static void journal_end_buffer_io_sync(struct buffer_head *bh, int uptodate)
unlock_buffer(bh); unlock_buffer(bh);
} }
/*
* When an ext3-ordered file is truncated, it is possible that many pages are
* not sucessfully freed, because they are attached to a committing transaction.
* After the transaction commits, these pages are left on the LRU, with no
* ->mapping, and with attached buffers. These pages are trivially reclaimable
* by the VM, but their apparent absence upsets the VM accounting, and it makes
* the numbers in /proc/meminfo look odd.
*
* So here, we have a buffer which has just come off the forget list. Look to
* see if we can strip all buffers from the backing page.
*
* Called under lock_journal(), and possibly under journal_datalist_lock. The
* caller provided us with a ref against the buffer, and we drop that here.
*/
static void release_buffer_page(struct buffer_head *bh)
{
struct page *page;
if (buffer_dirty(bh))
goto nope;
if (atomic_read(&bh->b_count) != 1)
goto nope;
page = bh->b_page;
if (!page)
goto nope;
if (page->mapping)
goto nope;
/* OK, it's a truncated page */
if (TestSetPageLocked(page))
goto nope;
page_cache_get(page);
__brelse(bh);
try_to_free_buffers(page);
unlock_page(page);
page_cache_release(page);
return;
nope:
__brelse(bh);
}
/* /*
* journal_commit_transaction * journal_commit_transaction
* *
...@@ -683,7 +728,8 @@ void journal_commit_transaction(journal_t *journal) ...@@ -683,7 +728,8 @@ void journal_commit_transaction(journal_t *journal)
jh->b_transaction = 0; jh->b_transaction = 0;
jbd_unlock_bh_state(bh); jbd_unlock_bh_state(bh);
journal_remove_journal_head(bh); journal_remove_journal_head(bh);
__brelse(bh); if (buffer_freed(bh))
release_buffer_page(bh);
} }
spin_unlock(&journal->j_list_lock); spin_unlock(&journal->j_list_lock);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment