Re: [PATCH] Fix JBD race in t_forget list handling

2005-07-11 Thread Stephen C. Tweedie
Hi,

On Mon, 2005-07-11 at 16:34, Jan Kara wrote:

>   attached patch should close the possible race between
> journal_commit_transaction() and journal_unmap_buffer() (which adds
> buffers to committing transaction's t_forget list) that could leave
> some buffers on transaction's t_forget list (hence leading to an
> assertion failure later when transaction is dropped). The patch is
> against 2.6.13-rc2 kernel.  The race was really happening to David Wilk
> <[EMAIL PROTECTED]> (thanks for testing) so please apply if you find
> the patch correct.

Indeed we can be adding to the committing transaction's t_forget list
here.  The fix looks correct: we're taking the j_list_lock anyway in
that loop for the checkpoint processing, so we're not taking it any more
frequently by grabbing it for the loop condition itself too --- just
holding it a little longer.

--Stephen

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


[PATCH] Fix JBD race in t_forget list handling

2005-07-11 Thread Jan Kara
  Hello all,

  attached patch should close the possible race between
journal_commit_transaction() and journal_unmap_buffer() (which adds
buffers to committing transaction's t_forget list) that could leave
some buffers on transaction's t_forget list (hence leading to an
assertion failure later when transaction is dropped). The patch is
against 2.6.13-rc2 kernel.  The race was really happening to David Wilk
<[EMAIL PROTECTED]> (thanks for testing) so please apply if you find
the patch correct.

Honza
-- 
Jan Kara <[EMAIL PROTECTED]>
SuSE CR Labs
Fix race between journal_commit_transaction() and other places as
journal_unmap_buffer() that are adding buffers to transaction's t_forget
list. We have to protect against such places by holding j_list_lock even when
traversing the t_forget list. The fact that other places can only add buffers
to the list makes the locking easier. OTOH the lock ranking complicates
the stuff...

Signed-off-by: Jan Kara <[EMAIL PROTECTED]>

diff -rup -x*.o -x.* linux-2.6.13-rc2/fs/jbd/commit.c 
linux-2.6.13-rc2-1-forgetfix/fs/jbd/commit.c
--- linux-2.6.13-rc2/fs/jbd/commit.cFri Jun 24 16:27:10 2005
+++ linux-2.6.13-rc2-1-forgetfix/fs/jbd/commit.cMon Jul 11 17:20:48 2005
@@ -720,11 +720,17 @@ wait_for_iobuf:
J_ASSERT(commit_transaction->t_log_list == NULL);
 
 restart_loop:
+   /*
+* As there are other places (journal_unmap_buffer()) adding buffers
+* to this list we have to be careful and hold the j_list_lock.
+*/
+   spin_lock(>j_list_lock);
while (commit_transaction->t_forget) {
transaction_t *cp_transaction;
struct buffer_head *bh;
 
jh = commit_transaction->t_forget;
+   spin_unlock(>j_list_lock);
bh = jh2bh(jh);
jbd_lock_bh_state(bh);
J_ASSERT_JH(jh, jh->b_transaction == commit_transaction ||
@@ -792,9 +798,25 @@ restart_loop:
journal_remove_journal_head(bh);  /* needs a brelse */
release_buffer_page(bh);
}
+   cond_resched_lock(>j_list_lock);
+   }
+   spin_unlock(>j_list_lock);
+   /*
+* This is a bit sleazy.  We borrow j_list_lock to protect
+* journal->j_committing_transaction in __journal_remove_checkpoint.
+* Really, __journal_remove_checkpoint should be using j_state_lock but
+* it's a bit hassle to hold that across __journal_remove_checkpoint
+*/
+   spin_lock(>j_state_lock);
+   spin_lock(>j_list_lock);
+   /*
+* Now recheck if some buffers did not get attached to the transaction
+* while the lock was dropped...
+*/
+   if (commit_transaction->t_forget) {
spin_unlock(>j_list_lock);
-   if (cond_resched())
-   goto restart_loop;
+   spin_unlock(>j_state_lock);
+   goto restart_loop;
}
 
/* Done with this transaction! */
@@ -803,14 +825,6 @@ restart_loop:
 
J_ASSERT(commit_transaction->t_state == T_COMMIT);
 
-   /*
-* This is a bit sleazy.  We borrow j_list_lock to protect
-* journal->j_committing_transaction in __journal_remove_checkpoint.
-* Really, __jornal_remove_checkpoint should be using j_state_lock but
-* it's a bit hassle to hold that across __journal_remove_checkpoint
-*/
-   spin_lock(>j_state_lock);
-   spin_lock(>j_list_lock);
commit_transaction->t_state = T_FINISHED;
J_ASSERT(commit_transaction == journal->j_committing_transaction);
journal->j_commit_sequence = commit_transaction->t_tid;


[PATCH] Fix JBD race in t_forget list handling

