Commit 1c69516f authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] JBD: fine-grain journal_add_journal_head locking

buffer_heads and journal_heads are joined at the hip.  We need a lock to
protect the joint and its refcounts.

JBD is currently using a global spinlock for that.  Change it to use one bit
in bh->b_state.
parent 6fe2ab38
...@@ -59,7 +59,7 @@ static int __try_to_free_cp_buf(struct journal_head *jh) ...@@ -59,7 +59,7 @@ static int __try_to_free_cp_buf(struct journal_head *jh)
if (jh->b_jlist == BJ_None && !buffer_locked(bh) && !buffer_dirty(bh)) { if (jh->b_jlist == BJ_None && !buffer_locked(bh) && !buffer_dirty(bh)) {
JBUFFER_TRACE(jh, "remove from checkpoint list"); JBUFFER_TRACE(jh, "remove from checkpoint list");
__journal_remove_checkpoint(jh); __journal_remove_checkpoint(jh);
__journal_remove_journal_head(bh); journal_remove_journal_head(bh);
BUFFER_TRACE(bh, "release"); BUFFER_TRACE(bh, "release");
__brelse(bh); __brelse(bh);
ret = 1; ret = 1;
...@@ -156,7 +156,7 @@ static int __cleanup_transaction(journal_t *journal, transaction_t *transaction) ...@@ -156,7 +156,7 @@ static int __cleanup_transaction(journal_t *journal, transaction_t *transaction)
if (!buffer_dirty(bh) && !buffer_jbddirty(bh)) { if (!buffer_dirty(bh) && !buffer_jbddirty(bh)) {
BUFFER_TRACE(bh, "remove from checkpoint"); BUFFER_TRACE(bh, "remove from checkpoint");
__journal_remove_checkpoint(jh); __journal_remove_checkpoint(jh);
__journal_remove_journal_head(bh); journal_remove_journal_head(bh);
__brelse(bh); __brelse(bh);
ret = 1; ret = 1;
} }
......
...@@ -212,7 +212,7 @@ void journal_commit_transaction(journal_t *journal) ...@@ -212,7 +212,7 @@ void journal_commit_transaction(journal_t *journal)
BUFFER_TRACE(bh, "writeout complete: unfile"); BUFFER_TRACE(bh, "writeout complete: unfile");
__journal_unfile_buffer(jh); __journal_unfile_buffer(jh);
jh->b_transaction = NULL; jh->b_transaction = NULL;
__journal_remove_journal_head(bh); journal_remove_journal_head(bh);
__brelse(bh); __brelse(bh);
} }
} }
...@@ -692,7 +692,7 @@ void journal_commit_transaction(journal_t *journal) ...@@ -692,7 +692,7 @@ void journal_commit_transaction(journal_t *journal)
J_ASSERT_JH(jh, jh->b_next_transaction == NULL); J_ASSERT_JH(jh, jh->b_next_transaction == NULL);
__journal_unfile_buffer(jh); __journal_unfile_buffer(jh);
jh->b_transaction = 0; jh->b_transaction = 0;
__journal_remove_journal_head(bh); journal_remove_journal_head(bh);
__brelse(bh); __brelse(bh);
} }
spin_unlock(&journal_datalist_lock); spin_unlock(&journal_datalist_lock);
......
...@@ -297,7 +297,7 @@ void __journal_clean_data_list(transaction_t *transaction) ...@@ -297,7 +297,7 @@ void __journal_clean_data_list(transaction_t *transaction)
BUFFER_TRACE(bh, "data writeout complete: unfile"); BUFFER_TRACE(bh, "data writeout complete: unfile");
__journal_unfile_buffer(jh); __journal_unfile_buffer(jh);
jh->b_transaction = NULL; jh->b_transaction = NULL;
__journal_remove_journal_head(bh); journal_remove_journal_head(bh);
__brelse(bh); __brelse(bh);
goto restart; goto restart;
} }
...@@ -1710,59 +1710,49 @@ static void journal_free_journal_head(struct journal_head *jh) ...@@ -1710,59 +1710,49 @@ static void journal_free_journal_head(struct journal_head *jh)
struct journal_head *journal_add_journal_head(struct buffer_head *bh) struct journal_head *journal_add_journal_head(struct buffer_head *bh)
{ {
struct journal_head *jh; struct journal_head *jh;
struct journal_head *new_jh = NULL;
spin_lock(&journal_datalist_lock); repeat:
if (!buffer_jbd(bh)) {
new_jh = journal_alloc_journal_head();
memset(new_jh, 0, sizeof(*new_jh));
}
jbd_lock_bh_journal_head(bh);
if (buffer_jbd(bh)) { if (buffer_jbd(bh)) {
jh = bh2jh(bh); jh = bh2jh(bh);
} else { } else {
J_ASSERT_BH(bh, J_ASSERT_BH(bh,
(atomic_read(&bh->b_count) > 0) || (atomic_read(&bh->b_count) > 0) ||
(bh->b_page && bh->b_page->mapping)); (bh->b_page && bh->b_page->mapping));
spin_unlock(&journal_datalist_lock);
jh = journal_alloc_journal_head();
memset(jh, 0, sizeof(*jh));
spin_lock(&journal_datalist_lock);
if (buffer_jbd(bh)) { if (!new_jh) {
/* Someone did it for us! */ jbd_unlock_bh_journal_head(bh);
J_ASSERT_BH(bh, bh->b_private != NULL); goto repeat;
journal_free_journal_head(jh);
jh = bh->b_private;
} else {
set_bit(BH_JBD, &bh->b_state);
bh->b_private = jh;
jh->b_bh = bh;
atomic_inc(&bh->b_count);
BUFFER_TRACE(bh, "added journal_head");
} }
jh = new_jh;
new_jh = NULL; /* We consumed it */
set_buffer_jbd(bh);
bh->b_private = jh;
jh->b_bh = bh;
get_bh(bh);
BUFFER_TRACE(bh, "added journal_head");
} }
jh->b_jcount++; jh->b_jcount++;
spin_unlock(&journal_datalist_lock); jbd_unlock_bh_journal_head(bh);
if (new_jh)
journal_free_journal_head(new_jh);
return bh->b_private; return bh->b_private;
} }
/* static void __journal_remove_journal_head(struct buffer_head *bh)
* journal_remove_journal_head(): if the buffer isn't attached to a transaction
* and has a zero b_jcount then remove and release its journal_head. If we did
* see that the buffer is not used by any transaction we also "logically"
* decrement ->b_count.
*
* We in fact take an additional increment on ->b_count as a convenience,
* because the caller usually wants to do additional things with the bh
* after calling here.
* The caller of journal_remove_journal_head() *must* run __brelse(bh) at some
* time. Once the caller has run __brelse(), the buffer is eligible for
* reaping by try_to_free_buffers().
*
* Requires journal_datalist_lock.
*/
void __journal_remove_journal_head(struct buffer_head *bh)
{ {
struct journal_head *jh = bh2jh(bh); struct journal_head *jh = bh2jh(bh);
assert_spin_locked(&journal_datalist_lock);
J_ASSERT_JH(jh, jh->b_jcount >= 0); J_ASSERT_JH(jh, jh->b_jcount >= 0);
atomic_inc(&bh->b_count);
get_bh(bh);
if (jh->b_jcount == 0) { if (jh->b_jcount == 0) {
if (jh->b_transaction == NULL && if (jh->b_transaction == NULL &&
jh->b_next_transaction == NULL && jh->b_next_transaction == NULL &&
...@@ -1772,7 +1762,7 @@ void __journal_remove_journal_head(struct buffer_head *bh) ...@@ -1772,7 +1762,7 @@ void __journal_remove_journal_head(struct buffer_head *bh)
BUFFER_TRACE(bh, "remove journal_head"); BUFFER_TRACE(bh, "remove journal_head");
bh->b_private = NULL; bh->b_private = NULL;
jh->b_bh = NULL; /* debug, really */ jh->b_bh = NULL; /* debug, really */
clear_bit(BH_JBD, &bh->b_state); clear_buffer_jbd(bh);
__brelse(bh); __brelse(bh);
journal_free_journal_head(jh); journal_free_journal_head(jh);
} else { } else {
...@@ -1781,26 +1771,38 @@ void __journal_remove_journal_head(struct buffer_head *bh) ...@@ -1781,26 +1771,38 @@ void __journal_remove_journal_head(struct buffer_head *bh)
} }
} }
/*
* journal_remove_journal_head(): if the buffer isn't attached to a transaction
* and has a zero b_jcount then remove and release its journal_head. If we did
* see that the buffer is not used by any transaction we also "logically"
* decrement ->b_count.
*
* We in fact take an additional increment on ->b_count as a convenience,
* because the caller usually wants to do additional things with the bh
* after calling here.
* The caller of journal_remove_journal_head() *must* run __brelse(bh) at some
* time. Once the caller has run __brelse(), the buffer is eligible for
* reaping by try_to_free_buffers().
*/
void journal_remove_journal_head(struct buffer_head *bh)
{
jbd_lock_bh_journal_head(bh);
__journal_remove_journal_head(bh);
jbd_unlock_bh_journal_head(bh);
}
void journal_unlock_journal_head(struct journal_head *jh) void journal_unlock_journal_head(struct journal_head *jh)
{ {
spin_lock(&journal_datalist_lock); struct buffer_head *bh = jh2bh(jh);
jbd_lock_bh_journal_head(bh);
J_ASSERT_JH(jh, jh->b_jcount > 0); J_ASSERT_JH(jh, jh->b_jcount > 0);
--jh->b_jcount; --jh->b_jcount;
if (!jh->b_jcount && !jh->b_transaction) { if (!jh->b_jcount && !jh->b_transaction) {
struct buffer_head *bh;
bh = jh2bh(jh);
__journal_remove_journal_head(bh); __journal_remove_journal_head(bh);
__brelse(bh); __brelse(bh);
} }
jbd_unlock_bh_journal_head(bh);
spin_unlock(&journal_datalist_lock);
}
void journal_remove_journal_head(struct buffer_head *bh)
{
spin_lock(&journal_datalist_lock);
__journal_remove_journal_head(bh);
spin_unlock(&journal_datalist_lock);
} }
/* /*
......
...@@ -1204,7 +1204,7 @@ void journal_forget (handle_t *handle, struct buffer_head *bh) ...@@ -1204,7 +1204,7 @@ void journal_forget (handle_t *handle, struct buffer_head *bh)
if (jh->b_cp_transaction) { if (jh->b_cp_transaction) {
__journal_file_buffer(jh, transaction, BJ_Forget); __journal_file_buffer(jh, transaction, BJ_Forget);
} else { } else {
__journal_remove_journal_head(bh); journal_remove_journal_head(bh);
__brelse(bh); __brelse(bh);
if (!buffer_jbd(bh)) { if (!buffer_jbd(bh)) {
spin_unlock(&journal_datalist_lock); spin_unlock(&journal_datalist_lock);
...@@ -1608,7 +1608,7 @@ static inline int __journal_try_to_free_buffer(struct buffer_head *bh) ...@@ -1608,7 +1608,7 @@ static inline int __journal_try_to_free_buffer(struct buffer_head *bh)
JBUFFER_TRACE(jh, "release data"); JBUFFER_TRACE(jh, "release data");
__journal_unfile_buffer(jh); __journal_unfile_buffer(jh);
jh->b_transaction = 0; jh->b_transaction = 0;
__journal_remove_journal_head(bh); journal_remove_journal_head(bh);
__brelse(bh); __brelse(bh);
} }
} else if (jh->b_cp_transaction != 0 && jh->b_transaction == 0) { } else if (jh->b_cp_transaction != 0 && jh->b_transaction == 0) {
...@@ -1616,7 +1616,7 @@ static inline int __journal_try_to_free_buffer(struct buffer_head *bh) ...@@ -1616,7 +1616,7 @@ static inline int __journal_try_to_free_buffer(struct buffer_head *bh)
if (jh->b_jlist == BJ_None) { if (jh->b_jlist == BJ_None) {
JBUFFER_TRACE(jh, "remove from checkpoint list"); JBUFFER_TRACE(jh, "remove from checkpoint list");
__journal_remove_checkpoint(jh); __journal_remove_checkpoint(jh);
__journal_remove_journal_head(bh); journal_remove_journal_head(bh);
__brelse(bh); __brelse(bh);
} }
} }
...@@ -1710,7 +1710,7 @@ static int dispose_buffer(struct journal_head *jh, ...@@ -1710,7 +1710,7 @@ static int dispose_buffer(struct journal_head *jh,
may_free = 0; may_free = 0;
} else { } else {
JBUFFER_TRACE(jh, "on running transaction"); JBUFFER_TRACE(jh, "on running transaction");
__journal_remove_journal_head(bh); journal_remove_journal_head(bh);
__brelse(bh); __brelse(bh);
} }
spin_unlock(&journal_datalist_lock); spin_unlock(&journal_datalist_lock);
...@@ -2051,7 +2051,7 @@ void journal_refile_buffer(struct journal_head *jh) ...@@ -2051,7 +2051,7 @@ void journal_refile_buffer(struct journal_head *jh)
bh = jh2bh(jh); bh = jh2bh(jh);
__journal_refile_buffer(jh); __journal_refile_buffer(jh);
__journal_remove_journal_head(bh); journal_remove_journal_head(bh);
spin_unlock(&journal_datalist_lock); spin_unlock(&journal_datalist_lock);
__brelse(bh); __brelse(bh);
......
...@@ -293,6 +293,7 @@ enum jbd_state_bits { ...@@ -293,6 +293,7 @@ enum jbd_state_bits {
BH_RevokeValid, /* Revoked flag is valid */ BH_RevokeValid, /* Revoked flag is valid */
BH_JBDDirty, /* Is dirty but journaled */ BH_JBDDirty, /* Is dirty but journaled */
BH_State, /* Pins most journal_head state */ BH_State, /* Pins most journal_head state */
BH_JournalHead, /* Pins bh->b_private and jh->b_bh */
}; };
BUFFER_FNS(JBD, jbd) BUFFER_FNS(JBD, jbd)
...@@ -326,6 +327,16 @@ static inline void jbd_unlock_bh_state(struct buffer_head *bh) ...@@ -326,6 +327,16 @@ static inline void jbd_unlock_bh_state(struct buffer_head *bh)
bit_spin_unlock(BH_State, &bh->b_state); bit_spin_unlock(BH_State, &bh->b_state);
} }
static inline void jbd_lock_bh_journal_head(struct buffer_head *bh)
{
bit_spin_lock(BH_JournalHead, &bh->b_state);
}
static inline void jbd_unlock_bh_journal_head(struct buffer_head *bh)
{
bit_spin_unlock(BH_JournalHead, &bh->b_state);
}
#define HAVE_JOURNAL_CALLBACK_STATUS #define HAVE_JOURNAL_CALLBACK_STATUS
/** /**
* struct journal_callback - Base structure for callback information. * struct journal_callback - Base structure for callback information.
...@@ -956,7 +967,6 @@ extern int journal_force_commit(journal_t *); ...@@ -956,7 +967,6 @@ extern int journal_force_commit(journal_t *);
extern struct journal_head extern struct journal_head
*journal_add_journal_head(struct buffer_head *bh); *journal_add_journal_head(struct buffer_head *bh);
extern void journal_remove_journal_head(struct buffer_head *bh); extern void journal_remove_journal_head(struct buffer_head *bh);
extern void __journal_remove_journal_head(struct buffer_head *bh);
extern void journal_unlock_journal_head(struct journal_head *jh); extern void journal_unlock_journal_head(struct journal_head *jh);
/* /*
......
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