コード例 #1
0
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;
}
コード例 #2
0
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;
}
コード例 #3
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;
	}
}
コード例 #4
0
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);
}
コード例 #5
0
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);
}
コード例 #6
0
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;
}
コード例 #7
0
ファイル: checkpoint.c プロジェクト: hugh712/Jollen
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);
	}
}
コード例 #8
0
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);
}
コード例 #9
0
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;
}
コード例 #10
0
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;
}
コード例 #11
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;
}
コード例 #12
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
}
コード例 #13
0
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;
}
コード例 #14
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;
}
コード例 #15
0
ファイル: checkpoint.c プロジェクト: hugh712/Jollen
/*
 * 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;
}