We now start to move onto the fields of the topmost JBD data structure: the journal. The patch implements the designed locking around the j_barrier_count member. And as a part of that, a lot of the new locking scheme is implemented. Several lock_kernel()s and sleep_on()s go away. fs/jbd/transaction.c | 110 +++++++++++++++++++++++++++++++++------------------ 1 files changed, 73 insertions(+), 37 deletions(-) diff -puN fs/jbd/transaction.c~jbd-150-j_barrier_count-locking fs/jbd/transaction.c --- 25/fs/jbd/transaction.c~jbd-150-j_barrier_count-locking 2003-05-24 20:23:25.000000000 -0700 +++ 25-akpm/fs/jbd/transaction.c 2003-05-24 20:29:07.000000000 -0700 @@ -41,16 +41,9 @@ * processes trying to touch the journal while it is in transition. */ -static transaction_t *get_transaction(journal_t *journal) +static transaction_t * +get_transaction(journal_t *journal, transaction_t *transaction) { - transaction_t * transaction; - - transaction = jbd_kmalloc(sizeof(*transaction), GFP_NOFS); - if (!transaction) - return NULL; - - memset(transaction, 0, sizeof(*transaction)); - transaction->t_journal = journal; transaction->t_state = T_RUNNING; transaction->t_tid = journal->j_transaction_sequence++; @@ -60,12 +53,12 @@ static transaction_t *get_transaction(jo spin_lock_init(&transaction->t_jcb_lock); /* Set up the commit timer for the new transaction. */ - J_ASSERT (!journal->j_commit_timer_active); + J_ASSERT(!journal->j_commit_timer_active); journal->j_commit_timer_active = 1; journal->j_commit_timer->expires = transaction->t_expires; add_timer(journal->j_commit_timer); - J_ASSERT (journal->j_running_transaction == NULL); + J_ASSERT(journal->j_running_transaction == NULL); journal->j_running_transaction = transaction; return transaction; @@ -91,6 +84,7 @@ static int start_this_handle(journal_t * transaction_t *transaction; int needed; int nblocks = handle->h_buffer_credits; + transaction_t *new_transaction = NULL; if (nblocks > journal->j_max_transaction_buffers) { printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n", @@ -99,40 +93,67 @@ static int start_this_handle(journal_t * return -ENOSPC; } +alloc_transaction: + if (!journal->j_running_transaction) { + new_transaction = jbd_kmalloc(sizeof(*new_transaction), + GFP_NOFS); + if (!new_transaction) + return -ENOMEM; + memset(new_transaction, 0, sizeof(*new_transaction)); + } + jbd_debug(3, "New handle %p going live.\n", handle); repeat: lock_journal(journal); + /* + * We need to hold j_state_lock until t_updates has been incremented, + * for proper journal barrier handling + */ + spin_lock(&journal->j_state_lock); if (is_journal_aborted(journal) || (journal->j_errno != 0 && !(journal->j_flags & JFS_ACK_ERR))) { + spin_unlock(&journal->j_state_lock); unlock_journal(journal); return -EROFS; } /* Wait on the journal's transaction barrier if necessary */ if (journal->j_barrier_count) { + spin_unlock(&journal->j_state_lock); unlock_journal(journal); - sleep_on(&journal->j_wait_transaction_locked); + wait_event(journal->j_wait_transaction_locked, + journal->j_barrier_count == 0); goto repeat; } repeat_locked: - if (!journal->j_running_transaction) - get_transaction(journal); + if (!journal->j_running_transaction) { + if (!new_transaction) { + spin_unlock(&journal->j_state_lock); + unlock_journal(journal); + goto alloc_transaction; + } + get_transaction(journal, new_transaction); + } + /* @@@ Error? */ J_ASSERT(journal->j_running_transaction); transaction = journal->j_running_transaction; - /* If the current transaction is locked down for commit, wait - * for the lock to be released. */ - + /* + * If the current transaction is locked down for commit, wait for the + * lock to be released. + */ if (transaction->t_state == T_LOCKED) { + spin_unlock(&journal->j_state_lock); unlock_journal(journal); jbd_debug(3, "Handle %p stalling...\n", handle); - sleep_on(&journal->j_wait_transaction_locked); + wait_event(journal->j_wait_transaction_locked, + transaction->t_state != T_LOCKED); goto repeat; } @@ -145,16 +166,24 @@ repeat_locked: needed = transaction->t_outstanding_credits + nblocks; if (needed > journal->j_max_transaction_buffers) { - /* If the current transaction is already too large, then - * start to commit it: we can then go back and attach - * this handle to a new transaction. */ - + /* + * If the current transaction is already too large, then start + * to commit it: we can then go back and attach this handle to + * a new transaction. + */ + DEFINE_WAIT(wait); + spin_unlock(&transaction->t_handle_lock); + spin_unlock(&journal->j_state_lock); jbd_debug(2, "Handle %p starting new commit...\n", handle); + prepare_to_wait(&journal->j_wait_transaction_locked, &wait, + TASK_UNINTERRUPTIBLE); log_start_commit(journal, transaction); unlock_journal(journal); - sleep_on(&journal->j_wait_transaction_locked); + schedule(); + finish_wait(&journal->j_wait_transaction_locked, &wait); lock_journal(journal); + spin_lock(&journal->j_state_lock); goto repeat_locked; } @@ -191,7 +220,9 @@ repeat_locked: if (log_space_left(journal) < needed) { jbd_debug(2, "Handle %p waiting for checkpoint...\n", handle); spin_unlock(&transaction->t_handle_lock); + spin_unlock(&journal->j_state_lock); log_wait_for_space(journal, needed); + spin_lock(&journal->j_state_lock); goto repeat_locked; } @@ -206,9 +237,8 @@ repeat_locked: handle, nblocks, transaction->t_outstanding_credits, log_space_left(journal)); spin_unlock(&transaction->t_handle_lock); - + spin_unlock(&journal->j_state_lock); unlock_journal(journal); - return 0; } @@ -407,11 +437,13 @@ int journal_restart(handle_t *handle, in */ void journal_lock_updates(journal_t *journal) { + DEFINE_WAIT(wait); + lock_journal(journal); - lock_kernel(); + spin_lock(&journal->j_state_lock); ++journal->j_barrier_count; - unlock_kernel(); + spin_unlock(&journal->j_state_lock); /* Wait until there are no running updates */ while (1) { @@ -423,19 +455,23 @@ void journal_lock_updates(journal_t *jou spin_unlock(&transaction->t_handle_lock); break; } - + prepare_to_wait(&journal->j_wait_updates, &wait, + TASK_UNINTERRUPTIBLE); spin_unlock(&transaction->t_handle_lock); unlock_journal(journal); - sleep_on(&journal->j_wait_updates); + schedule(); + finish_wait(&journal->j_wait_updates, &wait); lock_journal(journal); } unlock_journal(journal); - /* We have now established a barrier against other normal - * updates, but we also need to barrier against other - * journal_lock_updates() calls to make sure that we serialise - * special journal-locked operations too. */ + /* + * We have now established a barrier against other normal updates, but + * we also need to barrier against other journal_lock_updates() calls + * to make sure that we serialise special journal-locked operations + * too. + */ down(&journal->j_barrier); } @@ -451,12 +487,12 @@ void journal_unlock_updates (journal_t * { lock_journal(journal); - J_ASSERT (journal->j_barrier_count != 0); - + J_ASSERT(journal->j_barrier_count != 0); + up(&journal->j_barrier); - lock_kernel(); + spin_lock(&journal->j_state_lock); --journal->j_barrier_count; - unlock_kernel(); + spin_unlock(&journal->j_state_lock); wake_up(&journal->j_wait_transaction_locked); unlock_journal(journal); } _