aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorRabin Vincent <rabinv@axis.com>2017-05-03 11:54:01 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-05-20 08:28:40 -0400
commitf13d96bf98c2ba94968bbaf207f2af02290a979a (patch)
treec562d8e778947ae65288c2f07d53774b4956f5e1 /fs
parent411346640ccd04ea8571eb5871659b0bd33b007e (diff)
CIFS: fix oplock break deadlocks
commit 3998e6b87d4258a70df358296d6f1c7234012bfe upstream. When the final cifsFileInfo_put() is called from cifsiod and an oplock break work is queued, lockdep complains loudly: ============================================= [ INFO: possible recursive locking detected ] 4.11.0+ #21 Not tainted --------------------------------------------- kworker/0:2/78 is trying to acquire lock: ("cifsiod"){++++.+}, at: flush_work+0x215/0x350 but task is already holding lock: ("cifsiod"){++++.+}, at: process_one_work+0x255/0x8e0 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock("cifsiod"); lock("cifsiod"); *** DEADLOCK *** May be due to missing lock nesting notation 2 locks held by kworker/0:2/78: #0: ("cifsiod"){++++.+}, at: process_one_work+0x255/0x8e0 #1: ((&wdata->work)){+.+...}, at: process_one_work+0x255/0x8e0 stack backtrace: CPU: 0 PID: 78 Comm: kworker/0:2 Not tainted 4.11.0+ #21 Workqueue: cifsiod cifs_writev_complete Call Trace: dump_stack+0x85/0xc2 __lock_acquire+0x17dd/0x2260 ? match_held_lock+0x20/0x2b0 ? trace_hardirqs_off_caller+0x86/0x130 ? mark_lock+0xa6/0x920 lock_acquire+0xcc/0x260 ? lock_acquire+0xcc/0x260 ? flush_work+0x215/0x350 flush_work+0x236/0x350 ? flush_work+0x215/0x350 ? destroy_worker+0x170/0x170 __cancel_work_timer+0x17d/0x210 ? ___preempt_schedule+0x16/0x18 cancel_work_sync+0x10/0x20 cifsFileInfo_put+0x338/0x7f0 cifs_writedata_release+0x2a/0x40 ? cifs_writedata_release+0x2a/0x40 cifs_writev_complete+0x29d/0x850 ? preempt_count_sub+0x18/0xd0 process_one_work+0x304/0x8e0 worker_thread+0x9b/0x6a0 kthread+0x1b2/0x200 ? process_one_work+0x8e0/0x8e0 ? kthread_create_on_node+0x40/0x40 ret_from_fork+0x31/0x40 This is a real warning. Since the oplock is queued on the same workqueue this can deadlock if there is only one worker thread active for the workqueue (which will be the case during memory pressure when the rescuer thread is handling it). Furthermore, there is at least one other kind of hang possible due to the oplock break handling if there is only worker. (This can be reproduced without introducing memory pressure by having passing 1 for the max_active parameter of cifsiod.) cifs_oplock_break() can wait indefintely in the filemap_fdatawait() while the cifs_writev_complete() work is blocked: sysrq: SysRq : Show Blocked State task PC stack pid father kworker/0:1 D 0 16 2 0x00000000 Workqueue: cifsiod cifs_oplock_break Call Trace: __schedule+0x562/0xf40 ? mark_held_locks+0x4a/0xb0 schedule+0x57/0xe0 io_schedule+0x21/0x50 wait_on_page_bit+0x143/0x190 ? add_to_page_cache_lru+0x150/0x150 __filemap_fdatawait_range+0x134/0x190 ? do_writepages+0x51/0x70 filemap_fdatawait_range+0x14/0x30 filemap_fdatawait+0x3b/0x40 cifs_oplock_break+0x651/0x710 ? preempt_count_sub+0x18/0xd0 process_one_work+0x304/0x8e0 worker_thread+0x9b/0x6a0 kthread+0x1b2/0x200 ? process_one_work+0x8e0/0x8e0 ? kthread_create_on_node+0x40/0x40 ret_from_fork+0x31/0x40 dd D 0 683 171 0x00000000 Call Trace: __schedule+0x562/0xf40 ? mark_held_locks+0x29/0xb0 schedule+0x57/0xe0 io_schedule+0x21/0x50 wait_on_page_bit+0x143/0x190 ? add_to_page_cache_lru+0x150/0x150 __filemap_fdatawait_range+0x134/0x190 ? do_writepages+0x51/0x70 filemap_fdatawait_range+0x14/0x30 filemap_fdatawait+0x3b/0x40 filemap_write_and_wait+0x4e/0x70 cifs_flush+0x6a/0xb0 filp_close+0x52/0xa0 __close_fd+0xdc/0x150 SyS_close+0x33/0x60 entry_SYSCALL_64_fastpath+0x1f/0xbe Showing all locks held in the system: 2 locks held by kworker/0:1/16: #0: ("cifsiod"){.+.+.+}, at: process_one_work+0x255/0x8e0 #1: ((&cfile->oplock_break)){+.+.+.}, at: process_one_work+0x255/0x8e0 Showing busy workqueues and worker pools: workqueue cifsiod: flags=0xc pwq 0: cpus=0 node=0 flags=0x0 nice=0 active=1/1 in-flight: 16:cifs_oplock_break delayed: cifs_writev_complete, cifs_echo_request pool 0: cpus=0 node=0 flags=0x0 nice=0 hung=0s workers=3 idle: 750 3 Fix these problems by creating a a new workqueue (with a rescuer) for the oplock break work. Signed-off-by: Rabin Vincent <rabinv@axis.com> Signed-off-by: Steve French <smfrench@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/cifs/cifsfs.c15
-rw-r--r--fs/cifs/cifsglob.h1
-rw-r--r--fs/cifs/misc.c2
-rw-r--r--fs/cifs/smb2misc.c5
4 files changed, 18 insertions, 5 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 15261ba464c5..c0c253005b76 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -87,6 +87,7 @@ extern mempool_t *cifs_req_poolp;
87extern mempool_t *cifs_mid_poolp; 87extern mempool_t *cifs_mid_poolp;
88 88
89struct workqueue_struct *cifsiod_wq; 89struct workqueue_struct *cifsiod_wq;
90struct workqueue_struct *cifsoplockd_wq;
90__u32 cifs_lock_secret; 91__u32 cifs_lock_secret;
91 92
92/* 93/*
@@ -1283,9 +1284,16 @@ init_cifs(void)
1283 goto out_clean_proc; 1284 goto out_clean_proc;
1284 } 1285 }
1285 1286
1287 cifsoplockd_wq = alloc_workqueue("cifsoplockd",
1288 WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
1289 if (!cifsoplockd_wq) {
1290 rc = -ENOMEM;
1291 goto out_destroy_cifsiod_wq;
1292 }
1293
1286 rc = cifs_fscache_register(); 1294 rc = cifs_fscache_register();
1287 if (rc) 1295 if (rc)
1288 goto out_destroy_wq; 1296 goto out_destroy_cifsoplockd_wq;
1289 1297
1290 rc = cifs_init_inodecache(); 1298 rc = cifs_init_inodecache();
1291 if (rc) 1299 if (rc)
@@ -1333,7 +1341,9 @@ out_destroy_inodecache:
1333 cifs_destroy_inodecache(); 1341 cifs_destroy_inodecache();
1334out_unreg_fscache: 1342out_unreg_fscache:
1335 cifs_fscache_unregister(); 1343 cifs_fscache_unregister();
1336out_destroy_wq: 1344out_destroy_cifsoplockd_wq:
1345 destroy_workqueue(cifsoplockd_wq);
1346out_destroy_cifsiod_wq:
1337 destroy_workqueue(cifsiod_wq); 1347 destroy_workqueue(cifsiod_wq);
1338out_clean_proc: 1348out_clean_proc:
1339 cifs_proc_clean(); 1349 cifs_proc_clean();
@@ -1356,6 +1366,7 @@ exit_cifs(void)
1356 cifs_destroy_mids(); 1366 cifs_destroy_mids();
1357 cifs_destroy_inodecache(); 1367 cifs_destroy_inodecache();
1358 cifs_fscache_unregister(); 1368 cifs_fscache_unregister();
1369 destroy_workqueue(cifsoplockd_wq);
1359 destroy_workqueue(cifsiod_wq); 1370 destroy_workqueue(cifsiod_wq);
1360 cifs_proc_clean(); 1371 cifs_proc_clean();
1361} 1372}
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index b3830f7ab260..48ef401c3c61 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1651,6 +1651,7 @@ void cifs_oplock_break(struct work_struct *work);
1651 1651
1652extern const struct slow_work_ops cifs_oplock_break_ops; 1652extern const struct slow_work_ops cifs_oplock_break_ops;
1653extern struct workqueue_struct *cifsiod_wq; 1653extern struct workqueue_struct *cifsiod_wq;
1654extern struct workqueue_struct *cifsoplockd_wq;
1654extern __u32 cifs_lock_secret; 1655extern __u32 cifs_lock_secret;
1655 1656
1656extern mempool_t *cifs_mid_poolp; 1657extern mempool_t *cifs_mid_poolp;
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index c6729156f9a0..5419afea0a36 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -492,7 +492,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
492 CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, 492 CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
493 &pCifsInode->flags); 493 &pCifsInode->flags);
494 494
495 queue_work(cifsiod_wq, 495 queue_work(cifsoplockd_wq,
496 &netfile->oplock_break); 496 &netfile->oplock_break);
497 netfile->oplock_break_cancelled = false; 497 netfile->oplock_break_cancelled = false;
498 498
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index 97307808ae42..967dfe656ced 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -494,7 +494,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
494 else 494 else
495 cfile->oplock_break_cancelled = true; 495 cfile->oplock_break_cancelled = true;
496 496
497 queue_work(cifsiod_wq, &cfile->oplock_break); 497 queue_work(cifsoplockd_wq, &cfile->oplock_break);
498 kfree(lw); 498 kfree(lw);
499 return true; 499 return true;
500 } 500 }
@@ -638,7 +638,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
638 CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, 638 CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
639 &cinode->flags); 639 &cinode->flags);
640 spin_unlock(&cfile->file_info_lock); 640 spin_unlock(&cfile->file_info_lock);
641 queue_work(cifsiod_wq, &cfile->oplock_break); 641 queue_work(cifsoplockd_wq,
642 &cfile->oplock_break);
642 643
643 spin_unlock(&tcon->open_file_lock); 644 spin_unlock(&tcon->open_file_lock);
644 spin_unlock(&cifs_tcp_ses_lock); 645 spin_unlock(&cifs_tcp_ses_lock);