diff options
Diffstat (limited to 'fs/jbd/checkpoint.c')
| -rw-r--r-- | fs/jbd/checkpoint.c | 31 | 
1 files changed, 24 insertions, 7 deletions
diff --git a/fs/jbd/checkpoint.c b/fs/jbd/checkpoint.c index 1bd8d4acc6f..61f32f3868c 100644 --- a/fs/jbd/checkpoint.c +++ b/fs/jbd/checkpoint.c @@ -115,7 +115,7 @@ static int __try_to_free_cp_buf(struct journal_head *jh)   */  void __log_wait_for_space(journal_t *journal)  { -	int nblocks; +	int nblocks, space_left;  	assert_spin_locked(&journal->j_state_lock);  	nblocks = jbd_space_needed(journal); @@ -128,25 +128,42 @@ void __log_wait_for_space(journal_t *journal)  		/*  		 * Test again, another process may have checkpointed while we  		 * were waiting for the checkpoint lock. If there are no -		 * outstanding transactions there is nothing to checkpoint and -		 * we can't make progress. Abort the journal in this case. +		 * transactions ready to be checkpointed, try to recover +		 * journal space by calling cleanup_journal_tail(), and if +		 * that doesn't work, by waiting for the currently committing +		 * transaction to complete.  If there is absolutely no way +		 * to make progress, this is either a BUG or corrupted +		 * filesystem, so abort the journal and leave a stack +		 * trace for forensic evidence.  		 */  		spin_lock(&journal->j_state_lock);  		spin_lock(&journal->j_list_lock);  		nblocks = jbd_space_needed(journal); -		if (__log_space_left(journal) < nblocks) { +		space_left = __log_space_left(journal); +		if (space_left < nblocks) {  			int chkpt = journal->j_checkpoint_transactions != NULL; +			tid_t tid = 0; +			if (journal->j_committing_transaction) +				tid = journal->j_committing_transaction->t_tid;  			spin_unlock(&journal->j_list_lock);  			spin_unlock(&journal->j_state_lock);  			if (chkpt) {  				log_do_checkpoint(journal); +			} else if (cleanup_journal_tail(journal) == 0) { +				/* We were able to recover space; yay! */ +				; +			} else if (tid) { +				log_wait_commit(journal, tid);  			} else { -				printk(KERN_ERR "%s: no transactions\n", -				       __func__); +				printk(KERN_ERR "%s: needed %d blocks and " +				       "only had %d space available\n", +				       __func__, nblocks, space_left); +				printk(KERN_ERR "%s: no way to get more " +				       "journal space\n", __func__); +				WARN_ON(1);  				journal_abort(journal, 0);  			} -  			spin_lock(&journal->j_state_lock);  		} else {  			spin_unlock(&journal->j_list_lock);  |