2005-07-11 Thread Jan Kara
  Hello all,

  attached patch should close the possible race between
journal_commit_transaction() and journal_unmap_buffer() (which adds
buffers to committing transaction's t_forget list) that could leave
some buffers on transaction's t_forget list (hence leading to an
assertion failure later when transaction is dropped). The patch is
against 2.6.13-rc2 kernel.  The race was really happening to David Wilk
[EMAIL PROTECTED] (thanks for testing) so please apply if you find
the patch correct.

Honza
-- 
Jan Kara [EMAIL PROTECTED]
SuSE CR Labs
Fix race between journal_commit_transaction() and other places as
journal_unmap_buffer() that are adding buffers to transaction's t_forget
list. We have to protect against such places by holding j_list_lock even when
traversing the t_forget list. The fact that other places can only add buffers
to the list makes the locking easier. OTOH the lock ranking complicates
the stuff...

Signed-off-by: Jan Kara [EMAIL PROTECTED]

diff -rup -x*.o -x.* linux-2.6.13-rc2/fs/jbd/commit.c 
linux-2.6.13-rc2-1-forgetfix/fs/jbd/commit.c
--- linux-2.6.13-rc2/fs/jbd/commit.cFri Jun 24 16:27:10 2005
+++ linux-2.6.13-rc2-1-forgetfix/fs/jbd/commit.cMon Jul 11 17:20:48 2005
@@ -720,11 +720,17 @@ wait_for_iobuf:
J_ASSERT(commit_transaction-t_log_list == NULL);
 
 restart_loop:
+   /*
+* As there are other places (journal_unmap_buffer()) adding buffers
+* to this list we have to be careful and hold the j_list_lock.
+*/
+   spin_lock(journal-j_list_lock);
while (commit_transaction-t_forget) {
transaction_t *cp_transaction;
struct buffer_head *bh;
 
jh = commit_transaction-t_forget;
+   spin_unlock(journal-j_list_lock);
bh = jh2bh(jh);
jbd_lock_bh_state(bh);
J_ASSERT_JH(jh, jh-b_transaction == commit_transaction ||
@@ -792,9 +798,25 @@ restart_loop:
journal_remove_journal_head(bh);  /* needs a brelse */
release_buffer_page(bh);
}
+   cond_resched_lock(journal-j_list_lock);
+   }
+   spin_unlock(journal-j_list_lock);
+   /*
+* This is a bit sleazy.  We borrow j_list_lock to protect
+* journal-j_committing_transaction in __journal_remove_checkpoint.
+* Really, __journal_remove_checkpoint should be using j_state_lock but
+* it's a bit hassle to hold that across __journal_remove_checkpoint
+*/
+   spin_lock(journal-j_state_lock);
+   spin_lock(journal-j_list_lock);
+   /*
+* Now recheck if some buffers did not get attached to the transaction
+* while the lock was dropped...
+*/
+   if (commit_transaction-t_forget) {
spin_unlock(journal-j_list_lock);
-   if (cond_resched())
-   goto restart_loop;
+   spin_unlock(journal-j_state_lock);
+   goto restart_loop;
}
 
/* Done with this transaction! */
@@ -803,14 +825,6 @@ restart_loop:
 
J_ASSERT(commit_transaction-t_state == T_COMMIT);
 
-   /*
-* This is a bit sleazy.  We borrow j_list_lock to protect
-* journal-j_committing_transaction in __journal_remove_checkpoint.
-* Really, __jornal_remove_checkpoint should be using j_state_lock but
-* it's a bit hassle to hold that across __journal_remove_checkpoint
-*/
-   spin_lock(journal-j_state_lock);
-   spin_lock(journal-j_list_lock);
commit_transaction-t_state = T_FINISHED;
J_ASSERT(commit_transaction == journal-j_committing_transaction);
journal-j_commit_sequence = commit_transaction-t_tid;


Re: [PATCH] Fix JBD race in t_forget list handling

2005-07-11 Thread Stephen C. Tweedie
Hi,

On Mon, 2005-07-11 at 16:34, Jan Kara wrote:

   attached patch should close the possible race between
 journal_commit_transaction() and journal_unmap_buffer() (which adds
 buffers to committing transaction's t_forget list) that could leave
 some buffers on transaction's t_forget list (hence leading to an
 assertion failure later when transaction is dropped). The patch is
 against 2.6.13-rc2 kernel.  The race was really happening to David Wilk
 [EMAIL PROTECTED] (thanks for testing) so please apply if you find
 the patch correct.

Indeed we can be adding to the committing transaction's t_forget list
here.  The fix looks correct: we're taking the j_list_lock anyway in
that loop for the checkpoint processing, so we're not taking it any more
frequently by grabbing it for the loop condition itself too --- just
holding it a little longer.

--Stephen

-
To unsubscribe from this list: send the line unsubscribe linux-kernel in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/