void log_wait_for_space(journal_t *journal, int nblocks) { while (log_space_left(journal) < nblocks) { if (journal->j_flags & JFS_ABORT) return; unlock_journal(journal); down(&journal->j_checkpoint_sem); lock_journal(journal); /* Test again, another process may have checkpointed * while we were waiting for the checkpoint lock */ if (log_space_left(journal) < nblocks) { log_do_checkpoint(journal, nblocks); } up(&journal->j_checkpoint_sem); } }
int journal_extend (handle_t *handle, int nblocks) { transaction_t *transaction = handle->h_transaction; journal_t *journal = transaction->t_journal; int result = 1; int wanted; lock_journal (journal); /* Don't extend a locked-down transaction! */ if (handle->h_transaction->t_state != T_RUNNING) { jfs_debug(3, "denied handle %p %d blocks: " "transaction not running\n", handle, nblocks); goto error_out; } wanted = transaction->t_outstanding_credits + nblocks; if (wanted > journal->j_max_transaction_buffers) { jfs_debug(3, "denied handle %p %d blocks: " "transaction too large\n", handle, nblocks); goto error_out; } if (wanted > log_space_left(journal)) { jfs_debug(3, "denied handle %p %d blocks: " "insufficient log space\n", handle, nblocks); goto error_out; } handle->h_buffer_credits += nblocks; transaction->t_outstanding_credits += nblocks; result = 0; jfs_debug(3, "extended handle %p by %d\n", handle, nblocks); error_out: unlock_journal (journal); return result; }
static int start_this_handle(journal_t *journal, handle_t *handle) { transaction_t *transaction; int needed; int nblocks = handle->h_buffer_credits; jfs_debug(4, "New handle %p going live.\n", handle); repeat: lock_journal(journal); if ((journal->j_flags & JFS_ABORT) || (journal->j_errno != 0 && !(journal->j_flags & JFS_ACK_ERR))) { unlock_journal(journal); return -EROFS; } /* Wait on the journal's transaction barrier if necessary */ if (journal->j_barrier_count) { unlock_journal(journal); sleep_on(&journal->j_wait_transaction_locked); goto repeat; } repeat_locked: if (!journal->j_running_transaction) get_transaction(journal); /* @@@ 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 (transaction->t_state == T_LOCKED) { unlock_journal(journal); jfs_debug(3, "Handle %p stalling...\n", handle); sleep_on(&journal->j_wait_transaction_locked); goto repeat; } /* If there is not enough space left in the log to write all * potential buffers requested by this operation, we need to * stall pending a log checkpoint to free some more log * space. */ 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. */ jfs_debug(2, "Handle %p starting new commit...\n", handle); log_start_commit(journal, transaction); unlock_journal(journal); sleep_on(&journal->j_wait_transaction_locked); lock_journal(journal); goto repeat_locked; } /* * The commit code assumes that it can get enough log space * without forcing a checkpoint. This is *critical* for * correctness: a checkpoint of a buffer which is also * associated with a committing transaction creates a deadlock, * so commit simply cannot force through checkpoints. * * We must therefore ensure the necessary space in the journal * *before* starting to dirty potentially checkpointed buffers * in the new transaction. * * The worst part is, any transaction currently committing can * reduce the free space arbitrarily. Be careful to account for * those buffers when checkpointing. */ needed = journal->j_max_transaction_buffers; if (journal->j_committing_transaction) needed += journal->j_committing_transaction->t_outstanding_credits; if (log_space_left(journal) < needed) { jfs_debug(2, "Handle %p waiting for checkpoint...\n", handle); log_wait_for_space(journal, needed); goto repeat_locked; } /* OK, account for the buffers that this operation expects to * use and add the handle to the running transaction. */ handle->h_transaction = transaction; transaction->t_outstanding_credits += nblocks; transaction->t_updates++; jfs_debug(4, "Handle %p given %d credits (total %d, free %d)\n", handle, nblocks, transaction->t_outstanding_credits, log_space_left(journal)); unlock_journal(journal); return 0; }