diff options
Diffstat (limited to 'fs/jbd/checkpoint.c')
| -rw-r--r-- | fs/jbd/checkpoint.c | 67 | 
1 files changed, 46 insertions, 21 deletions
diff --git a/fs/jbd/checkpoint.c b/fs/jbd/checkpoint.c index e4b87bc1fa5..08c03044abd 100644 --- a/fs/jbd/checkpoint.c +++ b/fs/jbd/checkpoint.c @@ -22,6 +22,8 @@  #include <linux/jbd.h>  #include <linux/errno.h>  #include <linux/slab.h> +#include <linux/blkdev.h> +#include <trace/events/jbd.h>  /*   * Unlink a buffer from a transaction checkpoint list. @@ -95,10 +97,14 @@ static int __try_to_free_cp_buf(struct journal_head *jh)  	if (jh->b_jlist == BJ_None && !buffer_locked(bh) &&  	    !buffer_dirty(bh) && !buffer_write_io_error(bh)) { +		/* +		 * Get our reference so that bh cannot be freed before +		 * we unlock it +		 */ +		get_bh(bh);  		JBUFFER_TRACE(jh, "remove from checkpoint list");  		ret = __journal_remove_checkpoint(jh) + 1;  		jbd_unlock_bh_state(bh); -		journal_remove_journal_head(bh);  		BUFFER_TRACE(bh, "release");  		__brelse(bh);  	} else { @@ -220,8 +226,8 @@ restart:  			spin_lock(&journal->j_list_lock);  			goto restart;  		} +		get_bh(bh);  		if (buffer_locked(bh)) { -			get_bh(bh);  			spin_unlock(&journal->j_list_lock);  			jbd_unlock_bh_state(bh);  			wait_on_buffer(bh); @@ -240,7 +246,6 @@ restart:  		 */  		released = __journal_remove_checkpoint(jh);  		jbd_unlock_bh_state(bh); -		journal_remove_journal_head(bh);  		__brelse(bh);  	} @@ -253,9 +258,12 @@ static void  __flush_batch(journal_t *journal, struct buffer_head **bhs, int *batch_count)  {  	int i; +	struct blk_plug plug; +	blk_start_plug(&plug);  	for (i = 0; i < *batch_count; i++) -		write_dirty_buffer(bhs[i], WRITE); +		write_dirty_buffer(bhs[i], WRITE_SYNC); +	blk_finish_plug(&plug);  	for (i = 0; i < *batch_count; i++) {  		struct buffer_head *bh = bhs[i]; @@ -304,12 +312,12 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh,  		ret = 1;  		if (unlikely(buffer_write_io_error(bh)))  			ret = -EIO; +		get_bh(bh);  		J_ASSERT_JH(jh, !buffer_jbddirty(bh));  		BUFFER_TRACE(bh, "remove from checkpoint");  		__journal_remove_checkpoint(jh);  		spin_unlock(&journal->j_list_lock);  		jbd_unlock_bh_state(bh); -		journal_remove_journal_head(bh);  		__brelse(bh);  	} else {  		/* @@ -358,6 +366,7 @@ int log_do_checkpoint(journal_t *journal)  	 * journal straight away.  	 */  	result = cleanup_journal_tail(journal); +	trace_jbd_checkpoint(journal, result);  	jbd_debug(1, "cleanup_journal_tail returned %d\n", result);  	if (result <= 0)  		return result; @@ -444,8 +453,6 @@ out:   *   * Return <0 on error, 0 on success, 1 if there was nothing to clean up.   * - * Called with the journal lock held. - *   * This is the only part of the journaling code which really needs to be   * aware of transaction aborts.  Checkpointing involves writing to the   * main filesystem area rather than to the journal, so it can proceed @@ -463,13 +470,14 @@ int cleanup_journal_tail(journal_t *journal)  	if (is_journal_aborted(journal))  		return 1; -	/* OK, work out the oldest transaction remaining in the log, and +	/* +	 * OK, work out the oldest transaction remaining in the log, and  	 * the log block it starts at.  	 *  	 * If the log is now empty, we need to work out which is the  	 * next transaction ID we will write, and where it will -	 * start. */ - +	 * start. +	 */  	spin_lock(&journal->j_state_lock);  	spin_lock(&journal->j_list_lock);  	transaction = journal->j_checkpoint_transactions; @@ -495,7 +503,24 @@ int cleanup_journal_tail(journal_t *journal)  		spin_unlock(&journal->j_state_lock);  		return 1;  	} +	spin_unlock(&journal->j_state_lock); + +	/* +	 * We need to make sure that any blocks that were recently written out +	 * --- perhaps by log_do_checkpoint() --- are flushed out before we +	 * drop the transactions from the journal. Similarly we need to be sure +	 * superblock makes it to disk before next transaction starts reusing +	 * freed space (otherwise we could replay some blocks of the new +	 * transaction thinking they belong to the old one). So we use +	 * WRITE_FLUSH_FUA. It's unlikely this will be necessary, especially +	 * with an appropriately sized journal, but we need this to guarantee +	 * correctness.  Fortunately cleanup_journal_tail() doesn't get called +	 * all that often. +	 */ +	journal_update_sb_log_tail(journal, first_tid, blocknr, +				   WRITE_FLUSH_FUA); +	spin_lock(&journal->j_state_lock);  	/* OK, update the superblock to recover the freed space.  	 * Physical blocks come first: have we wrapped beyond the end of  	 * the log?  */ @@ -503,6 +528,7 @@ int cleanup_journal_tail(journal_t *journal)  	if (blocknr < journal->j_tail)  		freed = freed + journal->j_last - journal->j_first; +	trace_jbd_cleanup_journal_tail(journal, first_tid, blocknr, freed);  	jbd_debug(1,  		  "Cleaning journal tail from %d to %d (offset %u), "  		  "freeing %u\n", @@ -512,8 +538,6 @@ int cleanup_journal_tail(journal_t *journal)  	journal->j_tail_sequence = first_tid;  	journal->j_tail = blocknr;  	spin_unlock(&journal->j_state_lock); -	if (!(journal->j_flags & JFS_ABORT)) -		journal_update_superblock(journal, 1);  	return 0;  } @@ -523,11 +547,11 @@ int cleanup_journal_tail(journal_t *journal)  /*   * journal_clean_one_cp_list   * - * Find all the written-back checkpoint buffers in the given list and release them. + * Find all the written-back checkpoint buffers in the given list and release + * them.   * - * Called with the journal locked.   * Called with j_list_lock held. - * Returns number of bufers reaped (for debug) + * Returns number of buffers reaped (for debug)   */  static int journal_clean_one_cp_list(struct journal_head *jh, int *released) @@ -632,8 +656,8 @@ out:   * checkpoint lists.   *   * The function returns 1 if it frees the transaction, 0 otherwise. + * The function can free jh and bh.   * - * This function is called with the journal locked.   * This function is called with j_list_lock held.   * This function is called with jbd_lock_bh_state(jh2bh(jh))   */ @@ -652,13 +676,14 @@ int __journal_remove_checkpoint(struct journal_head *jh)  	}  	journal = transaction->t_journal; +	JBUFFER_TRACE(jh, "removing from transaction");  	__buffer_unlink(jh);  	jh->b_cp_transaction = NULL; +	journal_put_journal_head(jh);  	if (transaction->t_checkpoint_list != NULL ||  	    transaction->t_checkpoint_io_list != NULL)  		goto out; -	JBUFFER_TRACE(jh, "transaction has no more buffers");  	/*  	 * There is one special case to worry about: if we have just pulled the @@ -669,10 +694,8 @@ int __journal_remove_checkpoint(struct journal_head *jh)  	 * The locking here around t_state is a bit sleazy.  	 * See the comment at the end of journal_commit_transaction().  	 */ -	if (transaction->t_state != T_FINISHED) { -		JBUFFER_TRACE(jh, "belongs to running/committing transaction"); +	if (transaction->t_state != T_FINISHED)  		goto out; -	}  	/* OK, that was the last buffer for the transaction: we can now  	   safely remove this transaction from the log */ @@ -684,7 +707,6 @@ int __journal_remove_checkpoint(struct journal_head *jh)  	wake_up(&journal->j_wait_logspace);  	ret = 1;  out: -	JBUFFER_TRACE(jh, "exit");  	return ret;  } @@ -703,6 +725,8 @@ void __journal_insert_checkpoint(struct journal_head *jh,  	J_ASSERT_JH(jh, buffer_dirty(jh2bh(jh)) || buffer_jbddirty(jh2bh(jh)));  	J_ASSERT_JH(jh, jh->b_cp_transaction == NULL); +	/* Get reference for checkpointing transaction */ +	journal_grab_journal_head(jh2bh(jh));  	jh->b_cp_transaction = transaction;  	if (!transaction->t_checkpoint_list) { @@ -752,6 +776,7 @@ void __journal_drop_transaction(journal_t *journal, transaction_t *transaction)  	J_ASSERT(journal->j_committing_transaction != transaction);  	J_ASSERT(journal->j_running_transaction != transaction); +	trace_jbd_drop_transaction(journal, transaction);  	jbd_debug(1, "Dropping transaction %d, all done\n", transaction->t_tid);  	kfree(transaction);  }  | 
