nvmet-rdma: use a private workqueue for delete
Subsystems (2)
Sagi Grimberg
maintainerRFC PATCH 1/1 nvmet-rdma: use a private workqueue for delete
Queue deletion is done asynchronous when the last reference on the queue is dropped. Thus, in order to make sure we don't over allocate under a connect/disconnect storm, we let queue deletion complete before making forward progress.
However, given that we flush the system_wq from rdma_cm context which runs from a workqueue context, we can have a circular locking complaint [1]. Fix that by using a private workqueue for queue deletion.
[1]:
======================================================
WARNING: possible circular locking dependency detected
4.19.0-rc4-dbg+ #3 Not tainted
------------------------------------------------------
kworker/5:0/39 is trying to acquire lock:00000000a10b6db9 (&id_priv->handler_mutex){+.+.}, at: rdma_destroy_id+0x6f/0x440 [rdma_cm]
but task is already holding lock:00000000331b4e2c ((work_completion)(&queue->release_work)){+.+.}, at: process_one_work+0x3ed/0xa20
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #3 ((work_completion)(&queue->release_work)){+.+.}:
process_one_work+0x474/0xa20
worker_thread+0x63/0x5a0
kthread+0x1cf/0x1f0
ret_from_fork+0x24/0x30-> #2 ((wq_completion)"events"){+.+.}:
flush_workqueue+0xf3/0x970
nvmet_rdma_cm_handler+0x133d/0x1734 [nvmet_rdma]
cma_ib_req_handler+0x72f/0xf90 [rdma_cm]
cm_process_work+0x2e/0x110 [ib_cm]
cm_req_handler+0x135b/0x1c30 [ib_cm]
cm_work_handler+0x2b7/0x38cd [ib_cm]
process_one_work+0x4ae/0xa20
nvmet_rdma:nvmet_rdma_cm_handler: nvmet_rdma: disconnected (10): status 0 id 0000000040357082
worker_thread+0x63/0x5a0
kthread+0x1cf/0x1f0
ret_from_fork+0x24/0x30nvme nvme0: Reconnecting in 10 seconds...
-> #1 (&id_priv->handler_mutex/1){+.+.}:
__mutex_lock+0xfe/0xbe0
mutex_lock_nested+0x1b/0x20
cma_ib_req_handler+0x6aa/0xf90 [rdma_cm]
cm_process_work+0x2e/0x110 [ib_cm]
cm_req_handler+0x135b/0x1c30 [ib_cm]
cm_work_handler+0x2b7/0x38cd [ib_cm]
process_one_work+0x4ae/0xa20
worker_thread+0x63/0x5a0
kthread+0x1cf/0x1f0
ret_from_fork+0x24/0x30-> #0 (&id_priv->handler_mutex){+.+.}:
lock_acquire+0xc5/0x200
__mutex_lock+0xfe/0xbe0
mutex_lock_nested+0x1b/0x20
rdma_destroy_id+0x6f/0x440 [rdma_cm]
nvmet_rdma_release_queue_work+0x8e/0x1b0 [nvmet_rdma]
process_one_work+0x4ae/0xa20
worker_thread+0x63/0x5a0
kthread+0x1cf/0x1f0
ret_from_fork+0x24/0x30--- drivers/nvme/target/rdma.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-)
drivers/nvme/target/rdma.c +15 -4
@@ -121,6 +121,7 @@ struct nvmet_rdma_device {int inline_page_count; }; +struct workqueue_struct *nvmet_rdma_delete_wq;static bool nvmet_rdma_use_srq;module_param_named (use_srq, nvmet_rdma_use_srq, bool,0444 );MODULE_PARM_DESC (use_srq,"Use shared receive queue." );
@@ -1244,12 +1245,12 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id,if (queue->host_qid ==0 ) {/* Let inflight controller teardown complete */ -flush_scheduled_work (); +flush_workqueue (nvmet_rdma_delete_wq); } ret =nvmet_rdma_cm_accept (cm_id, queue, &event->param.conn);if (ret) { -schedule_work (&queue->release_work); +queue_work (nvmet_rdma_delete_wq, &queue->release_work);/* Destroying rdma_cm id is not needed here */ return 0 ; }
@@ -1314,7 +1315,7 @@ static void __nvmet_rdma_queue_disconnect(struct nvmet_rdma_queue *queue)if (disconnect) {rdma_disconnect (queue->cm_id); -schedule_work (&queue->release_work); +queue_work (nvmet_rdma_delete_wq, &queue->release_work); } }
@@ -1344,7 +1345,7 @@ static void nvmet_rdma_queue_connect_fail(struct rdma_cm_id *cm_id,mutex_unlock (&nvmet_rdma_queue_mutex);pr_err ("failed to connect queue %d\n" , queue->idx); -schedule_work (&queue->release_work); +queue_work (nvmet_rdma_delete_wq, &queue->release_work); }/**
@@ -1626,8 +1627,17 @@ static int __init nvmet_rdma_init(void)if (ret) goto err_ib_client; + nvmet_rdma_delete_wq =alloc_workqueue ("nvmet-rdma-delete-wq" , +WQ_UNBOUND |WQ_MEM_RECLAIM |WQ_SYSFS ,0 ); +if (!nvmet_rdma_delete_wq) { + ret = -ENOMEM ; + goto err_unreg_transport; + } +return 0 ; +err_unreg_transport: +nvmet_unregister_transport (&nvmet_rdma_ops); err_ib_client:ib_unregister_client (&nvmet_rdma_ib_client);return ret;
@@ -1635,6 +1645,7 @@ static int __init nvmet_rdma_init(void) static void __exit nvmet_rdma_exit(void) { +destroy_workqueue (nvmet_rdma_delete_wq);nvmet_unregister_transport (&nvmet_rdma_ops);ib_unregister_client (&nvmet_rdma_ib_client);WARN_ON_ONCE (!list_empty (&nvmet_rdma_queue_list));
Signature · 1 line
2.17.1
RFC PATCH 1/1 nvmet-rdma: use a private workqueue for delete
Quoted 9 lines · Sagi Grimberg
Queue deletion is done asynchronous when the last reference on the queue is dropped. Thus, in order to make sure we don't over allocate under a connect/disconnect storm, we let queue deletion complete before making forward progress.
However, given that we flush the
system_wqfromrdma_cmcontext which runs from a workqueue context, we can have a circular locking complaint [1]. Fix that by using a private workqueue for queue deletion.
Hi Sagi,
Thanks for this patch. With this patch applied the warning I reported earlier disappears but a new warning appeared:
======================================================
WARNING: possible circular locking dependency detected
4.19.0-rc5-dbg+ #8 Not tainted
------------------------------------------------------
kworker/u24:7/977 is trying to acquire lock:000000003dc8471f (&id_priv->handler_mutex){+.+.}, at: rdma_destroy_id+0x6f/0x440 [rdma_cm]
but task is already holding lock:0000000086ca5cb1 ((work_completion)(&queue->release_work)){+.+.}, at: process_one_work+0x3ed/0xa20
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #3 ((work_completion)(&queue->release_work)){+.+.}:
process_one_work+0x474/0xa20
worker_thread+0x63/0x5a0
kthread+0x1cf/0x1f0
ret_from_fork+0x24/0x30-> #2 ((wq_completion)"nvmet-rdma-delete-wq"){+.+.}:
flush_workqueue+0xf3/0x970
nvmet_rdma_cm_handler+0x1319/0x170f [nvmet_rdma]
cma_ib_req_handler+0x72f/0xf90 [rdma_cm]
cm_process_work+0x2e/0x110 [ib_cm]
cm_req_handler+0x135b/0x1c30 [ib_cm]
cm_work_handler+0x2b7/0x38cd [ib_cm]
process_one_work+0x4ae/0xa20
worker_thread+0x63/0x5a0
kthread+0x1cf/0x1f0
ret_from_fork+0x24/0x30-> #1 (&id_priv->handler_mutex/1){+.+.}:
__mutex_lock+0xfe/0xbe0
mutex_lock_nested+0x1b/0x20
cma_ib_req_handler+0x6aa/0xf90 [rdma_cm]
cm_process_work+0x2e/0x110 [ib_cm]
cm_req_handler+0x135b/0x1c30 [ib_cm]
cm_work_handler+0x2b7/0x38cd [ib_cm]
process_one_work+0x4ae/0xa20
worker_thread+0x63/0x5a0
kthread+0x1cf/0x1f0
ret_from_fork+0x24/0x30-> #0 (&id_priv->handler_mutex){+.+.}:
lock_acquire+0xc5/0x200
__mutex_lock+0xfe/0xbe0
mutex_lock_nested+0x1b/0x20
rdma_destroy_id+0x6f/0x440 [rdma_cm]
nvmet_rdma_release_queue_work+0x8e/0x1b0 [nvmet_rdma]
process_one_work+0x4ae/0xa20
worker_thread+0x63/0x5a0
kthread+0x1cf/0x1f0
ret_from_fork+0x24/0x30other info that might help us debug this:
Chain exists of: &id_priv->handler_mutex --> (wq_completion)"nvmet-rdma-delete-wq" --> (work_completion)(&queue->release_work) Possible unsafe locking scenario:
CPU0 CPU1
---- ----
lock((work_completion)(&queue->release_work));
lock((wq_completion)"nvmet-rdma-delete-wq");
lock((work_completion)(&queue->release_work)); lock(&id_priv->handler_mutex);
*** DEADLOCK ***
2 locks held by kworker/u24:7/977:
#0: 00000000148450ab ((wq_completion)"nvmet-rdma-delete-wq"){+.+.}, at: process_one_work+0x3ed/0xa20 #1: 0000000086ca5cb1 ((work_completion)(&queue->release_work)){+.+.}, at: process_one_work+0x3ed/0xa20
stack backtrace: CPU: 1 PID: 977 Comm: kworker/u24:7 Not tainted 4.19.0-rc5-dbg+ #8 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014 Workqueue: nvmet-rdma-delete-wq nvmet_rdma_release_queue_work [nvmet_rdma] Call Trace: dump_stack+0x86/0xc5 print_circular_bug.isra.32+0x20a/0x218 __lock_acquire+0x1a54/0x1b20 lock_acquire+0xc5/0x200 __mutex_lock+0xfe/0xbe0 mutex_lock_nested+0x1b/0x20 rdma_destroy_id+0x6f/0x440 [rdma_cm] nvmet_rdma_release_queue_work+0x8e/0x1b0 [nvmet_rdma] process_one_work+0x4ae/0xa20 worker_thread+0x63/0x5a0 kthread+0x1cf/0x1f0 ret_from_fork+0x24/0x30
Sagi Grimberg
maintainerRFC PATCH 1/1 nvmet-rdma: use a private workqueue for delete
Quoted 11 lines
Quoted 10 lines
Queue deletion is done asynchronous when the last reference on the queue is dropped. Thus, in order to make sure we don't over allocate under a connect/disconnect storm, we let queue deletion complete before making forward progress.
However, given that we flush the
system_wqfromrdma_cmcontext which runs from a workqueue context, we can have a circular locking complaint [1]. Fix that by using a private workqueue for queue deletion.
Quoted 4 lines
Hi Sagi,
Thanks for this patch. With this patch applied the warning I reported earlier disappears but a new warning appeared:
Thanks for testing, this is a similar complaint though...
What I'm missing here is why flushing a work that runs on workqueue A can't be done from a work that runs on workqueue B. It is guaranteed that the id_priv that is used as a barrier from rdma_destroy_id is different from the id_priv that is handling the connect. So I'm not clear on what is the dependency yet.
Any insights?
Quoted 95 lines
======================================================
WARNING: possible circular locking dependency detected
4.19.0-rc5-dbg+ #8 Not tainted
------------------------------------------------------
kworker/u24:7/977 is trying to acquire lock:000000003dc8471f(&id_priv->handler_mutex){+.+.}, at:rdma_destroy_id+0x6f/0x440[rdma_cm]but task is already holding lock:
0000000086ca5cb1((work_completion)(&queue->release_work)){+.+.}, at:process_one_work+0x3ed/0xa20which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #3 ((work_completion)(&queue->release_work)){+.+.}:
process_one_work+0x474/0xa20 worker_thread+0x63/0x5a0 kthread+0x1cf/0x1f0 ret_from_fork+0x24/0x30-> #2 ((wq_completion)"nvmet-rdma-delete-wq"){+.+.}:
flush_workqueue+0xf3/0x970 nvmet_rdma_cm_handler+0x1319/0x170f [nvmet_rdma] cma_ib_req_handler+0x72f/0xf90 [rdma_cm] cm_process_work+0x2e/0x110 [ib_cm] cm_req_handler+0x135b/0x1c30 [ib_cm] cm_work_handler+0x2b7/0x38cd [ib_cm] process_one_work+0x4ae/0xa20 worker_thread+0x63/0x5a0 kthread+0x1cf/0x1f0 ret_from_fork+0x24/0x30-> #1 (&id_priv->handler_mutex/1){+.+.}:
__mutex_lock+0xfe/0xbe0 mutex_lock_nested+0x1b/0x20 cma_ib_req_handler+0x6aa/0xf90 [rdma_cm] cm_process_work+0x2e/0x110 [ib_cm] cm_req_handler+0x135b/0x1c30 [ib_cm] cm_work_handler+0x2b7/0x38cd [ib_cm] process_one_work+0x4ae/0xa20 worker_thread+0x63/0x5a0 kthread+0x1cf/0x1f0 ret_from_fork+0x24/0x30-> #0 (&id_priv->handler_mutex){+.+.}:
lock_acquire+0xc5/0x200 __mutex_lock+0xfe/0xbe0 mutex_lock_nested+0x1b/0x20 rdma_destroy_id+0x6f/0x440 [rdma_cm] nvmet_rdma_release_queue_work+0x8e/0x1b0 [nvmet_rdma] process_one_work+0x4ae/0xa20 worker_thread+0x63/0x5a0 kthread+0x1cf/0x1f0 ret_from_fork+0x24/0x30other info that might help us debug this:
Chain exists of: &id_priv->handler_mutex --> (wq_completion)"nvmet-rdma-delete-wq" --> (work_completion)(&queue->release_work) Possible unsafe locking scenario:CPU0 CPU1 ---- ---- lock((work_completion)(&queue->release_work)); lock((wq_completion)"nvmet-rdma-delete-wq"); lock((work_completion)(&queue->release_work));
lock(&id_priv->handler_mutex);*** DEADLOCK ***
2 locks held by kworker/u24:7/977:
#0:00000000148450ab((wq_completion)"nvmet-rdma-delete-wq"){+.+.}, at:process_one_work+0x3ed/0xa20#1:0000000086ca5cb1((work_completion)(&queue->release_work)){+.+.}, at:process_one_work+0x3ed/0xa20stack backtrace: CPU: 1 PID: 977 Comm: kworker/u24:7 Not tainted 4.19.0-rc5-dbg+ #8 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014 Workqueue: nvmet-rdma-delete-wq nvmet_rdma_release_queue_work [nvmet_rdma] Call Trace: dump_stack+0x86/0xc5 print_circular_bug.isra.32+0x20a/0x218 __lock_acquire+0x1a54/0x1b20 lock_acquire+0xc5/0x200 __mutex_lock+0xfe/0xbe0 mutex_lock_nested+0x1b/0x20 rdma_destroy_id+0x6f/0x440 [rdma_cm] nvmet_rdma_release_queue_work+0x8e/0x1b0 [nvmet_rdma] process_one_work+0x4ae/0xa20 worker_thread+0x63/0x5a0 kthread+0x1cf/0x1f0 ret_from_fork+0x24/0x30
RFC PATCH 1/1 nvmet-rdma: use a private workqueue for delete
Quoted 17 lines · Sagi Grimberg
Quoted 10 lines
Quoted 9 lines
Queue deletion is done asynchronous when the last reference on the queue is dropped. Thus, in order to make sure we don't over allocate under a connect/disconnect storm, we let queue deletion complete before making forward progress.
However, given that we flush the
system_wqfromrdma_cmcontext which runs from a workqueue context, we can have a circular locking complaint [1]. Fix that by using a private workqueue for queue deletion.Quoted 4 lines
Hi Sagi,
Thanks for this patch. With this patch applied the warning I reported earlier disappears but a new warning appeared:
Thanks for testing, this is a similar complaint though...
Quoted 7 lines
What I'm missing here is why flushing a work that runs on workqueue A can't be done from a work that runs on workqueue B. It is guaranteed that the
id_privthat is used as a barrier fromrdma_destroy_idis different from theid_privthat is handling the connect. So I'm not clear on what is the dependency yet.Any insights?
Hi Sagi,
Further testing showed that the warning shown in my previous e-mail also occurs without your patch. Since I'm fine with your patch, feel free to add:
RFC PATCH 1/1 nvmet-rdma: use a private workqueue for delete
In reply to Sagi GrimbergThanks,
applied to nvme-4.20.
A message that is not in the archive (sent off-list or not yet received): CAO+b5-oBVw=-wvnWk1EF=RBaZtjX6bjUG+3WABXbvzX9UTu26w@mail.gmail.com
Sagi Grimberg
maintainerRFC PATCH 1/1 nvmet-rdma: use a private workqueue for delete
Quoted 5 lines
It seems like this has not yet been fixed entirely. This is what appeared in the kernel log this morning on my test setup with Christoph's nvme-4.20 branch (commit
cb4bfda62afa("nvme-pci: fix hot removal during error handling")):
There is something I'm missing here, the id_priv->handler_mutex that the connect context is running on is guaranteed to be different than the one being removed (a different cm_id) and also the workqueues are different.
Is it not allowed to flush workqueue A from a work that is hosted on workqueue B?
Quoted 100 lines
======================================================
WARNING: possible circular locking dependency detected 4.19.0-rc6-dbg+ #1 Not tainted
------------------------------------------------------
kworker/u16:7/169 is trying to acquire lock:00000000578ccf82(&id_priv->handler_mutex){+.+.}, at:rdma_destroy_id+0x6f/0x440[rdma_cm]but task is already holding lock:
000000005d67271b((work_completion)(&queue->release_work)){+.+.}, at:process_one_work+0x3ed/0xa20which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #3 ((work_completion)(&queue->release_work)){+.+.}:
? ? ? ?process_one_work+0x474/0xa20
? ? ? ?worker_thread+0x63/0x5a0
? ? ? ?kthread+0x1cf/0x1f0
? ? ? ?ret_from_fork+0x24/0x30-> #2 ((wq_completion)"nvmet-rdma-delete-wq"){+.+.}:
? ? ? ?flush_workqueue+0xf3/0x970
? ? ? ?nvmet_rdma_cm_handler+0x1319/0x170f [nvmet_rdma] ? ? ? ?cma_ib_req_handler+0x72f/0xf90 [rdma_cm] ? ? ? ?cm_process_work+0x2e/0x110 [ib_cm]
? ? ? ?cm_req_handler+0x135b/0x1c30 [ib_cm]
? ? ? ?cm_work_handler+0x2b7/0x38cd [ib_cm]
? ? ? ?process_one_work+0x4ae/0xa20
? ? ? ?worker_thread+0x63/0x5a0
? ? ? ?kthread+0x1cf/0x1f0
? ? ? ?ret_from_fork+0x24/0x30-> #1 (&id_priv->handler_mutex/1){+.+.}:
? ? ? ?__mutex_lock+0xfe/0xbe0
? ? ? ?mutex_lock_nested+0x1b/0x20
? ? ? ?cma_ib_req_handler+0x6aa/0xf90 [rdma_cm]
? ? ? ?cm_process_work+0x2e/0x110 [ib_cm]
? ? ? ?cm_req_handler+0x135b/0x1c30 [ib_cm]
? ? ? ?cm_work_handler+0x2b7/0x38cd [ib_cm]
? ? ? ?process_one_work+0x4ae/0xa20
? ? ? ?worker_thread+0x63/0x5a0
? ? ? ?kthread+0x1cf/0x1f0
? ? ? ?ret_from_fork+0x24/0x30-> #0 (&id_priv->handler_mutex){+.+.}:
? ? ? ?lock_acquire+0xd2/0x210
? ? ? ?__mutex_lock+0xfe/0xbe0
? ? ? ?mutex_lock_nested+0x1b/0x20
? ? ? ?rdma_destroy_id+0x6f/0x440 [rdma_cm]
? ? ? ?nvmet_rdma_release_queue_work+0x8e/0x1b0 [nvmet_rdma] ? ? ? ?process_one_work+0x4ae/0xa20
? ? ? ?worker_thread+0x63/0x5a0
? ? ? ?kthread+0x1cf/0x1f0
? ? ? ?ret_from_fork+0x24/0x30other info that might help us debug this:
Chain exists of: ? &id_priv->handler_mutex --> (wq_completion)"nvmet-rdma-delete-wq" -->(work_completion)(&queue->release_work)
?Possible unsafe locking scenario:
? ? ? ?CPU0 ? ? ? ? ? ? ? ? ? ?CPU1
? ? ? ?---- ? ? ? ? ? ? ? ? ? ?----
? lock((work_completion)(&queue->release_work));
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?lock((wq_completion)"nvmet-rdma-delete-wq");?lock((work_completion)(&queue->release_work));
?lock(&id_priv->handler_mutex);?*** DEADLOCK ***
2 locks held by kworker/u16:7/169:
?#0:00000000a32d4be9((wq_completion)"nvmet-rdma-delete-wq"){+.+.},
at:process_one_work+0x3ed/0xa20
?#1:000000005d67271b((work_completion)(&queue->release_work)){+.+.},
at:process_one_work+0x3ed/0xa20stack backtrace: CPU: 1 PID: 169 Comm: kworker/u16:7 Not tainted 4.19.0-rc6-dbg+ #1 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 Workqueue: nvmet-rdma-delete-wq nvmet_rdma_release_queue_work [nvmet_rdma] Call Trace: ?dump_stack+0xa4/0xf5 ?print_circular_bug.isra.32+0x20a/0x218 ?__lock_acquire+0x1a5e/0x1b20 ?lock_acquire+0xd2/0x210 ?__mutex_lock+0xfe/0xbe0 ?mutex_lock_nested+0x1b/0x20 ?rdma_destroy_id+0x6f/0x440 [rdma_cm] ?nvmet_rdma_release_queue_work+0x8e/0x1b0 [nvmet_rdma] ?process_one_work+0x4ae/0xa20 ?worker_thread+0x63/0x5a0 ?kthread+0x1cf/0x1f0 ?ret_from_fork+0x24/0x30
RFC PATCH 1/1 nvmet-rdma: use a private workqueue for delete
On Thu, 2018-10-18@18:08 -0700, Sagi Grimberg wrote:
+AD4 +AD4 It seems like this has not yet been fixed entirely. This is what appeared +AD4 +AD4 in the kernel log this morning on my test setup with Christoph's nvme-4.20 +AD4 +AD4 branch (commit cb4bfda62afa (+ACI-nvme-pci: fix hot removal during error +AD4 +AD4 handling+ACI)):
+AD4
+AD4 There is something I'm missing here, the id+AF8-priv-+AD4-handler+AF8-mutex that the +AD4 connect context is running on is guaranteed to be different than the +AD4 one being removed (a different cm+AF8-id) and also the workqueues are +AD4 different.
+AD4
+AD4 Is it not allowed to flush workqueue A from a work that is hosted on +AD4 workqueue B?
Hi Tejun and Johannes,
It seems like we ran into a lockdep complaint triggered by a recently queued patch (87915adc3f0a (+ACI-workqueue: re-add lockdep dependencies for flushing+ACI)). However, it's not clear to us whether anything is wrong with the code the complaint refers to. Can any of you have a look? I have attached the lockep complaint to this e-mail.
Thanks,
Bart.
-------------- next part --------------
======================================================
WARNING: possible circular locking dependency detected
4.19.0-rc6-dbg+ #1 Not tainted
------------------------------------------------------
kworker/u16:7/169 is trying to acquire lock:00000000578ccf82 (&id_priv->handler_mutex){+.+.}, at: rdma_destroy_id+0x6f/0x440 [rdma_cm]
but task is already holding lock:000000005d67271b ((work_completion)(&queue->release_work)){+.+.}, at: process_one_work+0x3ed/0xa20
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #3 ((work_completion)(&queue->release_work)){+.+.}:
process_one_work+0x474/0xa20
worker_thread+0x63/0x5a0
kthread+0x1cf/0x1f0
ret_from_fork+0x24/0x30-> #2 ((wq_completion)"nvmet-rdma-delete-wq"){+.+.}:
flush_workqueue+0xf3/0x970
nvmet_rdma_cm_handler+0x1319/0x170f [nvmet_rdma]
cma_ib_req_handler+0x72f/0xf90 [rdma_cm]
cm_process_work+0x2e/0x110 [ib_cm]
cm_req_handler+0x135b/0x1c30 [ib_cm]
cm_work_handler+0x2b7/0x38cd [ib_cm]
process_one_work+0x4ae/0xa20
worker_thread+0x63/0x5a0
kthread+0x1cf/0x1f0
ret_from_fork+0x24/0x30-> #1 (&id_priv->handler_mutex/1){+.+.}:
__mutex_lock+0xfe/0xbe0
mutex_lock_nested+0x1b/0x20
cma_ib_req_handler+0x6aa/0xf90 [rdma_cm]
cm_process_work+0x2e/0x110 [ib_cm]
cm_req_handler+0x135b/0x1c30 [ib_cm]
cm_work_handler+0x2b7/0x38cd [ib_cm]
process_one_work+0x4ae/0xa20
worker_thread+0x63/0x5a0
kthread+0x1cf/0x1f0
ret_from_fork+0x24/0x30-> #0 (&id_priv->handler_mutex){+.+.}:
lock_acquire+0xd2/0x210
__mutex_lock+0xfe/0xbe0
mutex_lock_nested+0x1b/0x20
rdma_destroy_id+0x6f/0x440 [rdma_cm]
nvmet_rdma_release_queue_work+0x8e/0x1b0 [nvmet_rdma]
process_one_work+0x4ae/0xa20
worker_thread+0x63/0x5a0
kthread+0x1cf/0x1f0
ret_from_fork+0x24/0x30other info that might help us debug this:
Chain exists of: &id_priv->handler_mutex --> (wq_completion)"nvmet-rdma-delete-wq" --> (work_completion)(&queue->release_work) Possible unsafe locking scenario:
CPU0 CPU1
---- ----
lock((work_completion)(&queue->release_work));
lock((wq_completion)"nvmet-rdma-delete-wq");
lock((work_completion)(&queue->release_work)); lock(&id_priv->handler_mutex);
*** DEADLOCK ***
2 locks held by kworker/u16:7/169:
#0: 00000000a32d4be9 ((wq_completion)"nvmet-rdma-delete-wq"){+.+.}, at: process_one_work+0x3ed/0xa20 #1: 000000005d67271b ((work_completion)(&queue->release_work)){+.+.}, at: process_one_work+0x3ed/0xa20
stack backtrace: CPU: 1 PID: 169 Comm: kworker/u16:7 Not tainted 4.19.0-rc6-dbg+ #1 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 Workqueue: nvmet-rdma-delete-wq nvmet_rdma_release_queue_work [nvmet_rdma] Call Trace: dump_stack+0xa4/0xf5 print_circular_bug.isra.32+0x20a/0x218 __lock_acquire+0x1a5e/0x1b20 lock_acquire+0xd2/0x210 __mutex_lock+0xfe/0xbe0 mutex_lock_nested+0x1b/0x20 rdma_destroy_id+0x6f/0x440 [rdma_cm] nvmet_rdma_release_queue_work+0x8e/0x1b0 [nvmet_rdma] process_one_work+0x4ae/0xa20 worker_thread+0x63/0x5a0 kthread+0x1cf/0x1f0 ret_from_fork+0x24/0x30
RFC PATCH 1/1 nvmet-rdma: use a private workqueue for delete
Hi Bart, all,
Quoted 5 lines · Bart Van Assche
Quoted 4 lines · Sagi Grimberg
Quoted 4 lines
It seems like this has not yet been fixed entirely. This is what appeared in the kernel log this morning on my test setup with Christoph's nvme-4.20 branch (commit
cb4bfda62afa("nvme-pci: fix hot removal during error handling")):
FWIW, I'm not sure where to find this tree (it's not on kernel.org, apparently, at least none of hch's?). As a result, I don't have the correct code here now.
Quoted 5 lines
Quoted 5 lines
There is something I'm missing here, the
id_priv->handler_mutexthat the connect context is running on is guaranteed to be different than the one being removed (a differentcm_id) and also the workqueues are different.
Quoted 8 lines
Quoted 2 lines
Is it not allowed to flush workqueue A from a work that is hosted on workqueue B?
It seems like we ran into a lockdep complaint triggered by a recently queued patch (
87915adc3f0a("workqueue: re-add lockdep dependencies for flushing")). However, it's not clear to us whether anything is wrong with the code the complaint refers to. Can any of you have a look? I have attached the lockep complaint to this e-mail.
Is it possible that you're just running into the general lockdep limitation in that it doesn't know anything about *instances*, just *classes*?
Without looking at the correct code I don't want to try to diagnose this further, but that's what it seems like?
It's clearly telling you that you're acquiring "id_priv->handler_mutex" and lists a long dependency chain:
&id_priv->handler_mutex --> (wq_completion)"nvmet-rdma-delete-wq" --> (work_completion)(&queue->release_work)
so that you shouldn't be trying to acquire this mutex again from running inside the release work:
kworker/u16:7/169 is trying to acquire lock:
00000000578ccf82 (&id_priv->handler_mutex){+.+.}, at: rdma_destroy_id+0x6f/0x440 [rdma_cm]
but task is already holding lock:
000000005d67271b ((work_completion)(&queue->release_work)){+.+.}, at: process_one_work+0x3ed/0xa20
This looks like lockdep is working as it should.
If these are indeed (guaranteed to be) different, then I'd say you'd have to split them into different classes. However, I have no idea what the rules are for the recursion here, so I can't really say how you could do that.
johannes
RFC PATCH 1/1 nvmet-rdma: use a private workqueue for delete
Quoted 10 lines · Johannes Berg
Quoted 5 lines · Bart Van Assche
Quoted 4 lines · Sagi Grimberg
Quoted 4 lines
It seems like this has not yet been fixed entirely. This is what appeared in the kernel log this morning on my test setup with Christoph's nvme-4.20 branch (commit
cb4bfda62afa("nvme-pci: fix hot removal during error handling")):FWIW, I'm not sure where to find this tree (it's not on kernel.org, apparently, at least none of hch's?). As a result, I don't have the correct code here now.
Christoph's NVMe tree is available at git://git.infradead.org/nvme.git.
Bart.
RFC PATCH 1/1 nvmet-rdma: use a private workqueue for delete
Quoted 8 lines · Bart Van Assche
Quoted 7 lines · Johannes Berg
Quoted 5 lines · Bart Van Assche
Quoted 4 lines · Sagi Grimberg
Quoted 4 lines
It seems like this has not yet been fixed entirely. This is what appeared in the kernel log this morning on my test setup with Christoph's nvme-4.20 branch (commit
cb4bfda62afa("nvme-pci: fix hot removal during error handling")):
Quoted 5 lines
Quoted 3 lines
FWIW, I'm not sure where to find this tree (it's not on kernel.org, apparently, at least none of hch's?). As a result, I don't have the correct code here now.
Christoph's NVMe tree is available at
git://git.infradead.org/nvme.git.
Ok, thanks. I think I'll go off Sagi Grimberg's explanation though rather than try to understand the code myself.
johannes
RFC PATCH 1/1 nvmet-rdma: use a private workqueue for delete
Quoted 9 lines · Johannes Berg
Quoted 8 lines · Bart Van Assche
Quoted 7 lines · Johannes Berg
Quoted 5 lines · Bart Van Assche
Quoted 4 lines · Sagi Grimberg
Quoted 4 lines
It seems like this has not yet been fixed entirely. This is what appeared in the kernel log this morning on my test setup with Christoph's nvme-4.20 branch (commit
cb4bfda62afa("nvme-pci: fix hot removal during error handling")):
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
FWIW, I'm not sure where to find this tree (it's not on kernel.org, apparently, at least none of hch's?). As a result, I don't have the correct code here now.
Christoph's NVMe tree is available at
git://git.infradead.org/nvme.git.Ok, thanks. I think I'll go off Sagi Grimberg's explanation though rather than try to understand the code myself.
Are the lockdep annotations in kernel/workqueue.c correct? My understanding is that lock_map_acquire() should only be used to annotate mutually exclusive locking (e.g. mutex, spinlock). However, multiple work items associated with the same workqueue can be executed concurrently. From lockdep.h:
#define lock_map_acquire(l) lock_acquire_exclusive(l, 0, 0, NULL, _THIS_IP_)
Thanks,
Bart.
RFC PATCH 1/1 nvmet-rdma: use a private workqueue for delete
Quoted 19 lines · Bart Van Assche
Quoted 9 lines · Johannes Berg
Quoted 8 lines · Bart Van Assche
Quoted 7 lines · Johannes Berg
Quoted 5 lines · Bart Van Assche
Quoted 4 lines · Sagi Grimberg
Quoted 4 lines
It seems like this has not yet been fixed entirely. This is what appeared in the kernel log this morning on my test setup with Christoph's nvme-4.20 branch (commit
cb4bfda62afa("nvme-pci: fix hot removal during error handling")):Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
FWIW, I'm not sure where to find this tree (it's not on kernel.org, apparently, at least none of hch's?). As a result, I don't have the correct code here now.
Christoph's NVMe tree is available at
git://git.infradead.org/nvme.git.Ok, thanks. I think I'll go off Sagi Grimberg's explanation though rather than try to understand the code myself.
Quoted 6 lines
Are the lockdep annotations in
kernel/workqueue.ccorrect? My understanding is thatlock_map_acquire()should only be used to annotate mutually exclusive locking (e.g. mutex, spinlock). However, multiple work items associated with the same workqueue can be executed concurrently. Fromlockdep.h:#define
lock_map_acquire(l)lock_acquire_exclusive(l, 0, 0, NULL, _THIS_IP_)
I've talked about this in the other thread, in the interest of keeping things together, can you ask that again over there?
johannes
RFC PATCH 1/1 nvmet-rdma: use a private workqueue for delete
Quoted 25 lines · Johannes Berg
Quoted 24 lines · Bart Van Assche
Quoted 9 lines · Johannes Berg
Quoted 8 lines · Bart Van Assche
Quoted 7 lines · Johannes Berg
Quoted 5 lines · Bart Van Assche
Quoted 4 lines · Sagi Grimberg
Quoted 4 lines
It seems like this has not yet been fixed entirely. This is what appeared in the kernel log this morning on my test setup with Christoph's nvme-4.20 branch (commit
cb4bfda62afa("nvme-pci: fix hot removal during error handling")):Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
FWIW, I'm not sure where to find this tree (it's not on kernel.org, apparently, at least none of hch's?). As a result, I don't have the correct code here now.
Christoph's NVMe tree is available at
git://git.infradead.org/nvme.git.Ok, thanks. I think I'll go off Sagi Grimberg's explanation though rather than try to understand the code myself.
Are the lockdep annotations in
kernel/workqueue.ccorrect? My understanding is thatlock_map_acquire()should only be used to annotate mutually exclusive locking (e.g. mutex, spinlock). However, multiple work items associated with the same workqueue can be executed concurrently. Fromlockdep.h:
Quoted 4 lines
Quoted 1 line
#define
lock_map_acquire(l)lock_acquire_exclusive(l, 0, 0, NULL, _THIS_IP_)I've talked about this in the other thread, in the interest of keeping things together, can you ask that again over there?
Actually, I'll just go answer over there, no need to repost the question.
johannes
Sagi Grimberg
maintainerRFC PATCH 1/1 nvmet-rdma: use a private workqueue for delete
In reply to Johannes BergQuoted 37 lines
Quoted 6 lines
Quoted 6 lines
There is something I'm missing here, the
id_priv->handler_mutexthat the connect context is running on is guaranteed to be different than the one being removed (a differentcm_id) and also the workqueues are different.Quoted 8 lines
Quoted 2 lines
Is it not allowed to flush workqueue A from a work that is hosted on workqueue B?
It seems like we ran into a lockdep complaint triggered by a recently queued patch (
87915adc3f0a("workqueue: re-add lockdep dependencies for flushing")). However, it's not clear to us whether anything is wrong with the code the complaint refers to. Can any of you have a look? I have attached the lockep complaint to this e-mail.Is it possible that you're just running into the general lockdep limitation in that it doesn't know anything about *instances*, just *classes*?
Without looking at the correct code I don't want to try to diagnose this further, but that's what it seems like?
It's clearly telling you that you're acquiring "
id_priv->handler_mutex" and lists a long dependency chain:&id_priv->handler_mutex --> (wq_completion)"nvmet-rdma-delete-wq" --> (work_completion)(&queue->release_work)so that you shouldn't be trying to acquire this mutex again from running inside the release work:
kworker/u16:7/169 is trying to acquire lock:
00000000578ccf82 (&id_priv->handler_mutex){+.+.}, at: rdma_destroy_id+0x6f/0x440 [rdma_cm] but task is already holding lock: 000000005d67271b ((work_completion)(&queue->release_work)){+.+.}, at: process_one_work+0x3ed/0xa20
Quoted 6 lines
This looks like lockdep is working as it should.
If these are indeed (guaranteed to be) different, then I'd say you'd have to split them into different classes. However, I have no idea what the rules are for the recursion here, so I can't really say how you could do that.
I'm not sure how I can divide them into classes. The priv is really an internal structure that surrounds a connection connection representation. The priv->handler_mutex wraps every event handling dispatched to the upper layer consumer.
The connection destruction barriers by acquiring and releasing this event_handler mutex such that no events are handled by the
See drivers/infiniband/core/cma.c rdma_destroy_id()
In our case, one of the event handlers flushes a workqueue that is hosting work items that essentially call rdma_destroy_id() on connections that are guaranteed not to be the one currently handling the event. So the priv is guaranteed to be a different instance.
RFC PATCH 1/1 nvmet-rdma: use a private workqueue for delete
Quoted 9 lines · Sagi Grimberg
Quoted 1 line
Quoted 1 line
Quoted 1 line
I'm not sure how I can divide them into classes. The priv is really an internal structure that surrounds a connection connection representation. The
priv->handler_mutexwraps every event handling dispatched to the upper layer consumer.The connection destruction barriers by acquiring and releasing this
event_handlermutex such that no events are handled by the
Quoted 6 lines
See
drivers/infiniband/core/cma.crdma_destroy_id()In our case, one of the event handlers flushes a workqueue that is hosting work items that essentially call
rdma_destroy_id()on connections that are guaranteed not to be the one currently handling the event. So the priv is guaranteed to be a different instance.
I think the key here would be "are guaranteed not to be the one currently handling the event". How exactly is that guaranteed?
Is there some sort of static guarantee for this?
But I guess the easiest thing to do would be to use mutex_lock_nested() in some places here, or add something like flush_workqueue_nested(). That would let you annotate this place in the code, basically saying - yes, I know I'm doing
CPU0 CPU1mutex_lock(A) start_work(B)flush_work(B) mutex_lock(A)
but it's fine because I know, in this specific instance, that it's really A' not A for the mutex. So if you were to do
mutex_lock_nested(A, SINGLE_DEPTH_NESTING)
around the flush_work() - since your code knows that the flush_work() is actually a different item and guaranteed to not build a graph connecting back to itself, that should work to tell lockdep enough to not complain here.
If this could actually recurse, but not to itself, you'd have to bump the level up each time - which does get difficult since you're executing asynchronously with the work queue. Not sure off the top of my head how I'd solve that.
johannes