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;
}