Commit 37835361 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] JBD: fix locking around log_start_commit()

There are various places in which JBD is starting a commit against a
transaction without sufficient locking in place to ensure that that
transaction is still alive.

Change it so that log_start_commit() takes a transaction ID instead.  Make
the caller take a copy of that ID inside the appropriate locks.
parent b1a1826d
...@@ -1811,7 +1811,7 @@ void ext3_write_super (struct super_block * sb) ...@@ -1811,7 +1811,7 @@ void ext3_write_super (struct super_block * sb)
if (down_trylock(&sb->s_lock) == 0) if (down_trylock(&sb->s_lock) == 0)
BUG(); BUG();
sb->s_dirt = 0; sb->s_dirt = 0;
log_start_commit(EXT3_SB(sb)->s_journal, NULL); journal_start_commit(EXT3_SB(sb)->s_journal, NULL);
} }
static int ext3_sync_fs(struct super_block *sb, int wait) static int ext3_sync_fs(struct super_block *sb, int wait)
...@@ -1819,9 +1819,10 @@ static int ext3_sync_fs(struct super_block *sb, int wait) ...@@ -1819,9 +1819,10 @@ static int ext3_sync_fs(struct super_block *sb, int wait)
tid_t target; tid_t target;
sb->s_dirt = 0; sb->s_dirt = 0;
target = log_start_commit(EXT3_SB(sb)->s_journal, NULL); if (journal_start_commit(EXT3_SB(sb)->s_journal, &target)) {
if (wait) if (wait)
log_wait_commit(EXT3_SB(sb)->s_journal, target); log_wait_commit(EXT3_SB(sb)->s_journal, target);
}
return 0; return 0;
} }
......
...@@ -124,7 +124,6 @@ static void jbd_sync_bh(journal_t *journal, struct buffer_head *bh) ...@@ -124,7 +124,6 @@ static void jbd_sync_bh(journal_t *journal, struct buffer_head *bh)
* checkpoint. (journal_remove_checkpoint() deletes the transaction when * checkpoint. (journal_remove_checkpoint() deletes the transaction when
* the last checkpoint buffer is cleansed) * the last checkpoint buffer is cleansed)
* *
* Called with the journal locked.
* Called with j_list_lock held. * Called with j_list_lock held.
*/ */
static int __cleanup_transaction(journal_t *journal, transaction_t *transaction) static int __cleanup_transaction(journal_t *journal, transaction_t *transaction)
...@@ -167,16 +166,11 @@ static int __cleanup_transaction(journal_t *journal, transaction_t *transaction) ...@@ -167,16 +166,11 @@ static int __cleanup_transaction(journal_t *journal, transaction_t *transaction)
spin_unlock(&journal->j_list_lock); spin_unlock(&journal->j_list_lock);
jbd_unlock_bh_state(bh); jbd_unlock_bh_state(bh);
log_start_commit(journal, t); log_start_commit(journal, tid);
log_wait_commit(journal, tid); log_wait_commit(journal, tid);
goto out_return_1; goto out_return_1;
} }
/*
* We used to test for (jh->b_list != BUF_CLEAN) here.
* But unmap_underlying_metadata() can place buffer onto
* BUF_CLEAN.
*/
/* /*
* AKPM: I think the buffer_jbddirty test is redundant - it * AKPM: I think the buffer_jbddirty test is redundant - it
* shouldn't have NULL b_transaction? * shouldn't have NULL b_transaction?
...@@ -322,7 +316,7 @@ int log_do_checkpoint(journal_t *journal, int nblocks) ...@@ -322,7 +316,7 @@ int log_do_checkpoint(journal_t *journal, int nblocks)
int cleanup_ret, retry = 0; int cleanup_ret, retry = 0;
tid_t this_tid; tid_t this_tid;
transaction = journal->j_checkpoint_transactions->t_cpprev; transaction = journal->j_checkpoint_transactions->t_cpnext;
this_tid = transaction->t_tid; this_tid = transaction->t_tid;
jh = transaction->t_checkpoint_list; jh = transaction->t_checkpoint_list;
last_jh = jh->b_cpprev; last_jh = jh->b_cpprev;
...@@ -359,7 +353,7 @@ int log_do_checkpoint(journal_t *journal, int nblocks) ...@@ -359,7 +353,7 @@ int log_do_checkpoint(journal_t *journal, int nblocks)
* If someone cleaned up this transaction while we slept, we're * If someone cleaned up this transaction while we slept, we're
* done * done
*/ */
if (journal->j_checkpoint_transactions->t_cpprev != transaction) if (journal->j_checkpoint_transactions->t_cpnext != transaction)
continue; continue;
/* /*
* Maybe it's a new transaction, but it fell at the same * Maybe it's a new transaction, but it fell at the same
......
...@@ -73,7 +73,7 @@ EXPORT_SYMBOL(journal_errno); ...@@ -73,7 +73,7 @@ EXPORT_SYMBOL(journal_errno);
EXPORT_SYMBOL(journal_ack_err); EXPORT_SYMBOL(journal_ack_err);
EXPORT_SYMBOL(journal_clear_err); EXPORT_SYMBOL(journal_clear_err);
EXPORT_SYMBOL(log_wait_commit); EXPORT_SYMBOL(log_wait_commit);
EXPORT_SYMBOL(log_start_commit); EXPORT_SYMBOL(journal_start_commit);
EXPORT_SYMBOL(journal_wipe); EXPORT_SYMBOL(journal_wipe);
EXPORT_SYMBOL(journal_blocks_per_page); EXPORT_SYMBOL(journal_blocks_per_page);
EXPORT_SYMBOL(journal_invalidatepage); EXPORT_SYMBOL(journal_invalidatepage);
...@@ -433,52 +433,55 @@ int __log_space_left(journal_t *journal) ...@@ -433,52 +433,55 @@ int __log_space_left(journal_t *journal)
} }
/* /*
* Called under j_state_lock. * Called under j_state_lock. Returns true if a transaction was started.
*/ */
tid_t __log_start_commit(journal_t *journal, transaction_t *transaction) int __log_start_commit(journal_t *journal, tid_t target)
{ {
tid_t target = journal->j_commit_request;
/*
* A NULL transaction asks us to commit the currently running
* transaction, if there is one.
*/
if (transaction)
target = transaction->t_tid;
else {
transaction = journal->j_running_transaction;
if (!transaction)
goto out;
target = transaction->t_tid;
}
/* /*
* Are we already doing a recent enough commit? * Are we already doing a recent enough commit?
*/ */
if (tid_geq(journal->j_commit_request, target)) if (!tid_geq(journal->j_commit_request, target)) {
goto out; /*
* We want a new commit: OK, mark the request and wakup the
* commit thread. We do _not_ do the commit ourselves.
*/
/* journal->j_commit_request = target;
* We want a new commit: OK, mark the request and wakup the jbd_debug(1, "JBD: requesting commit %d/%d\n",
* commit thread. We do _not_ do the commit ourselves. journal->j_commit_request,
*/ journal->j_commit_sequence);
wake_up(&journal->j_wait_commit);
return 1;
}
return 0;
}
journal->j_commit_request = target; int log_start_commit(journal_t *journal, tid_t tid)
jbd_debug(1, "JBD: requesting commit %d/%d\n", {
journal->j_commit_request, int ret;
journal->j_commit_sequence);
wake_up(&journal->j_wait_commit);
out: spin_lock(&journal->j_state_lock);
return target; ret = __log_start_commit(journal, tid);
spin_unlock(&journal->j_state_lock);
return ret;
} }
tid_t log_start_commit(journal_t *journal, transaction_t *transaction) /*
* Start a commit of the current running transaction (if any). Returns true
* if a transaction was started, and fills its tid in at *ptid
*/
int journal_start_commit(journal_t *journal, tid_t *ptid)
{ {
tid_t ret; int ret = 0;
spin_lock(&journal->j_state_lock); spin_lock(&journal->j_state_lock);
ret = __log_start_commit(journal, transaction); if (journal->j_running_transaction) {
tid_t tid = journal->j_running_transaction->t_tid;
ret = __log_start_commit(journal, tid);
if (ret && ptid)
*ptid = tid;
}
spin_unlock(&journal->j_state_lock); spin_unlock(&journal->j_state_lock);
return ret; return ret;
} }
...@@ -1243,7 +1246,7 @@ int journal_flush(journal_t *journal) ...@@ -1243,7 +1246,7 @@ int journal_flush(journal_t *journal)
/* Force everything buffered to the log... */ /* Force everything buffered to the log... */
if (journal->j_running_transaction) { if (journal->j_running_transaction) {
transaction = journal->j_running_transaction; transaction = journal->j_running_transaction;
__log_start_commit(journal, transaction); __log_start_commit(journal, transaction->t_tid);
} else if (journal->j_committing_transaction) } else if (journal->j_committing_transaction)
transaction = journal->j_committing_transaction; transaction = journal->j_committing_transaction;
...@@ -1374,7 +1377,7 @@ void __journal_abort_hard(journal_t *journal) ...@@ -1374,7 +1377,7 @@ void __journal_abort_hard(journal_t *journal)
journal->j_flags |= JFS_ABORT; journal->j_flags |= JFS_ABORT;
transaction = journal->j_running_transaction; transaction = journal->j_running_transaction;
if (transaction) if (transaction)
__log_start_commit(journal, transaction); __log_start_commit(journal, transaction->t_tid);
spin_unlock(&journal->j_state_lock); spin_unlock(&journal->j_state_lock);
} }
......
...@@ -173,7 +173,7 @@ static int start_this_handle(journal_t *journal, handle_t *handle) ...@@ -173,7 +173,7 @@ static int start_this_handle(journal_t *journal, handle_t *handle)
spin_unlock(&transaction->t_handle_lock); spin_unlock(&transaction->t_handle_lock);
prepare_to_wait(&journal->j_wait_transaction_locked, &wait, prepare_to_wait(&journal->j_wait_transaction_locked, &wait,
TASK_UNINTERRUPTIBLE); TASK_UNINTERRUPTIBLE);
__log_start_commit(journal, transaction); __log_start_commit(journal, transaction->t_tid);
spin_unlock(&journal->j_state_lock); spin_unlock(&journal->j_state_lock);
schedule(); schedule();
finish_wait(&journal->j_wait_transaction_locked, &wait); finish_wait(&journal->j_wait_transaction_locked, &wait);
...@@ -396,6 +396,7 @@ int journal_restart(handle_t *handle, int nblocks) ...@@ -396,6 +396,7 @@ int journal_restart(handle_t *handle, int nblocks)
J_ASSERT(transaction->t_updates > 0); J_ASSERT(transaction->t_updates > 0);
J_ASSERT(journal_current_handle() == handle); J_ASSERT(journal_current_handle() == handle);
spin_lock(&journal->j_state_lock);
spin_lock(&transaction->t_handle_lock); spin_lock(&transaction->t_handle_lock);
transaction->t_outstanding_credits -= handle->h_buffer_credits; transaction->t_outstanding_credits -= handle->h_buffer_credits;
transaction->t_updates--; transaction->t_updates--;
...@@ -405,7 +406,8 @@ int journal_restart(handle_t *handle, int nblocks) ...@@ -405,7 +406,8 @@ int journal_restart(handle_t *handle, int nblocks)
spin_unlock(&transaction->t_handle_lock); spin_unlock(&transaction->t_handle_lock);
jbd_debug(2, "restarting handle %p\n", handle); jbd_debug(2, "restarting handle %p\n", handle);
log_start_commit(journal, transaction); __log_start_commit(journal, transaction->t_tid);
spin_unlock(&journal->j_state_lock);
handle->h_buffer_credits = nblocks; handle->h_buffer_credits = nblocks;
ret = start_this_handle(journal, handle); ret = start_this_handle(journal, handle);
...@@ -1382,6 +1384,7 @@ int journal_stop(handle_t *handle) ...@@ -1382,6 +1384,7 @@ int journal_stop(handle_t *handle)
} }
current->journal_info = NULL; current->journal_info = NULL;
spin_lock(&journal->j_state_lock);
spin_lock(&transaction->t_handle_lock); spin_lock(&transaction->t_handle_lock);
transaction->t_outstanding_credits -= handle->h_buffer_credits; transaction->t_outstanding_credits -= handle->h_buffer_credits;
transaction->t_updates--; transaction->t_updates--;
...@@ -1415,8 +1418,9 @@ int journal_stop(handle_t *handle) ...@@ -1415,8 +1418,9 @@ int journal_stop(handle_t *handle)
jbd_debug(2, "transaction too old, requesting commit for " jbd_debug(2, "transaction too old, requesting commit for "
"handle %p\n", handle); "handle %p\n", handle);
/* This is non-blocking */ /* This is non-blocking */
log_start_commit(journal, transaction); __log_start_commit(journal, transaction->t_tid);
spin_unlock(&journal->j_state_lock);
/* /*
* Special case: JFS_SYNC synchronous updates require us * Special case: JFS_SYNC synchronous updates require us
* to wait for the commit to complete. * to wait for the commit to complete.
...@@ -1425,6 +1429,7 @@ int journal_stop(handle_t *handle) ...@@ -1425,6 +1429,7 @@ int journal_stop(handle_t *handle)
err = log_wait_commit(journal, tid); err = log_wait_commit(journal, tid);
} else { } else {
spin_unlock(&transaction->t_handle_lock); spin_unlock(&transaction->t_handle_lock);
spin_unlock(&journal->j_state_lock);
} }
jbd_free_handle(handle); jbd_free_handle(handle);
......
...@@ -184,17 +184,6 @@ static inline handle_t *ext3_journal_current_handle(void) ...@@ -184,17 +184,6 @@ static inline handle_t *ext3_journal_current_handle(void)
return journal_current_handle(); return journal_current_handle();
} }
static inline void
ext3_log_start_commit(journal_t *journal, transaction_t *transaction)
{
log_start_commit(journal, transaction);
}
static inline void ext3_log_wait_commit(journal_t *journal, tid_t tid)
{
log_wait_commit(journal, tid);
}
static inline int ext3_journal_extend(handle_t *handle, int nblocks) static inline int ext3_journal_extend(handle_t *handle, int nblocks)
{ {
return journal_extend(handle, nblocks); return journal_extend(handle, nblocks);
......
...@@ -988,12 +988,13 @@ extern void journal_switch_revoke_table(journal_t *journal); ...@@ -988,12 +988,13 @@ extern void journal_switch_revoke_table(journal_t *journal);
*/ */
int __log_space_left(journal_t *); /* Called with journal locked */ int __log_space_left(journal_t *); /* Called with journal locked */
extern tid_t log_start_commit (journal_t *, transaction_t *); int log_start_commit(journal_t *journal, tid_t tid);
extern tid_t __log_start_commit(journal_t *, transaction_t *); int __log_start_commit(journal_t *journal, tid_t tid);
extern int log_wait_commit (journal_t *, tid_t); int journal_start_commit(journal_t *journal, tid_t *tid);
extern int log_do_checkpoint (journal_t *, int); int log_wait_commit(journal_t *journal, tid_t tid);
int log_do_checkpoint(journal_t *journal, int nblocks);
void __log_wait_for_space(journal_t *, int nblocks); void __log_wait_for_space(journal_t *journal, int nblocks);
extern void __journal_drop_transaction(journal_t *, transaction_t *); extern void __journal_drop_transaction(journal_t *, transaction_t *);
extern int cleanup_journal_tail(journal_t *); extern int cleanup_journal_tail(journal_t *);
......
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