

The logic in there can leak transactions if a race happens.  And I think it
can cause j_running_transaction and j_committing_transaction to point at the
same thing.  Fix that up.


 fs/jbd/transaction.c |   17 +++++++++++++----
 1 files changed, 13 insertions(+), 4 deletions(-)

diff -puN fs/jbd/transaction.c~jbd-560-transaction-leak-fix fs/jbd/transaction.c
--- 25/fs/jbd/transaction.c~jbd-560-transaction-leak-fix	2003-05-26 21:20:48.000000000 -0700
+++ 25-akpm/fs/jbd/transaction.c	2003-05-26 21:24:14.000000000 -0700
@@ -87,20 +87,24 @@ static int start_this_handle(journal_t *
 	int needed;
 	int nblocks = handle->h_buffer_credits;
 	transaction_t *new_transaction = NULL;
+	int ret;
 
 	if (nblocks > journal->j_max_transaction_buffers) {
 		printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n",
 		       current->comm, nblocks,
 		       journal->j_max_transaction_buffers);
-		return -ENOSPC;
+		ret = -ENOSPC;
+		goto out;
 	}
 
 alloc_transaction:
 	if (!journal->j_running_transaction) {
 		new_transaction = jbd_kmalloc(sizeof(*new_transaction),
 						GFP_NOFS);
-		if (!new_transaction)
-			return -ENOMEM;
+		if (!new_transaction) {
+			ret = -ENOMEM;
+			goto out;
+		}
 		memset(new_transaction, 0, sizeof(*new_transaction));
 	}
 
@@ -116,7 +120,8 @@ repeat:
 	if (is_journal_aborted(journal) ||
 	    (journal->j_errno != 0 && !(journal->j_flags & JFS_ACK_ERR))) {
 		spin_unlock(&journal->j_state_lock);
-		return -EROFS; 
+		ret = -EROFS; 
+		goto out;
 	}
 
 	/* Wait on the journal's transaction barrier if necessary */
@@ -134,6 +139,7 @@ repeat_locked:
 			goto alloc_transaction;
 		}
 		get_transaction(journal, new_transaction);
+		new_transaction = NULL;
 	}
 
 	/* @@@ Error? */
@@ -230,6 +236,9 @@ repeat_locked:
 		  __log_space_left(journal));
 	spin_unlock(&transaction->t_handle_lock);
 	spin_unlock(&journal->j_state_lock);
+out:
+	if (new_transaction)
+		kfree(new_transaction);
 	return 0;
 }
 

_
