int journal_dirty_data (handle_t *handle, struct buffer_head *bh) { journal_t *journal = handle->h_transaction->t_journal; lock_journal(journal); mark_buffer_dirty(bh, 0); /* * What if the buffer is already part of a running transaction? * * There are two cases: * 1) It is part of the current running transaction. Refile it, * just in case we have allocated it as metadata, deallocated * it, then reallocated it as data. * 2) It is part of the previous, still-committing transaction. * If all we want to do is to guarantee that the buffer will be * written to disk before this new transaction commits, then * being sure that the *previous* transaction has this same * property is sufficient for us! Just leave it on its old * transaction. * * In case (2), the buffer must not already exist as metadata * --- that would violate write ordering (a transaction is free * to write its data at any point, even before the previous * committing transaction has committed). The caller must * never, ever allow this to happen: there's nothing we can do * about it in this layer. */ if (bh->b_transaction) { if (bh->b_transaction != handle->h_transaction) { J_ASSERT (bh->b_transaction == journal->j_committing_transaction); /* @@@ IS THIS TRUE ? */ J_ASSERT (bh->b_next_transaction == NULL); /* Special case --- the buffer might actually have been allocated and then immediately deallocated in the previous, committing transaction, so might still be left on that transaction's metadata lists. */ if (bh->b_jlist != BJ_Data) { J_ASSERT (bh->b_jlist != BJ_Shadow); J_ASSERT (test_and_clear_bit(BH_QuickFree, &bh->b_state)); journal_unfile_buffer(bh); bh->b_transaction = NULL; journal_file_buffer(bh, handle->h_transaction, BJ_Data); refile_buffer(bh); } } } else { journal_file_buffer(bh, handle->h_transaction, BJ_Data); refile_buffer(bh); } unlock_journal(journal); return 0; }
static void lock_journal_bh_wait(struct buffer_head *bh, journal_t *journal) { repeat: wait_on_buffer(bh); lock_journal(journal); if (buffer_locked(bh)) { unlock_journal(journal); goto repeat; } }
void journal_unlock_updates (journal_t *journal) { lock_journal(journal); J_ASSERT (journal->j_barrier_count != 0); up(&journal->j_barrier); --journal->j_barrier_count; wake_up(&journal->j_wait_transaction_locked); unlock_journal(journal); }
void journal_lock_updates (journal_t *journal) { lock_journal(journal); ++journal->j_barrier_count; /* Wait until there are no running updates */ while (1) { transaction_t *transaction = journal->j_running_transaction; if (!transaction) break; if (!transaction->t_updates) break; unlock_journal(journal); sleep_on(&journal->j_wait_updates); lock_journal(journal); } unlock_journal(journal); down(&journal->j_barrier); }
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); } }
void journal_release_buffer (handle_t *handle, struct buffer_head *bh) { transaction_t *transaction = handle->h_transaction; journal_t *journal = transaction->t_journal; lock_journal(journal); /* If the buffer is reserved but not modified by this * transaction, then it is safe to release it. In all other * cases, just leave the buffer as it is. */ if (bh->b_jlist == BJ_Reserved && bh->b_transaction == transaction && !buffer_jdirty(bh)) { handle->h_buffer_credits++; journal_refile_buffer(bh); } unlock_journal(journal); }
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; }
int journal_get_create_access (handle_t *handle, struct buffer_head *bh) { transaction_t *transaction = handle->h_transaction; journal_t *journal = transaction->t_journal; jfs_debug(5, "buffer_head %p\n", bh); lock_journal(journal); /* The buffer may already belong to this transaction due to pre-zeroing in the filesystem's new_block code */ J_ASSERT (bh->b_transaction == transaction || bh->b_transaction == NULL); J_ASSERT (bh->b_next_transaction == NULL); J_ASSERT (buffer_locked(bh)); J_ASSERT(handle->h_buffer_credits > 0); handle->h_buffer_credits--; bh->b_transaction = transaction; journal_file_buffer(bh, transaction, BJ_Reserved); refile_buffer(bh); unlock_journal(journal); return 0; }
int journal_dirty_metadata (handle_t *handle, struct buffer_head *bh) { transaction_t *transaction = handle->h_transaction; journal_t *journal = transaction->t_journal; jfs_debug(5, "buffer_head %p\n", bh); lock_journal(journal); mark_buffer_jdirty(bh); J_ASSERT(bh->b_transaction != NULL); /* * Metadata already on the current transaction list doesn't * need to be filed. Metadata on another transaction's list must * be committing, and will be refiled once the commit completes: * leave it alone for now. */ if (bh->b_transaction != transaction) { J_ASSERT (bh->b_transaction == journal->j_committing_transaction); J_ASSERT (bh->b_next_transaction == transaction); /* And this case is illegal: we can't reuse another * transaction's data buffer, ever. */ J_ASSERT (bh->b_jlist != BJ_Data); goto done; } /* That test should have eliminated the following case: */ J_ASSERT (bh->b_frozen_data == 0); journal_file_buffer (bh, handle->h_transaction, BJ_Metadata); done: unlock_journal(journal); return 0; }
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; }
/* * Clean up a transaction's checkpoint list. * * We wait for any pending IO to complete and make sure any clean * buffers are removed from the transaction. * * Return 1 if we performed any actions which might have destroyed the * checkpoint. (journal_remove_checkpoint() deletes the transaction when * the last checkpoint buffer is cleansed) * * Called with the journal locked. * Called with journal_datalist_lock held. */ static int __cleanup_transaction(journal_t *journal, transaction_t *transaction) { struct journal_head *jh, *next_jh, *last_jh; struct buffer_head *bh; int ret = 0; assert_spin_locked(&journal_datalist_lock); jh = transaction->t_checkpoint_list; if (!jh) return 0; last_jh = jh->b_cpprev; next_jh = jh; do { jh = next_jh; bh = jh2bh(jh); if (buffer_locked(bh)) { atomic_inc(&bh->b_count); spin_unlock(&journal_datalist_lock); unlock_journal(journal); wait_on_buffer(bh); /* the journal_head may have gone by now */ BUFFER_TRACE(bh, "brelse"); __brelse(bh); goto out_return_1; } if (jh->b_transaction != NULL) { transaction_t *transaction = jh->b_transaction; tid_t tid = transaction->t_tid; spin_unlock(&journal_datalist_lock); log_start_commit(journal, transaction); unlock_journal(journal); log_wait_commit(journal, tid); 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. Since refile_buffer() no longer takes buffers * off checkpoint lists, we cope with it here */ /* * AKPM: I think the buffer_jdirty test is redundant - it * shouldn't have NULL b_transaction? */ next_jh = jh->b_cpnext; if (!buffer_dirty(bh) && !buffer_jdirty(bh)) { BUFFER_TRACE(bh, "remove from checkpoint"); __journal_remove_checkpoint(jh); __journal_remove_journal_head(bh); refile_buffer(bh); __brelse(bh); ret = 1; } jh = next_jh; } while (jh != last_jh); return ret; out_return_1: lock_journal(journal); spin_lock(&journal_datalist_lock); return 1; }