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 journal_forget (handle_t *handle, struct buffer_head *bh) { transaction_t *transaction = handle->h_transaction; journal_t *journal = transaction->t_journal; struct journal_head *jh; int drop_reserve = 0; int err = 0; int was_modified = 0; BUFFER_TRACE(bh, "entry"); jbd_lock_bh_state(bh); spin_lock(&journal->j_list_lock); if (!buffer_jbd(bh)) goto not_jbd; jh = bh2jh(bh); /* Critical error: attempting to delete a bitmap buffer, maybe? * Don't do any jbd operations, and return an error. */ if (!J_EXPECT_JH(jh, !jh->b_committed_data, "inconsistent data on disk")) { err = -EIO; goto not_jbd; } /* keep track of wether or not this transaction modified us */ was_modified = jh->b_modified; /* * The buffer's going from the transaction, we must drop * all references -bzzz */ jh->b_modified = 0; if (jh->b_transaction == handle->h_transaction) { J_ASSERT_JH(jh, !jh->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_buffer_dirty(bh); clear_buffer_jbddirty(bh); JBUFFER_TRACE(jh, "belongs to current transaction: unfile"); /* * we only want to drop a reference if this transaction * modified the buffer */ if (was_modified) drop_reserve = 1; /* * We are no longer going to journal this buffer. * However, the commit of this transaction is still * important to the buffer: the delete that we are now * processing might obsolete an old log entry, so by * committing, we can satisfy the buffer's checkpoint. * * So, if we have a checkpoint on the buffer, we should * now refile the buffer on our BJ_Forget list so that * we know to remove the checkpoint after we commit. */ if (jh->b_cp_transaction) { __journal_temp_unlink_buffer(jh); __journal_file_buffer(jh, transaction, BJ_Forget); } else { __journal_unfile_buffer(jh); journal_remove_journal_head(bh); __brelse(bh); if (!buffer_jbd(bh)) { spin_unlock(&journal->j_list_lock); jbd_unlock_bh_state(bh); __bforget(bh); goto drop; } } } else if (jh->b_transaction) { J_ASSERT_JH(jh, (jh->b_transaction == journal->j_committing_transaction)); /* However, if the buffer is still owned by a prior * (committing) transaction, we can't drop it yet... */ JBUFFER_TRACE(jh, "belongs to older transaction"); /* ... but we CAN drop it from the new transaction if we * have also modified it since the original commit. */ if (jh->b_next_transaction) { J_ASSERT(jh->b_next_transaction == transaction); jh->b_next_transaction = NULL; /* * only drop a reference if this transaction modified * the buffer */ if (was_modified) drop_reserve = 1; } } not_jbd: spin_unlock(&journal->j_list_lock); jbd_unlock_bh_state(bh); __brelse(bh); drop: if (drop_reserve) { /* no need to reserve log space for this block -bzzz */ handle->h_buffer_credits++; } return err; }
int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh) { transaction_t *transaction = handle->h_transaction; journal_t *journal = transaction->t_journal; struct journal_head *jh; int drop_reserve = 0; int err = 0; int was_modified = 0; BUFFER_TRACE(bh, "entry"); jbd_lock_bh_state(bh); spin_lock(&journal->j_list_lock); if (!buffer_jbd(bh)) goto not_jbd; jh = bh2jh(bh); if (!J_EXPECT_JH(jh, !jh->b_committed_data, "inconsistent data on disk")) { err = -EIO; goto not_jbd; } was_modified = jh->b_modified; jh->b_modified = 0; if (jh->b_transaction == handle->h_transaction) { J_ASSERT_JH(jh, !jh->b_frozen_data); clear_buffer_dirty(bh); clear_buffer_jbddirty(bh); JBUFFER_TRACE(jh, "belongs to current transaction: unfile"); if (was_modified) drop_reserve = 1; if (jh->b_cp_transaction) { __jbd2_journal_temp_unlink_buffer(jh); __jbd2_journal_file_buffer(jh, transaction, BJ_Forget); } else { __jbd2_journal_unfile_buffer(jh); if (!buffer_jbd(bh)) { spin_unlock(&journal->j_list_lock); jbd_unlock_bh_state(bh); __bforget(bh); goto drop; } } } else if (jh->b_transaction) { J_ASSERT_JH(jh, (jh->b_transaction == journal->j_committing_transaction)); JBUFFER_TRACE(jh, "belongs to older transaction"); if (jh->b_next_transaction) { J_ASSERT(jh->b_next_transaction == transaction); jh->b_next_transaction = NULL; if (was_modified) drop_reserve = 1; } } not_jbd: spin_unlock(&journal->j_list_lock); jbd_unlock_bh_state(bh); __brelse(bh); drop: if (drop_reserve) { handle->h_buffer_credits++; } return err; }