 fs/jbd/journal.c     |   17 +++++++++++++++++
 fs/jbd/transaction.c |   31 ++++++++++++++++---------------
 include/linux/jbd.h  |    8 ++++----
 3 files changed, 37 insertions(+), 19 deletions(-)

diff -puN fs/jbd/transaction.c~ext3-035-journal_try_to_free_buffers-race-fix fs/jbd/transaction.c
--- 25/fs/jbd/transaction.c~ext3-035-journal_try_to_free_buffers-race-fix	2003-06-02 22:39:03.000000000 -0700
+++ 25-akpm/fs/jbd/transaction.c	2003-06-02 22:39:03.000000000 -0700
@@ -1553,10 +1553,8 @@ void journal_unfile_buffer(journal_t *jo
  * Called from journal_try_to_free_buffers().
  *
  * Called under jbd_lock_bh_state(bh)
- *
- * Returns non-zero iff we were able to free the journal_head.
  */
-static inline int
+static void
 __journal_try_to_free_buffer(journal_t *journal, struct buffer_head *bh)
 {
 	struct journal_head *jh;
@@ -1589,10 +1587,8 @@ __journal_try_to_free_buffer(journal_t *
 		}
 	}
 	spin_unlock(&journal->j_list_lock);
-	return !buffer_jbd(bh);
-
 out:
-	return 0;
+	return;
 }
 
 
@@ -1642,18 +1638,23 @@ int journal_try_to_free_buffers(journal_
 	head = page_buffers(page);
 	bh = head;
 	do {
-		jbd_lock_bh_state(bh);
+		struct journal_head *jh;
+
 		/*
-		 * We don't have to worry about the buffer being pulled off its
-		 * journal_head in here, because __try_to_free_cp_buf runs
-		 * under jbd_lock_bh_state()
+		 * We take our own ref against the journal_head here to avoid
+		 * having to add tons of locking around each instance of
+		 * journal_remove_journal_head() and journal_put_journal_head().
 		 */
-		if (buffer_jbd(bh) &&
-				!__journal_try_to_free_buffer(journal, bh)) {
-			jbd_unlock_bh_state(bh);
-			goto busy;
-		}
+		jh = journal_grab_journal_head(bh);
+		if (!jh)
+			continue;
+
+		jbd_lock_bh_state(bh);
+		__journal_try_to_free_buffer(journal, bh);
+		journal_put_journal_head(jh);
 		jbd_unlock_bh_state(bh);
+		if (buffer_jbd(bh))
+			goto busy;
 	} while ((bh = bh->b_this_page) != head);
 	ret = try_to_free_buffers(page);
 busy:
diff -puN fs/jbd/journal.c~ext3-035-journal_try_to_free_buffers-race-fix fs/jbd/journal.c
--- 25/fs/jbd/journal.c~ext3-035-journal_try_to_free_buffers-race-fix	2003-06-02 22:39:03.000000000 -0700
+++ 25-akpm/fs/jbd/journal.c	2003-06-02 22:39:03.000000000 -0700
@@ -1679,6 +1679,23 @@ repeat:
 	return bh->b_private;
 }
 
+/*
+ * Grab a ref against this buffer_head's journal_head.  If it ended up not
+ * having a journal_head, return NULL
+ */
+struct journal_head *journal_grab_journal_head(struct buffer_head *bh)
+{
+	struct journal_head *jh = NULL;
+
+	jbd_lock_bh_journal_head(bh);
+	if (buffer_jbd(bh)) {
+		jh = bh2jh(bh);
+		jh->b_jcount++;
+	}
+	jbd_unlock_bh_journal_head(bh);
+	return jh;
+}
+
 static void __journal_remove_journal_head(struct buffer_head *bh)
 {
 	struct journal_head *jh = bh2jh(bh);
diff -puN include/linux/jbd.h~ext3-035-journal_try_to_free_buffers-race-fix include/linux/jbd.h
--- 25/include/linux/jbd.h~ext3-035-journal_try_to_free_buffers-race-fix	2003-06-02 22:39:03.000000000 -0700
+++ 25-akpm/include/linux/jbd.h	2003-06-02 22:39:03.000000000 -0700
@@ -943,10 +943,10 @@ extern int	   journal_force_commit(journ
 /*
  * journal_head management
  */
-extern struct journal_head
-		*journal_add_journal_head(struct buffer_head *bh);
-extern void	journal_remove_journal_head(struct buffer_head *bh);
-extern void	journal_put_journal_head(struct journal_head *jh);
+struct journal_head *journal_add_journal_head(struct buffer_head *bh);
+struct journal_head *journal_grab_journal_head(struct buffer_head *bh);
+void journal_remove_journal_head(struct buffer_head *bh);
+void journal_put_journal_head(struct journal_head *jh);
 
 /*
  * handle management

_
