Skip to content

Commit 466d042

Browse files
Wengang-oraclegregkh
authored andcommitted
ocfs2: fix deadlock between setattr and dio_end_io_write
commit 90bd070aae6c4fb5d302f9c4b9c88be60c8197ec upstream. 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 28f5a8a7c033 ("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]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 84427f0 commit 466d042

File tree

2 files changed

+7
-12
lines changed

2 files changed

+7
-12
lines changed

fs/ocfs2/aops.c

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2301,7 +2301,7 @@ static void ocfs2_dio_end_io_write(struct inode *inode,
23012301
struct ocfs2_alloc_context *meta_ac = NULL;
23022302
handle_t *handle = NULL;
23032303
loff_t end = offset + bytes;
2304-
int ret = 0, credits = 0, locked = 0;
2304+
int ret = 0, credits = 0;
23052305

23062306
ocfs2_init_dealloc_ctxt(&dealloc);
23072307

@@ -2312,13 +2312,6 @@ static void ocfs2_dio_end_io_write(struct inode *inode,
23122312
!dwc->dw_orphaned)
23132313
goto out;
23142314

2315-
/* ocfs2_file_write_iter will get i_mutex, so we need not lock if we
2316-
* are in that context. */
2317-
if (dwc->dw_writer_pid != task_pid_nr(current)) {
2318-
inode_lock(inode);
2319-
locked = 1;
2320-
}
2321-
23222315
ret = ocfs2_inode_lock(inode, &di_bh, 1);
23232316
if (ret < 0) {
23242317
mlog_errno(ret);
@@ -2393,8 +2386,6 @@ static void 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

fs/ocfs2/file.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,22 +1236,24 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
12361236
goto bail_unlock;
12371237
}
12381238
}
1239+
down_write(&OCFS2_I(inode)->ip_alloc_sem);
12391240
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS +
12401241
2 * ocfs2_quota_trans_credits(sb));
12411242
if (IS_ERR(handle)) {
12421243
status = PTR_ERR(handle);
12431244
mlog_errno(status);
1244-
goto bail_unlock;
1245+
goto bail_unlock_alloc;
12451246
}
12461247
status = __dquot_transfer(inode, transfer_to);
12471248
if (status < 0)
12481249
goto bail_commit;
12491250
} else {
1251+
down_write(&OCFS2_I(inode)->ip_alloc_sem);
12501252
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
12511253
if (IS_ERR(handle)) {
12521254
status = PTR_ERR(handle);
12531255
mlog_errno(status);
1254-
goto bail_unlock;
1256+
goto bail_unlock_alloc;
12551257
}
12561258
}
12571259

@@ -1264,6 +1266,8 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
12641266

12651267
bail_commit:
12661268
ocfs2_commit_trans(osb, handle);
1269+
bail_unlock_alloc:
1270+
up_write(&OCFS2_I(inode)->ip_alloc_sem);
12671271
bail_unlock:
12681272
if (status) {
12691273
ocfs2_inode_unlock(inode, 1);

0 commit comments

Comments
 (0)