patch-2.4.25 linux-2.4.25/fs/jbd/commit.c

Next file: linux-2.4.25/fs/jbd/journal.c
Previous file: linux-2.4.25/fs/intermezzo/file.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.24/fs/jbd/commit.c linux-2.4.25/fs/jbd/commit.c
@@ -19,6 +19,8 @@
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
 #include <linux/smp_lock.h>
 
 extern spinlock_t journal_datalist_lock;
@@ -34,6 +36,49 @@
 }
 
 /*
+ * 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 (TryLockPage(page))
+		goto nope;
+
+	page_cache_get(page);
+	__brelse(bh);
+	try_to_free_buffers(page, GFP_NOIO);
+	unlock_page(page);
+	page_cache_release(page);
+	return;
+
+nope:
+	__brelse(bh);
+}
+
+/*
  * journal_commit_transaction
  *
  * The primary function for committing a transaction to the log.  This
@@ -211,7 +256,7 @@
 				jh->b_transaction = NULL;
 				__journal_remove_journal_head(bh);
 				refile_buffer(bh);
-				__brelse(bh);
+				release_buffer_page(bh);
 			}
 		}
 		if (bufs == ARRAY_SIZE(wbuf)) {
@@ -648,7 +693,8 @@
 	while (commit_transaction->t_forget) {
 		transaction_t *cp_transaction;
 		struct buffer_head *bh;
-
+		int was_freed = 0;
+		
 		jh = commit_transaction->t_forget;
 		J_ASSERT_JH(jh,	jh->b_transaction == commit_transaction ||
 			jh->b_transaction == journal->j_running_transaction);
@@ -699,6 +745,7 @@
 		 * behind for writeback and gets reallocated for another
 		 * use in a different page. */
 		if (__buffer_state(bh, Freed)) {
+			was_freed = 1;
 			clear_bit(BH_Freed, &bh->b_state);
 			clear_bit(BH_JBDDirty, &bh->b_state);
 		}
@@ -714,7 +761,12 @@
 			__journal_unfile_buffer(jh);
 			jh->b_transaction = 0;
 			__journal_remove_journal_head(bh);
-			__brelse(bh);
+			spin_unlock(&journal_datalist_lock);
+			if (was_freed)
+				release_buffer_page(bh);
+			else
+				__brelse(bh);
+			continue;
 		}
 		spin_unlock(&journal_datalist_lock);
 	}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)