[Ocfs2-devel] [PATCH] ocfs2: fix deadlock due to wrong locking order

Junxiao Bi junxiao.bi at oracle.com
Wed Sep 24 23:39:15 PDT 2014


Hi Alex,

On 09/24/2014 05:41 PM, alex chen wrote:
> Hi Junxiao,
> 
> On 2014/9/10 9:48, Junxiao Bi wrote:
>> For commit ocfs2 journal, ocfs2 journal thread will acquire the mutex
>> osb->journal->j_trans_barrier and wake up jbd2 commit thread, then it
>> will wait until jbd2 commit thread done. In order journal mode, jbd2
>> needs flushing dirty data pages first, and this needs get page lock.
>> So osb->journal->j_trans_barrier should be got before page lock.
>>
>> But ocfs2_write_zero_page() and ocfs2_write_begin_inline() obey this
>> locking order, and this will cause deadlock and hung the whole cluster.
>>
>> One deadlock catched is the following:
>>
>> PID: 13449  TASK: ffff8802e2f08180  CPU: 31  COMMAND: "oracle"
>>  #0 [ffff8802ee3f79b0] __schedule at ffffffff8150a524
>>  #1 [ffff8802ee3f7a58] schedule at ffffffff8150acbf
>>  #2 [ffff8802ee3f7a68] rwsem_down_failed_common at ffffffff8150cb85
>>  #3 [ffff8802ee3f7ad8] rwsem_down_read_failed at ffffffff8150cc55
>>  #4 [ffff8802ee3f7ae8] call_rwsem_down_read_failed at ffffffff812617a4
>>  #5 [ffff8802ee3f7b50] ocfs2_start_trans at ffffffffa0498919 [ocfs2]
>>  #6 [ffff8802ee3f7ba0] ocfs2_zero_start_ordered_transaction at ffffffffa048b2b8 [ocfs2]
>>  #7 [ffff8802ee3f7bf0] ocfs2_write_zero_page at ffffffffa048e9bd [ocfs2]
>>  #8 [ffff8802ee3f7c80] ocfs2_zero_extend_range at ffffffffa048ec83 [ocfs2]
>>  #9 [ffff8802ee3f7ce0] ocfs2_zero_extend at ffffffffa048edfd [ocfs2]
>>  #10 [ffff8802ee3f7d50] ocfs2_extend_file at ffffffffa049079e [ocfs2]
>>  #11 [ffff8802ee3f7da0] ocfs2_setattr at ffffffffa04910ed [ocfs2]
>>  #12 [ffff8802ee3f7e70] notify_change at ffffffff81187d29
>>  #13 [ffff8802ee3f7ee0] do_truncate at ffffffff8116bbc1
>>  #14 [ffff8802ee3f7f50] sys_ftruncate at ffffffff8116bcbd
>>  #15 [ffff8802ee3f7f80] system_call_fastpath at ffffffff81515142
>>     RIP: 00007f8de750c6f7  RSP: 00007fffe786e478  RFLAGS: 00000206
>>     RAX: 000000000000004d  RBX: ffffffff81515142  RCX: 0000000000000000
>>     RDX: 0000000000000200  RSI: 0000000000028400  RDI: 000000000000000d
>>     RBP: 00007fffe786e040   R8: 0000000000000000   R9: 000000000000000d
>>     R10: 0000000000000000  R11: 0000000000000206  R12: 000000000000000d
>>     R13: 00007fffe786e710  R14: 00007f8de70f8340  R15: 0000000000028400
>>     ORIG_RAX: 000000000000004d  CS: 0033  SS: 002b
>>
>> crash64> bt
>> PID: 7610   TASK: ffff88100fd56140  CPU: 1   COMMAND: "ocfs2cmt"
>>  #0 [ffff88100f4d1c50] __schedule at ffffffff8150a524
>>  #1 [ffff88100f4d1cf8] schedule at ffffffff8150acbf
>>  #2 [ffff88100f4d1d08] jbd2_log_wait_commit at ffffffffa01274fd [jbd2]
>>  #3 [ffff88100f4d1d98] jbd2_journal_flush at ffffffffa01280b4 [jbd2]
>>  #4 [ffff88100f4d1dd8] ocfs2_commit_cache at ffffffffa0499b14 [ocfs2]
>>  #5 [ffff88100f4d1e38] ocfs2_commit_thread at ffffffffa0499d38 [ocfs2]
>>  #6 [ffff88100f4d1ee8] kthread at ffffffff81090db6
>>  #7 [ffff88100f4d1f48] kernel_thread_helper at ffffffff81516284
>>
>> crash64> bt
>> PID: 7609   TASK: ffff88100f2d4480  CPU: 0   COMMAND: "jbd2/dm-20-86"
>>  #0 [ffff88100def3920] __schedule at ffffffff8150a524
>>  #1 [ffff88100def39c8] schedule at ffffffff8150acbf
>>  #2 [ffff88100def39d8] io_schedule at ffffffff8150ad6c
>>  #3 [ffff88100def39f8] sleep_on_page at ffffffff8111069e
>>  #4 [ffff88100def3a08] __wait_on_bit_lock at ffffffff8150b30a
>>  #5 [ffff88100def3a58] __lock_page at ffffffff81110687
>>  #6 [ffff88100def3ab8] write_cache_pages at ffffffff8111b752
>>  #7 [ffff88100def3be8] generic_writepages at ffffffff8111b901
>>  #8 [ffff88100def3c48] journal_submit_data_buffers at ffffffffa0120f67 [jbd2]
>>  #9 [ffff88100def3cf8] jbd2_journal_commit_transaction at ffffffffa0121372[jbd2]
>>  #10 [ffff88100def3e68] kjournald2 at ffffffffa0127a86 [jbd2]
>>  #11 [ffff88100def3ee8] kthread at ffffffff81090db6
>>  #12 [ffff88100def3f48] kernel_thread_helper at ffffffff81516284
>>
>> Signed-off-by: Junxiao Bi <junxiao.bi at oracle.com>
>> ---
>>  fs/ocfs2/aops.c |   15 ++++++++-------
>>  fs/ocfs2/file.c |   52 ++++++++++++++++++++++++----------------------------
>>  2 files changed, 32 insertions(+), 35 deletions(-)
>>
>> diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
>> index c04183b..c71174a 100644
>> --- a/fs/ocfs2/aops.c
>> +++ b/fs/ocfs2/aops.c
>> @@ -1485,8 +1485,16 @@ static int ocfs2_write_begin_inline(struct address_space *mapping,
>>  	handle_t *handle;
>>  	struct ocfs2_dinode *di = (struct ocfs2_dinode *)wc->w_di_bh->b_data;
>>  
>> +	handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
>> +	if (IS_ERR(handle)) {
>> +		ret = PTR_ERR(handle);
>> +		mlog_errno(ret);
>> +		goto out;
>> +	}
>> +
>>  	page = find_or_create_page(mapping, 0, GFP_NOFS);
>>  	if (!page) {
>> +		ocfs2_commit_trans(osb, handle);
>>  		ret = -ENOMEM;
>>  		mlog_errno(ret);
>>  		goto out;
>> @@ -1498,13 +1506,6 @@ static int ocfs2_write_begin_inline(struct address_space *mapping,
>>  	wc->w_pages[0] = wc->w_target_page = page;
>>  	wc->w_num_pages = 1;
>>  
>> -	handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
>> -	if (IS_ERR(handle)) {
>> -		ret = PTR_ERR(handle);
>> -		mlog_errno(ret);
>> -		goto out;
>> -	}
>> -
>>  	ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode), wc->w_di_bh,
>>  				      OCFS2_JOURNAL_ACCESS_WRITE);
>>  	if (ret) {
>> diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
>> index 2930e23..c534cb0 100644
>> --- a/fs/ocfs2/file.c
>> +++ b/fs/ocfs2/file.c
>> @@ -760,7 +760,7 @@ static int ocfs2_write_zero_page(struct inode *inode, u64 abs_from,
>>  	struct address_space *mapping = inode->i_mapping;
>>  	struct page *page;
>>  	unsigned long index = abs_from >> PAGE_CACHE_SHIFT;
>> -	handle_t *handle = NULL;
>> +	handle_t *handle;
>>  	int ret = 0;
>>  	unsigned zero_from, zero_to, block_start, block_end;
>>  	struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
>> @@ -769,11 +769,17 @@ static int ocfs2_write_zero_page(struct inode *inode, u64 abs_from,
>>  	BUG_ON(abs_to > (((u64)index + 1) << PAGE_CACHE_SHIFT));
>>  	BUG_ON(abs_from & (inode->i_blkbits - 1));
>>  
>> +	handle = ocfs2_zero_start_ordered_transaction(inode, di_bh);
>> +	if (IS_ERR(handle)) {
>> +		ret = PTR_ERR(handle);
>> +		goto out;
>> +	}
>> +
> 
> The ocfs2_zero_start_ordered_transaction may return NULL.

Yes, this should be fixed. How about the following fix?

diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index c534cb0..682732f 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -833,14 +833,17 @@ static int ocfs2_write_zero_page(struct inode
*inode, u64 abs_from,
        di->i_mtime = di->i_ctime = cpu_to_le64(inode->i_mtime.tv_sec);
        di->i_ctime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec);
        di->i_mtime_nsec = di->i_ctime_nsec;
-       ocfs2_journal_dirty(handle, di_bh);
-       ocfs2_update_inode_fsync_trans(handle, inode, 1);
+       if (handle) {
+               ocfs2_journal_dirty(handle, di_bh);
+               ocfs2_update_inode_fsync_trans(handle, inode, 1);
+       }

 out_unlock:
        unlock_page(page);
        page_cache_release(page);
 out_commit_trans:
-       ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
+       if (handle)
+               ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
 out:
        return ret;
 }


Thanks,
Junxiao.
> 
>>  	page = find_or_create_page(mapping, index, GFP_NOFS);
>>  	if (!page) {
>>  		ret = -ENOMEM;
>>  		mlog_errno(ret);
>> -		goto out;
>> +		goto out_commit_trans;
>>  	}
>>  
>>  	/* Get the offsets within the page that we want to zero */
>> @@ -805,15 +811,6 @@ static int ocfs2_write_zero_page(struct inode *inode, u64 abs_from,
>>  			goto out_unlock;
>>  		}
>>  
>> -		if (!handle) {
>> -			handle = ocfs2_zero_start_ordered_transaction(inode,
>> -								      di_bh);
>> -			if (IS_ERR(handle)) {
>> -				ret = PTR_ERR(handle);
>> -				handle = NULL;
>> -				break;
>> -			}
>> -		}
>>  
>>  		/* must not update i_size! */
>>  		ret = block_commit_write(page, block_start + 1,
>> @@ -824,27 +821,26 @@ static int ocfs2_write_zero_page(struct inode *inode, u64 abs_from,
>>  			ret = 0;
>>  	}
>>  
>> -	if (handle) {
>> -		/*
>> -		 * fs-writeback will release the dirty pages without page lock
>> -		 * whose offset are over inode size, the release happens at
>> -		 * block_write_full_page().
>> -		 */
>> -		i_size_write(inode, abs_to);
>> -		inode->i_blocks = ocfs2_inode_sector_count(inode);
>> -		di->i_size = cpu_to_le64((u64)i_size_read(inode));
>> -		inode->i_mtime = inode->i_ctime = CURRENT_TIME;
>> -		di->i_mtime = di->i_ctime = cpu_to_le64(inode->i_mtime.tv_sec);
>> -		di->i_ctime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec);
>> -		di->i_mtime_nsec = di->i_ctime_nsec;
>> -		ocfs2_journal_dirty(handle, di_bh);
>> -		ocfs2_update_inode_fsync_trans(handle, inode, 1);
>> -		ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
>> -	}
>> +	/*
>> +	 * fs-writeback will release the dirty pages without page lock
>> +	 * whose offset are over inode size, the release happens at
>> +	 * block_write_full_page().
>> +	 */
>> +	i_size_write(inode, abs_to);
>> +	inode->i_blocks = ocfs2_inode_sector_count(inode);
>> +	di->i_size = cpu_to_le64((u64)i_size_read(inode));
>> +	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
>> +	di->i_mtime = di->i_ctime = cpu_to_le64(inode->i_mtime.tv_sec);
>> +	di->i_ctime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec);
>> +	di->i_mtime_nsec = di->i_ctime_nsec;
>> +	ocfs2_journal_dirty(handle, di_bh);
>> +	ocfs2_update_inode_fsync_trans(handle, inode, 1);
> 
> if ocfs2_zero_start_ordered_transaction return NULL, here will BUG.
> so should judge if handle is NULL.
> 
>>  
>>  out_unlock:
>>  	unlock_page(page);
>>  	page_cache_release(page);
>> +out_commit_trans:
>> +	ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
> 
> if ocfs2_zero_start_ordered_transaction return NULL, here will BUG.
> so should judge if handle is NULL.
> 
>>  out:
>>  	return ret;
>>  }
>>
> 




More information about the Ocfs2-devel mailing list