int journal_get_undo_access (handle_t *handle, struct buffer_head *bh) { journal_t *journal = handle->h_transaction->t_journal; int err; lock_journal_bh_wait(bh, journal); /* Do this first --- it can drop the journal lock, so we want to * make sure that obtaining the committed_data is done * atomically wrt. completion of any outstanding commits. */ err = do_get_write_access (handle, bh, 1); if (!bh->b_committed_data) { /* Copy out the current buffer contents into the * preserved, committed copy. */ bh->b_committed_data = kmalloc(bh->b_size, GFP_KERNEL); if (!bh->b_committed_data) { unlock_journal(journal); return -ENOMEM; } memcpy (bh->b_committed_data, bh->b_data, bh->b_size); } unlock_journal(journal); if (!err) J_ASSERT(bh->b_committed_data); return err; }
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); }
int journal_get_write_access (handle_t *handle, struct buffer_head *bh) { transaction_t *transaction = handle->h_transaction; journal_t *journal = transaction->t_journal; int rc; /* We do not want to get caught playing with fields which the * log thread also manipulates. Make sure that the buffer * completes any outstanding IO before proceeding. */ lock_journal_bh_wait(bh, journal); rc = do_get_write_access(handle, bh, 0); unlock_journal(journal); return rc; }
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; }
void journal_forget (handle_t *handle, struct buffer_head *bh) { journal_t * journal = handle->h_transaction->t_journal; J_ASSERT (!test_and_set_bit(BH_Freed, &bh->b_state)); clear_bit(BH_Alloced, &bh->b_state); /* @@@ DEBUG: This is just buffer debug state: we can do this without a journal lock... */ if (handle->h_transaction->t_tid == bh->b_alloc_transaction) { bh->b_alloc_transaction = 0; set_bit(BH_QuickFree, &bh->b_state); } bh->b_free2_transaction = bh->b_free_transaction; bh->b_free_transaction = handle->h_transaction->t_tid; /* If we can't lock the journal because somebody else already * has the lock, then just release the buffer: that's better * than changing the semantics of bforget() to include a possible * context switch. */ if (try_lock_journal(journal)) goto nolock; if (bh->b_transaction == handle->h_transaction) { J_ASSERT(!bh->b_frozen_data); /* If we are forgetting a buffer which is already part * of this transaction, then we can just drop it from * the transaction immediately. */ clear_bit(BH_Dirty, &bh->b_state); clear_bit(BH_JDirty, &bh->b_state); journal_unfile_buffer(bh); bh->b_transaction = 0; } else if (bh->b_transaction) { /* However, if the buffer is still owned by a prior * (committing) transaction, we can't do anything with * it right now. */ unlock_journal(journal); brelse(bh); return; } J_ASSERT(!bh->b_frozen_data); J_ASSERT(!bh->b_committed_data); unlock_journal(journal); /* @@@ DEBUG ONLY. Eventually we will indeed want to be able to * discard forgotten buffers a bit more intelligently. */ nolock: brelse(bh); return; #if 0 /* Now we know that the buffer isn't being committed anywhere, * but it might still be on a transaction's checkpoint list. If * so, we want to place it on the new transaction's forget list: * on commit it will undo the old checkpoint. Remember, we have * to be able to unwind the forget() if we take a crash before * the commit! */ if (bh->b_cp_transaction) { journal_file_buffer(bh, handle->h_transaction, BJ_Forget); bh->b_transaction = handle->h_transaction; brelse(bh); } else __bforget(bh); #endif }
int do_get_write_access (handle_t *handle, struct buffer_head *bh, int force_copy) { transaction_t *transaction = handle->h_transaction; journal_t *journal = transaction->t_journal; int error = 0; char *frozen_buffer = NULL; jfs_debug(5, "buffer_head %p, force_copy %d\n", bh, force_copy); repeat: /* @@@ Need to check for errors here at some point. */ /* The caller must make sure that we enter here with the buffer * unlocked (probably by calling lock_journal_bh_wait). If we * sleep in this function, we have to wait again for the buffer * to make sure it is still unlocked. We cannot journal a * buffer if somebody else may already be in the process of * writing it to disk! */ J_ASSERT (buffer_uptodate(bh)); J_ASSERT (!buffer_locked(bh)); /* The buffer is already part of this transaction if * b_transaction or b_next_transaction points to it. */ if (bh->b_transaction == transaction || bh->b_next_transaction == transaction) goto done; /* If there is already a copy-out version of this buffer, then * we don't need to make another one. */ if (bh->b_frozen_data) { J_ASSERT(bh->b_next_transaction == NULL); bh->b_next_transaction = transaction; J_ASSERT(handle->h_buffer_credits > 0); handle->h_buffer_credits--; goto done; } /* Is there data here we need to preserve? */ if (bh->b_transaction && bh->b_transaction != transaction) { J_ASSERT (bh->b_next_transaction == NULL); J_ASSERT (bh->b_transaction == journal->j_committing_transaction); J_ASSERT (bh->b_list == BUF_JOURNAL); /* There is one case we have to be very careful about. * If the committing transaction is currently writing * this buffer out to disk and has NOT made a copy-out, * then we cannot modify the buffer contents at all * right now. The essence of copy-out is that it is the * extra copy, not the primary copy, which gets * journaled. If the primary copy is already going to * disk then we cannot do copy-out here. */ if (bh->b_jlist == BJ_Shadow) { unlock_journal(journal); /* commit wakes up all shadow buffers after IO */ sleep_on(&bh->b_wait); lock_journal_bh_wait(bh, journal); goto repeat; } /* Only do the copy if the currently-owning transaction * still needs it. If it is on the Forget list, the * committing transaction is past that stage. The * buffer had better remain locked during the kmalloc, * but that should be true --- we hold the journal lock * still and the buffer is already on the BUF_JOURNAL * list so won't be flushed. * * Subtle point, though: if this is a get_undo_access, * then we will be relying on the frozen_data to contain * the new value of the committed_data record after the * transaction, so we HAVE to force the frozen_data copy * in that case. */ if (bh->b_jlist != BJ_Forget || force_copy) { if (bh->b_jlist == BJ_Data) J_ASSERT(test_bit(BH_QuickFree, &bh->b_state)); if (!frozen_buffer) { unlock_journal(journal); frozen_buffer = kmalloc(bh->b_size,GFP_KERNEL); lock_journal_bh_wait(bh, journal); if (!frozen_buffer) { error = -ENOMEM; goto done; } goto repeat; } bh->b_frozen_data = frozen_buffer; frozen_buffer = NULL; memcpy (bh->b_frozen_data, bh->b_data, bh->b_size); } bh->b_next_transaction = transaction; } J_ASSERT(handle->h_buffer_credits > 0); handle->h_buffer_credits--; /* Finally, if the buffer is not journaled right now, we need to * make sure it doesn't get written to disk before the caller * actually commits the new data. */ if (!bh->b_transaction) { J_ASSERT (!bh->b_next_transaction); bh->b_transaction = transaction; journal_file_buffer(bh, transaction, BJ_Reserved); } done: if (bh->b_list != BUF_JOURNAL) refile_buffer(bh); clear_bit(BH_QuickFree, &bh->b_state); /* If we are about to journal a buffer, then any revoke pending on it is no longer valid. */ journal_cancel_revoke(handle, bh); if (frozen_buffer) kfree(frozen_buffer); return error; }
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; }