Skip to content

Commit 685c64f

Browse files
Wengang-oraclesfrothwell
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 [ffffc9000bcf3c08] __schedule at ffffffff818667cc #1 [ffffc9000bcf3ca0] schedule at ffffffff81866de6 #2 [ffffc9000bcf3cb8] inode_dio_wait at ffffffff812a2d04 #3 [ffffc9000bcf3d28] ocfs2_setattr at ffffffffc05f322e [ocfs2] #4 [ffffc9000bcf3e18] notify_change at ffffffff812a5a09 #5 [ffffc9000bcf3e60] do_truncate at ffffffff812808f5 #6 [ffffc9000bcf3ed8] do_sys_ftruncate.constprop.18 at ffffffff81280cf2 #7 [ffffc9000bcf3f18] sys_ftruncate at ffffffff81280d8e #8 [ffffc9000bcf3f28] do_syscall_64 at ffffffff81003949 #9 [ffffc9000bcf3f50] 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 hang at locking inode->i_rwsem. #0 [ffffc90009b47b40] __schedule+700 at ffffffff818667cc #1 [ffffc90009b47bd8] schedule+54 at ffffffff81866de6 #2 [ffffc90009b47bf0] rwsem_down_write_failed+536 at ffffffff8186aa28 #3 [ffffc90009b47c88] call_rwsem_down_write_failed+23 at ffffffff8185a1b7 #4 [ffffc90009b47cd0] down_write+45 at ffffffff81869c9d #5 [ffffc90009b47ce8] ocfs2_dio_end_io_write+180 at ffffffffc05d5444 [ocfs2] #6 [ffffc90009b47dd8] ocfs2_dio_end_io+85 at ffffffffc05d5a85 [ocfs2] #7 [ffffc90009b47e18] dio_complete+140 at ffffffff812c873c #8 [ffffc90009b47e50] dio_aio_complete_work+25 at ffffffff812c89f9 #9 [ffffc90009b47e60] process_one_work+361 at ffffffff810b1889 #10 [ffffc90009b47ea8] worker_thread+77 at ffffffff810b233d #11 [ffffc90009b47f08] kthread+261 at ffffffff810b7fd5 #12 [ffffc90009b47f50] ret_from_fork+62 at ffffffff81a0035e Thus above forms ABBA deadlock. The same deadlock was mentioned in upstream commit 28f5a8a. well, it seems that that commit just removed 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 its self. 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. Link: https://lkml.kernel.org/r/20210331203654.3911-1-wen.gang.wang@oracle.com Signed-off-by: Wengang Wang <wen.gang.wang@oracle.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Junxiao Bi <junxiao.bi@oracle.com> Cc: Joseph Qi <jiangqi903@gmail.com> Cc: Changwei Ge <gechangwei@live.cn> Cc: Gang He <ghe@suse.com> Cc: Jun Piao <piaojun@huawei.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
1 parent 969f31e commit 685c64f

File tree

2 files changed

+11
-11
lines changed

2 files changed

+11
-11
lines changed

fs/ocfs2/aops.c

Lines changed: 1 addition & 10 deletions
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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1124,7 +1124,7 @@ int ocfs2_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
11241124
handle_t *handle = NULL;
11251125
struct dquot *transfer_to[MAXQUOTAS] = { };
11261126
int qtype;
1127-
int had_lock;
1127+
int had_lock, had_alloc_lock = 0;
11281128
struct ocfs2_lock_holder oh;
11291129

11301130
trace_ocfs2_setattr(inode, dentry,
@@ -1245,6 +1245,9 @@ 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);
1249+
had_alloc_lock = 1;
1250+
12481251
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS +
12491252
2 * ocfs2_quota_trans_credits(sb));
12501253
if (IS_ERR(handle)) {
@@ -1256,6 +1259,9 @@ int ocfs2_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
12561259
if (status < 0)
12571260
goto bail_commit;
12581261
} else {
1262+
down_write(&OCFS2_I(inode)->ip_alloc_sem);
1263+
had_alloc_lock = 1;
1264+
12591265
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
12601266
if (IS_ERR(handle)) {
12611267
status = PTR_ERR(handle);
@@ -1274,6 +1280,9 @@ int ocfs2_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
12741280
bail_commit:
12751281
ocfs2_commit_trans(osb, handle);
12761282
bail_unlock:
1283+
if (had_alloc_lock)
1284+
up_write(&OCFS2_I(inode)->ip_alloc_sem);
1285+
12771286
if (status && inode_locked) {
12781287
ocfs2_inode_unlock_tracker(inode, 1, &oh, had_lock);
12791288
inode_locked = 0;

0 commit comments

Comments
 (0)