diff options
Diffstat (limited to 'fs/jbd/commit.c')
| -rw-r--r-- | fs/jbd/commit.c | 45 | 
1 files changed, 34 insertions, 11 deletions
diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c index 52c15c77602..86b39b167c2 100644 --- a/fs/jbd/commit.c +++ b/fs/jbd/commit.c @@ -86,7 +86,12 @@ nope:  static void release_data_buffer(struct buffer_head *bh)  {  	if (buffer_freed(bh)) { +		WARN_ON_ONCE(buffer_dirty(bh));  		clear_buffer_freed(bh); +		clear_buffer_mapped(bh); +		clear_buffer_new(bh); +		clear_buffer_req(bh); +		bh->b_bdev = NULL;  		release_buffer_page(bh);  	} else  		put_bh(bh); @@ -866,17 +871,35 @@ restart_loop:  		 * there's no point in keeping a checkpoint record for  		 * it. */ -		/* A buffer which has been freed while still being -		 * journaled by a previous transaction may end up still -		 * being dirty here, but we want to avoid writing back -		 * that buffer in the future after the "add to orphan" -		 * operation been committed,  That's not only a performance -		 * gain, it also stops aliasing problems if the buffer is -		 * left behind for writeback and gets reallocated for another -		 * use in a different page. */ -		if (buffer_freed(bh) && !jh->b_next_transaction) { -			clear_buffer_freed(bh); -			clear_buffer_jbddirty(bh); +		/* +		 * A buffer which has been freed while still being journaled by +		 * a previous transaction. +		 */ +		if (buffer_freed(bh)) { +			/* +			 * If the running transaction is the one containing +			 * "add to orphan" operation (b_next_transaction != +			 * NULL), we have to wait for that transaction to +			 * commit before we can really get rid of the buffer. +			 * So just clear b_modified to not confuse transaction +			 * credit accounting and refile the buffer to +			 * BJ_Forget of the running transaction. If the just +			 * committed transaction contains "add to orphan" +			 * operation, we can completely invalidate the buffer +			 * now. We are rather throughout in that since the +			 * buffer may be still accessible when blocksize < +			 * pagesize and it is attached to the last partial +			 * page. +			 */ +			jh->b_modified = 0; +			if (!jh->b_next_transaction) { +				clear_buffer_freed(bh); +				clear_buffer_jbddirty(bh); +				clear_buffer_mapped(bh); +				clear_buffer_new(bh); +				clear_buffer_req(bh); +				bh->b_bdev = NULL; +			}  		}  		if (buffer_jbddirty(bh)) {  |