aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRabin Vincent <rabinv@axis.com>2017-05-03 11:54:01 -0400
committerSteve French <smfrench@gmail.com>2017-05-03 11:10:10 -0400
commit3998e6b87d4258a70df358296d6f1c7234012bfe (patch)
tree22ea39f418a248edf1cc3c9fb657df36984c5332
parent6026685de33b0db5b2b6b0e9b41b3a1a3261033c (diff)
CIFS: fix oplock break deadlocks
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> CC: Stable <stable@vger.kernel.org>
-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 34fee9fb7e4f..5c52c8f56102 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/*
@@ -1374,9 +1375,16 @@ init_cifs(void)
1374 goto out_clean_proc; 1375 goto out_clean_proc;
1375 } 1376 }
1376 1377
1378 cifsoplockd_wq = alloc_workqueue("cifsoplockd",
1379 WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
1380 if (!cifsoplockd_wq) {
1381 rc = -ENOMEM;
1382 goto out_destroy_cifsiod_wq;
1383 }
1384
1377 rc = cifs_fscache_register(); 1385 rc = cifs_fscache_register();
1378 if (rc) 1386 if (rc)
1379 goto out_destroy_wq; 1387 goto out_destroy_cifsoplockd_wq;
1380 1388
1381 rc = cifs_init_inodecache(); 1389 rc = cifs_init_inodecache();
1382 if (rc) 1390 if (rc)
@@ -1424,7 +1432,9 @@ out_destroy_inodecache:
1424 cifs_destroy_inodecache(); 1432 cifs_destroy_inodecache();
1425out_unreg_fscache: 1433out_unreg_fscache:
1426 cifs_fscache_unregister(); 1434 cifs_fscache_unregister();
1427out_destroy_wq: 1435out_destroy_cifsoplockd_wq:
1436 destroy_workqueue(cifsoplockd_wq);
1437out_destroy_cifsiod_wq:
1428 destroy_workqueue(cifsiod_wq); 1438 destroy_workqueue(cifsiod_wq);
1429out_clean_proc: 1439out_clean_proc:
1430 cifs_proc_clean(); 1440 cifs_proc_clean();
@@ -1447,6 +1457,7 @@ exit_cifs(void)
1447 cifs_destroy_mids(); 1457 cifs_destroy_mids();
1448 cifs_destroy_inodecache(); 1458 cifs_destroy_inodecache();
1449 cifs_fscache_unregister(); 1459 cifs_fscache_unregister();
1460 destroy_workqueue(cifsoplockd_wq);
1450 destroy_workqueue(cifsiod_wq); 1461 destroy_workqueue(cifsiod_wq);
1451 cifs_proc_clean(); 1462 cifs_proc_clean();
1452} 1463}
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 5c0e11a166ff..8be55be70faf 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1702,6 +1702,7 @@ void cifs_oplock_break(struct work_struct *work);
1702 1702
1703extern const struct slow_work_ops cifs_oplock_break_ops; 1703extern const struct slow_work_ops cifs_oplock_break_ops;
1704extern struct workqueue_struct *cifsiod_wq; 1704extern struct workqueue_struct *cifsiod_wq;
1705extern struct workqueue_struct *cifsoplockd_wq;
1705extern __u32 cifs_lock_secret; 1706extern __u32 cifs_lock_secret;
1706 1707
1707extern mempool_t *cifs_mid_poolp; 1708extern mempool_t *cifs_mid_poolp;
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index d8f8ddcdd57c..b08531977daa 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -489,7 +489,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
489 CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, 489 CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
490 &pCifsInode->flags); 490 &pCifsInode->flags);
491 491
492 queue_work(cifsiod_wq, 492 queue_work(cifsoplockd_wq,
493 &netfile->oplock_break); 493 &netfile->oplock_break);
494 netfile->oplock_break_cancelled = false; 494 netfile->oplock_break_cancelled = false;
495 495
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index 1a04b3a5beb1..7b08a1446a7f 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -499,7 +499,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
499 else 499 else
500 cfile->oplock_break_cancelled = true; 500 cfile->oplock_break_cancelled = true;
501 501
502 queue_work(cifsiod_wq, &cfile->oplock_break); 502 queue_work(cifsoplockd_wq, &cfile->oplock_break);
503 kfree(lw); 503 kfree(lw);
504 return true; 504 return true;
505 } 505 }
@@ -643,7 +643,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
643 CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, 643 CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
644 &cinode->flags); 644 &cinode->flags);
645 spin_unlock(&cfile->file_info_lock); 645 spin_unlock(&cfile->file_info_lock);
646 queue_work(cifsiod_wq, &cfile->oplock_break); 646 queue_work(cifsoplockd_wq,
647 &cfile->oplock_break);
647 648
648 spin_unlock(&tcon->open_file_lock); 649 spin_unlock(&tcon->open_file_lock);
649 spin_unlock(&cifs_tcp_ses_lock); 650 spin_unlock(&cifs_tcp_ses_lock);