diff options
Diffstat (limited to 'fs/jbd/commit.c')
| -rw-r--r-- | fs/jbd/commit.c | 64 | 
1 files changed, 48 insertions, 16 deletions
| diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c index 5a8ca61498ca..2eccbfaa1d48 100644 --- a/fs/jbd/commit.c +++ b/fs/jbd/commit.c @@ -36,7 +36,7 @@ static void journal_end_buffer_io_sync(struct buffer_head *bh, int uptodate)  /*   * When an ext3-ordered file is truncated, it is possible that many pages are - * not sucessfully freed, because they are attached to a committing transaction. + * not successfully freed, because they are attached to a committing transaction.   * After the transaction commits, these pages are left on the LRU, with no   * ->mapping, and with attached buffers.  These pages are trivially reclaimable   * by the VM, but their apparent absence upsets the VM accounting, and it makes @@ -45,8 +45,8 @@ static void journal_end_buffer_io_sync(struct buffer_head *bh, int uptodate)   * So here, we have a buffer which has just come off the forget list.  Look to   * see if we can strip all buffers from the backing page.   * - * Called under lock_journal(), and possibly under journal_datalist_lock.  The - * caller provided us with a ref against the buffer, and we drop that here. + * Called under journal->j_list_lock.  The caller provided us with a ref + * against the buffer, and we drop that here.   */  static void release_buffer_page(struct buffer_head *bh)  { @@ -78,6 +78,19 @@ nope:  }  /* + * Decrement reference counter for data buffer. If it has been marked + * 'BH_Freed', release it and the page to which it belongs if possible. + */ +static void release_data_buffer(struct buffer_head *bh) +{ +	if (buffer_freed(bh)) { +		clear_buffer_freed(bh); +		release_buffer_page(bh); +	} else +		put_bh(bh); +} + +/*   * Try to acquire jbd_lock_bh_state() against the buffer, when j_list_lock is   * held.  For ranking reasons we must trylock.  If we lose, schedule away and   * return 0.  j_list_lock is dropped in this case. @@ -172,7 +185,7 @@ static void journal_do_submit_data(struct buffer_head **wbuf, int bufs)  /*   *  Submit all the data buffers to disk   */ -static void journal_submit_data_buffers(journal_t *journal, +static int journal_submit_data_buffers(journal_t *journal,  				transaction_t *commit_transaction)  {  	struct journal_head *jh; @@ -180,6 +193,7 @@ static void journal_submit_data_buffers(journal_t *journal,  	int locked;  	int bufs = 0;  	struct buffer_head **wbuf = journal->j_wbuf; +	int err = 0;  	/*  	 * Whenever we unlock the journal and sleep, things can get added @@ -231,7 +245,7 @@ write_out_data:  			if (locked)  				unlock_buffer(bh);  			BUFFER_TRACE(bh, "already cleaned up"); -			put_bh(bh); +			release_data_buffer(bh);  			continue;  		}  		if (locked && test_clear_buffer_dirty(bh)) { @@ -253,15 +267,17 @@ write_out_data:  			put_bh(bh);  		} else {  			BUFFER_TRACE(bh, "writeout complete: unfile"); +			if (unlikely(!buffer_uptodate(bh))) +				err = -EIO;  			__journal_unfile_buffer(jh);  			jbd_unlock_bh_state(bh);  			if (locked)  				unlock_buffer(bh);  			journal_remove_journal_head(bh); -			/* Once for our safety reference, once for +			/* One for our safety reference, other for  			 * journal_remove_journal_head() */  			put_bh(bh); -			put_bh(bh); +			release_data_buffer(bh);  		}  		if (need_resched() || spin_needbreak(&journal->j_list_lock)) { @@ -271,6 +287,8 @@ write_out_data:  	}  	spin_unlock(&journal->j_list_lock);  	journal_do_submit_data(wbuf, bufs); + +	return err;  }  /* @@ -410,8 +428,7 @@ void journal_commit_transaction(journal_t *journal)  	 * Now start flushing things to disk, in the order they appear  	 * on the transaction lists.  Data blocks go first.  	 */ -	err = 0; -	journal_submit_data_buffers(journal, commit_transaction); +	err = journal_submit_data_buffers(journal, commit_transaction);  	/*  	 * Wait for all previously submitted IO to complete. @@ -426,10 +443,21 @@ void journal_commit_transaction(journal_t *journal)  		if (buffer_locked(bh)) {  			spin_unlock(&journal->j_list_lock);  			wait_on_buffer(bh); -			if (unlikely(!buffer_uptodate(bh))) -				err = -EIO;  			spin_lock(&journal->j_list_lock);  		} +		if (unlikely(!buffer_uptodate(bh))) { +			if (TestSetPageLocked(bh->b_page)) { +				spin_unlock(&journal->j_list_lock); +				lock_page(bh->b_page); +				spin_lock(&journal->j_list_lock); +			} +			if (bh->b_page->mapping) +				set_bit(AS_EIO, &bh->b_page->mapping->flags); + +			unlock_page(bh->b_page); +			SetPageError(bh->b_page); +			err = -EIO; +		}  		if (!inverted_lock(journal, bh)) {  			put_bh(bh);  			spin_lock(&journal->j_list_lock); @@ -443,17 +471,21 @@ void journal_commit_transaction(journal_t *journal)  		} else {  			jbd_unlock_bh_state(bh);  		} -		put_bh(bh); +		release_data_buffer(bh);  		cond_resched_lock(&journal->j_list_lock);  	}  	spin_unlock(&journal->j_list_lock); -	if (err) -		journal_abort(journal, err); +	if (err) { +		char b[BDEVNAME_SIZE]; -	journal_write_revoke_records(journal, commit_transaction); +		printk(KERN_WARNING +			"JBD: Detected IO errors while flushing file data " +			"on %s\n", bdevname(journal->j_fs_dev, b)); +		err = 0; +	} -	jbd_debug(3, "JBD: commit phase 2\n"); +	journal_write_revoke_records(journal, commit_transaction);  	/*  	 * If we found any dirty or locked buffers, then we should have | 
