Exemplo n.º 1
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;
}
Exemplo n.º 2
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;
	}
}
Exemplo n.º 3
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);
}
Exemplo n.º 4
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);
}
Exemplo n.º 5
0
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);
	}
}
Exemplo n.º 6
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);
}
Exemplo n.º 7
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;
}
Exemplo n.º 8
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;
}
Exemplo n.º 9
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;
}
Exemplo n.º 10
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;
}
Exemplo n.º 11
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;
}