Skip to content

Commit 90bd070

Browse files
Wengang-oracletorvalds
authored andcommitted
ocfs2: fix deadlock between setattr and dio_end_io_write
The following deadlock is detected: truncate -> setattr path is waiting for pending direct IO to be done (inode->i_dio_count become zero) with inode->i_rwsem held (down_write). PID: 14827 TASK: ffff881686a9af80 CPU: 20 COMMAND: "ora_p005_hrltd9" #0 __schedule at ffffffff818667cc #1 schedule at ffffffff81866de6 #2 inode_dio_wait at ffffffff812a2d04 #3 ocfs2_setattr at ffffffffc05f322e [ocfs2] #4 notify_change at ffffffff812a5a09 #5 do_truncate at ffffffff812808f5 #6 do_sys_ftruncate.constprop.18 at ffffffff81280cf2 #7 sys_ftruncate at ffffffff81280d8e #8 do_syscall_64 at ffffffff81003949 #9 entry_SYSCALL_64_after_hwframe at ffffffff81a001ad dio completion path is going to complete one direct IO (decrement inode->i_dio_count), but before that it hung at locking inode->i_rwsem: #0 __schedule+700 at ffffffff818667cc #1 schedule+54 at ffffffff81866de6 #2 rwsem_down_write_failed+536 at ffffffff8186aa28 #3 call_rwsem_down_write_failed+23 at ffffffff8185a1b7 #4 down_write+45 at ffffffff81869c9d #5 ocfs2_dio_end_io_write+180 at ffffffffc05d5444 [ocfs2] #6 ocfs2_dio_end_io+85 at ffffffffc05d5a85 [ocfs2] #7 dio_complete+140 at ffffffff812c873c #8 dio_aio_complete_work+25 at ffffffff812c89f9 #9 process_one_work+361 at ffffffff810b1889 #10 worker_thread+77 at ffffffff810b233d #11 kthread+261 at ffffffff810b7fd5 #12 ret_from_fork+62 at ffffffff81a0035e Thus above forms ABBA deadlock. The same deadlock was mentioned in upstream commit 28f5a8a ("ocfs2: should wait dio before inode lock in ocfs2_setattr()"). It seems that that commit only removed the cluster lock (the victim of above dead lock) from the ABBA deadlock party. End-user visible effects: Process hang in truncate -> ocfs2_setattr path and other processes hang at ocfs2_dio_end_io_write path. This is to fix the deadlock itself. It removes inode_lock() call from dio completion path to remove the deadlock and add ip_alloc_sem lock in setattr path to synchronize the inode modifications. [[email protected]: remove the "had_alloc_lock" as suggested] Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Wengang Wang <[email protected]> Reviewed-by: Joseph Qi <[email protected]> Cc: Mark Fasheh <[email protected]> Cc: Joel Becker <[email protected]> Cc: Junxiao Bi <[email protected]> Cc: Changwei Ge <[email protected]> Cc: Gang He <[email protected]> Cc: Jun Piao <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 9562fd1 commit 90bd070

File tree

2 files changed

+7
-12
lines changed

2 files changed

+7
-12
lines changed

fs/ocfs2/aops.c

+1-10
Original file line numberDiff line numberDiff line change
@@ -2295,7 +2295,7 @@ static int ocfs2_dio_end_io_write(struct inode *inode,
22952295
struct ocfs2_alloc_context *meta_ac = NULL;
22962296
handle_t *handle = NULL;
22972297
loff_t end = offset + bytes;
2298-
int ret = 0, credits = 0, locked = 0;
2298+
int ret = 0, credits = 0;
22992299

23002300
ocfs2_init_dealloc_ctxt(&dealloc);
23012301

@@ -2306,13 +2306,6 @@ static int ocfs2_dio_end_io_write(struct inode *inode,
23062306
!dwc->dw_orphaned)
23072307
goto out;
23082308

2309-
/* ocfs2_file_write_iter will get i_mutex, so we need not lock if we
2310-
* are in that context. */
2311-
if (dwc->dw_writer_pid != task_pid_nr(current)) {
2312-
inode_lock(inode);
2313-
locked = 1;
2314-
}
2315-
23162309
ret = ocfs2_inode_lock(inode, &di_bh, 1);
23172310
if (ret < 0) {
23182311
mlog_errno(ret);
@@ -2393,8 +2386,6 @@ static int ocfs2_dio_end_io_write(struct inode *inode,
23932386
if (meta_ac)
23942387
ocfs2_free_alloc_context(meta_ac);
23952388
ocfs2_run_deallocs(osb, &dealloc);
2396-
if (locked)
2397-
inode_unlock(inode);
23982389
ocfs2_dio_free_write_ctx(inode, dwc);
23992390

24002391
return ret;

fs/ocfs2/file.c

+6-2
Original file line numberDiff line numberDiff line change
@@ -1245,22 +1245,24 @@ int ocfs2_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
12451245
goto bail_unlock;
12461246
}
12471247
}
1248+
down_write(&OCFS2_I(inode)->ip_alloc_sem);
12481249
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS +
12491250
2 * ocfs2_quota_trans_credits(sb));
12501251
if (IS_ERR(handle)) {
12511252
status = PTR_ERR(handle);
12521253
mlog_errno(status);
1253-
goto bail_unlock;
1254+
goto bail_unlock_alloc;
12541255
}
12551256
status = __dquot_transfer(inode, transfer_to);
12561257
if (status < 0)
12571258
goto bail_commit;
12581259
} else {
1260+
down_write(&OCFS2_I(inode)->ip_alloc_sem);
12591261
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
12601262
if (IS_ERR(handle)) {
12611263
status = PTR_ERR(handle);
12621264
mlog_errno(status);
1263-
goto bail_unlock;
1265+
goto bail_unlock_alloc;
12641266
}
12651267
}
12661268

@@ -1273,6 +1275,8 @@ int ocfs2_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
12731275

12741276
bail_commit:
12751277
ocfs2_commit_trans(osb, handle);
1278+
bail_unlock_alloc:
1279+
up_write(&OCFS2_I(inode)->ip_alloc_sem);
12761280
bail_unlock:
12771281
if (status && inode_locked) {
12781282
ocfs2_inode_unlock_tracker(inode, 1, &oh, had_lock);

0 commit comments

Comments
 (0)