iwarp device removal deadlock fix
Subsystems (3)
cover iwarp device removal deadlock fix
This RFC series attempts to address the deadlock issue discovered while testing nvmf/rdma handling rdma device removal events from the rdma_cm. For a discussion of the deadlock that can happen, see
http://lists.infradead.org/pipermail/linux-nvme/2016-July/005440.html.
For my description of the deadlock itself, see this post in the above thread:
http://lists.infradead.org/pipermail/linux-nvme/2016-July/005465.html
In a nutshell, iw_cxgb4 and the iw_cm block during qp/cm_id destruction until all references are removed. This combined with the iwarp CM passing disconnect events up to the rdma_cm during disconnect and/or qp/cm_id destruction leads to a deadlock.
My proposed solution is to remove the need for iw_cxgb4 and iw_cm to block during object destruction for the recnts to reach 0, but rather to let the freeing of the object memory be deferred when the last deref is done. This allows all the qps/cm_ids to be destroyed without blocking, and all the object memory freeing ends up happinging when the application's device_remove event handler function returns to the rdma_cm.
Sean, I was hoping you could have a look at the iwcm.c patch particularly, to tell my why its broken. :) I spent some time trying to figure out why we really need the CALLBACK_DESTROY flag, but I concluded it really isn't needed. The one side effect I see with my change, is that the application could possibly get a cm_id event after it has destroyed the cm_id. There probably is a way to discard events that have a reference on the cm_id but get processed after the app has destoyed the cm_id by having a new flag indicating "destroyed by app".
I've included Sagi's proposed nvme-rdma patch to fix the existing touch-after-free problem with the device removal event handler function that brought about this deadlock analysis. Also this series would be the way to submit the final fix for all this (assuming my proposal isn't broken too badly).
Thanks,
Steve.
---
Sagi Grimberg (1):
nvme-rdma: Fix device removal handling
Steve Wise (2):
iw_cm: free cm_id resources on the last deref
iw_cxgb4: don't block in destroy_qp awaiting the last deref
drivers/infiniband/core/iwcm.c | 41 +++++----------------------- drivers/infiniband/hw/cxgb4/iw_cxgb4.h | 2 +- drivers/infiniband/hw/cxgb4/qp.c | 21 ++++++++++----- drivers/nvme/host/rdma.c | 49 +++++++++++++++++++--------------- 4 files changed, 48 insertions(+), 65 deletions(-)
Signature · 1 line
2.7.0
PATCH 1/3 iw_cm: free cm_id resources on the last deref
Remove the complicated logic to free the cm_id resources in iw_cm event handlers vs when an application thread destroys the device. I'm not sure why this code was written, but simply allowing the last deref to free the memory is cleaner. It also prevents a deadlock when applications try to destroy cm_id's in their cm event handler function.
--- drivers/infiniband/core/iwcm.c | 41 ++++++----------------------------------- 1 file changed, 6 insertions(+), 35 deletions(-)
drivers/infiniband/core/iwcm.c +6 -35
@@ -183,15 +183,14 @@ static void free_cm_id(struct iwcm_id_private *cm_id_priv)/* * Release a reference on cm_id. If the last reference is being -* released, enable the waiting thread (in iw_destroy_cm_id) to -* get woken up, and return 1 if a thread is already waiting. +* released, free the cm_id and return 1. */ static int iwcm_deref_id (struct iwcm_id_private *cm_id_priv) {BUG_ON (atomic_read (&cm_id_priv->refcount)==0 );if (atomic_dec_and_test (&cm_id_priv->refcount)) {BUG_ON (!list_empty (&cm_id_priv->work_list)); -complete (&cm_id_priv->destroy_comp); +free_cm_id (cm_id_priv);return 1 ; }
@@ -208,19 +207,10 @@ static void add_ref(struct iw_cm_id *cm_id)static void rem_ref (struct iw_cm_id *cm_id) {struct iwcm_id_private *cm_id_priv; -int cb_destroy; cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); -/* -* Test bit before deref in case the cm_id gets freed on another -* thread. -*/ - cb_destroy =test_bit (IWCM_F_CALLBACK_DESTROY , &cm_id_priv->flags); -if (iwcm_deref_id (cm_id_priv) && cb_destroy) { -BUG_ON (!list_empty (&cm_id_priv->work_list)); -free_cm_id (cm_id_priv); - } + (void )iwcm_deref_id (cm_id_priv); }static int cm_event_handler (struct iw_cm_id *cm_id,struct iw_cm_event *event);
@@ -433,13 +423,7 @@ void iw_destroy_cm_id(struct iw_cm_id *cm_id)struct iwcm_id_private *cm_id_priv; cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); -BUG_ON (test_bit (IWCM_F_CALLBACK_DESTROY , &cm_id_priv->flags)); -destroy_cm_id (cm_id); - -wait_for_completion (&cm_id_priv->destroy_comp); - -free_cm_id (cm_id_priv); }EXPORT_SYMBOL (iw_destroy_cm_id);
@@ -809,10 +793,7 @@ static void cm_conn_req_handler(struct iwcm_id_private *listen_id_priv, ret = cm_id->cm_handler (cm_id, iw_event);if (ret) {iw_cm_reject (cm_id,NULL ,0 ); -set_bit (IWCM_F_CALLBACK_DESTROY , &cm_id_priv->flags); -destroy_cm_id (cm_id); -if (atomic_read (&cm_id_priv->refcount)==0 ) -free_cm_id (cm_id_priv); +iw_destroy_cm_id (cm_id); } out:
@@ -1000,7 +981,6 @@ static void cm_work_handler(struct work_struct *_work)unsigned long flags;int empty;int ret =0 ; -int destroy_id;spin_lock_irqsave (&cm_id_priv->lock, flags); empty =list_empty (&cm_id_priv->work_list);
@@ -1014,19 +994,10 @@ static void cm_work_handler(struct work_struct *_work)spin_unlock_irqrestore (&cm_id_priv->lock, flags); ret =process_event (cm_id_priv, &levent); -if (ret) { -set_bit (IWCM_F_CALLBACK_DESTROY , &cm_id_priv->flags); +if (ret)destroy_cm_id (&cm_id_priv->id); - } -BUG_ON (atomic_read (&cm_id_priv->refcount)==0 ); - destroy_id =test_bit (IWCM_F_CALLBACK_DESTROY , &cm_id_priv->flags); -if (iwcm_deref_id (cm_id_priv)) { -if (destroy_id) { -BUG_ON (!list_empty (&cm_id_priv->work_list)); -free_cm_id (cm_id_priv); - } +if (iwcm_deref_id (cm_id_priv))return ; - }if (empty)return ;spin_lock_irqsave (&cm_id_priv->lock, flags);
Signature · 1 line
2.7.0
Sagi Grimberg
maintainerPATCH 1/3 iw_cm: free cm_id resources on the last deref
Quoted 6 lines
Remove the complicated logic to free the
cm_idresources iniw_cmevent handlers vs when an application thread destroys the device. I'm not sure why this code was written, but simply allowing the last deref to free the memory is cleaner. It also prevents a deadlock when applications try to destroy cm_id's in their cm event handler function.
The description here is misleading. we can never destroy the cm_id inside the cm_id handler. Also, I don't think the deadlock was on cm_id removal but rather on the qp referenced by the cm_id. I think the change log can be improved.
The patch looks fine to me.
PATCH 1/3 iw_cm: free cm_id resources on the last deref
Quoted 8 lines
Quoted 7 lines
Remove the complicated logic to free the
cm_idresources iniw_cmevent handlers vs when an application thread destroys the device. I'm not sure why this code was written, but simply allowing the last deref to free the memory is cleaner. It also prevents a deadlock when applications try to destroy cm_id's in their cm event handler function.
Quoted 5 lines
The description here is misleading. we can never destroy the
cm_idinside thecm_idhandler. Also, I don't think the deadlock was oncm_idremoval but rather on the qp referenced by thecm_id. I think the change log can be improved.
I'll reword it.
Quoted 1 line
The patch looks fine to me.
PATCH 1/3 iw_cm: free cm_id resources on the last deref
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
Remove the complicated logic to free the
cm_idresources iniw_cmevent handlers vs when an application thread destroys the device. I'm not sure why this code was written, but simply allowing the last deref to free the memory is cleaner. It also prevents a deadlock when applications try to destroy cm_id's in their cm event handler function.
Quoted 7 lines
Quoted 5 lines
The description here is misleading. we can never destroy the
cm_idinside thecm_idhandler. Also, I don't think the deadlock was oncm_idremoval but rather on the qp referenced by thecm_id. I think the change log can be improved.I'll reword it.
The nvme unplug handler does indeed destroy all the qps -and- cm_ids used for the controllers for this device, with the exception of the cm_id handling the event. That is what causes this deadlock. Once I fixed iw_cxgb4 (in patch 2) to not block until the refcnt reaches 0 in c4iw_destroy_qp(), I then hit the block in iw_destroy_cm_id() which deadlocks the process due to the iw_cm worker thread already stuck trying to post an event to the rdma_cm for the cm_id handling the event.
Perhaps I should describe the deadlock in detail like I did in the email threads leading up to this series?
While I'm rambling, there is still a condition that probably needs to be
addressed: if the application event handler function disconnects the cm_id that is handling the event, the iw_cm workq thread gets stuck posting a IW_CM_EVENT_CLOSE to rdma_cm. So the iw_cm workq thread is stuck in cm_close_handler() calling cm_id_priv->id.cm_handler() which is cma_iw_handler() which is blocked in cma_disable_callback() because the application is currently running its event handler for this cm_id. This block is released when the application returns from its event handler function.
But maybe cma_iw_handler() should queue the event if it cannot deliver it, vs blocking the iw_cm workq thread?
Steve.
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH 1/3 iw_cm: free cm_id resources on the last deref
Quoted 10 lines
While I'm rambling, there is still a condition that probably needs to be
addressed: if the application event handler function disconnects thecm_idthat is handling the event, theiw_cmworkq thread gets stuck posting aIW_CM_EVENT_CLOSEtordma_cm. So theiw_cmworkq thread is stuck incm_close_handler()callingcm_id_priv->id.cm_handler()which iscma_iw_handler()which is blocked incma_disable_callback()because the application is currently running its event handler for thiscm_id. This block is released when the application returns from its event handler function.
Quoted 3 lines
But maybe
cma_iw_handler()should queue the event if it cannot deliver it, vs blocking theiw_cmworkq thread?
Answering myself: queueing the event seems to be a no-go because the rdma_cm can return non-zero to the iw_cm to indicate it should destroy the iw_cm_id. This cannot be done correctly if the event is queued/deferred.
PATCH 2/3 iw_cxgb4: don't block in destroy_qp awaiting the last deref
In reply to Steve WiseBlocking in c4iw_destroy_qp() causes a deadlock when apps destroy a qp or disconnect a cm_id from their cm event handler function. There is no nead to block here anyway, so just replace the refcnt atomic with a kref object and free the memory on the last put.
--- drivers/infiniband/hw/cxgb4/iw_cxgb4.h | 2 +- drivers/infiniband/hw/cxgb4/qp.c | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-)
drivers/infiniband/hw/cxgb4/iw_cxgb4.h +1 -1
@@ -472,7 +472,7 @@ struct c4iw_qp {struct t4_wq wq;spinlock_t lock;struct mutex mutex; -atomic_t refcnt; +struct kref kref;wait_queue_head_t wait;struct timer_list timer;int sq_sig_all;
drivers/infiniband/hw/cxgb4/qp.c +14 -7
@@ -683,17 +683,25 @@ static int build_inv_stag(union t4_wr *wqe, struct ib_send_wr *wr,return 0 ; } +void _free_qp (struct kref *kref) +{ +struct c4iw_qp *qhp; + + qhp = container_of(kref, struct c4iw_qp, kref); +PDBG ("%s qhp %p\n" , __func__, qhp); +kfree (qhp); +} +void c4iw_qp_add_ref (struct ib_qp *qp) {PDBG ("%s ib_qp %p\n" , __func__, qp); -atomic_inc (&(to_c4iw_qp (qp)->refcnt)); +kref_get (&to_c4iw_qp (qp)->kref); }void c4iw_qp_rem_ref (struct ib_qp *qp) {PDBG ("%s ib_qp %p\n" , __func__, qp); -if (atomic_dec_and_test (&(to_c4iw_qp (qp)->refcnt))) -wake_up (&(to_c4iw_qp (qp)->wait)); +kref_put (&to_c4iw_qp (qp)->kref, _free_qp); }static void add_to_fc_list (struct list_head *head,struct list_head *entry)
@@ -1592,8 +1600,6 @@ int c4iw_destroy_qp(struct ib_qp *ib_qp)wait_event (qhp->wait, !qhp->ep);remove_handle (rhp, &rhp->qpidr, qhp->wq.sq.qid); -atomic_dec (&qhp->refcnt); -wait_event (qhp->wait, !atomic_read (&qhp->refcnt));spin_lock_irq (&rhp->lock);if (!list_empty (&qhp->db_fc_entry))
@@ -1606,8 +1612,9 @@ int c4iw_destroy_qp(struct ib_qp *ib_qp)destroy_qp (&rhp->rdev, &qhp->wq, ucontext ? &ucontext->uctx : &rhp->rdev.uctx); +c4iw_qp_rem_ref (ib_qp); +PDBG ("%s ib_qp %p qpid 0x%0x\n" , __func__, ib_qp, qhp->wq.sq.qid); -kfree (qhp);return 0 ; }
@@ -1704,7 +1711,7 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,init_completion (&qhp->rq_drained);mutex_init (&qhp->mutex);init_waitqueue_head (&qhp->wait); -atomic_set (&qhp->refcnt,1 ); +kref_init (&qhp->kref); ret =insert_handle (rhp, &rhp->qpidr, qhp, qhp->wq.sq.qid);if (ret)
Signature · 1 line
2.7.0
Sagi Grimberg
maintainerPATCH 2/3 iw_cxgb4: don't block in destroy_qp awaiting the last deref
Looks good Steve!
Sagi Grimberg
maintainerPATCH 3/3 nvme-rdma: Fix device removal handling
In reply to Steve WiseDevice removal sequence may have crashed because the controller (and admin queue space) was freed before we destroyed the admin queue resources. Thus we want to destroy the admin queue and only then queue controller deletion and wait for it to complete.
More specifically we:
1. own the controller deletion (make sure we are not competing with another deletion).
2. get rid of inflight reconnects if exists (which also destroy and create queues).
3. destroy the queue.
4. safely queue controller deletion (and wait for it to complete).
--- drivers/nvme/host/rdma.c | 49 ++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-)
drivers/nvme/host/rdma.c +27 -22
@@ -169,7 +169,6 @@ MODULE_PARM_DESC(register_always,static int nvme_rdma_cm_handler (struct rdma_cm_id *cm_id,struct rdma_cm_event *event);static void nvme_rdma_recv_done (struct ib_cq *cq,struct ib_wc *wc); -static int __nvme_rdma_del_ctrl (struct nvme_rdma_ctrl *ctrl);/* XXX: really should move to a generic header sooner or later.. */ static inline void put_unaligned_le24 (u32 val,u8 *p)
@@ -1318,37 +1317,43 @@ out_destroy_queue_ib:* that caught the event. Since we hold the callout until the controller * deletion is completed, we'll deadlock if the controller deletion will * call rdma_destroy_id on this queue's cm_id. Thus, we claim ownership -* of destroying this queue before-hand, destroy the queue resources -* after the controller deletion completed with the exception of destroying -* the cm_id implicitely by returning a non-zero rc to the callout. +* of destroying this queue before-hand, destroy the queue resources, +* then queue the controller deletion which won't destroy this queue and +* we destroy the cm_id implicitely by returning a non-zero rc to the callout. */ static int nvme_rdma_device_unplug (struct nvme_rdma_queue *queue) {struct nvme_rdma_ctrl *ctrl = queue->ctrl; -int ret, ctrl_deleted =0 ; +int ret; -/* First disable the queue so ctrl delete won't free it */ -if (!test_and_clear_bit (NVME_RDMA_Q_CONNECTED , &queue->flags)) - goto out; +/* Own the controller deletion */ +if (!nvme_change_ctrl_state (&ctrl->ctrl,NVME_CTRL_DELETING )) +return 0 ; -/* delete the controller */ - ret =__nvme_rdma_del_ctrl (ctrl); -if (!ret) { -dev_warn (ctrl->ctrl.device, -"Got rdma device removal event, deleting ctrl\n" ); -flush_work (&ctrl->delete_work); +dev_warn (ctrl->ctrl.device, +"Got rdma device removal event, deleting ctrl\n" ); -/* Return non-zero so the cm_id will destroy implicitly */ - ctrl_deleted =1 ; +/* Get rid of reconnect work if its running */ +cancel_delayed_work_sync (&ctrl->reconnect_work); -/* Free this queue ourselves */ -rdma_disconnect (queue->cm_id); -ib_drain_qp (queue->qp); -nvme_rdma_destroy_queue_ib (queue); +/* Disable the queue so ctrl delete won't free it */ +if (!test_and_clear_bit (NVME_RDMA_Q_CONNECTED , &queue->flags)) { + ret =0 ; + goto queue_delete; } -out: -return ctrl_deleted; +/* Free this queue ourselves */ +nvme_rdma_stop_queue (queue); +nvme_rdma_destroy_queue_ib (queue); + +/* Return non-zero so the cm_id will destroy implicitly */ + ret =1 ; + +queue_delete: +/* queue controller deletion */ +queue_work (nvme_rdma_wq, &ctrl->delete_work); +flush_work (&ctrl->delete_work); +return ret; } static int nvme_rdma_cm_handler(struct rdma_cm_id *cm_id,
Signature · 1 line
2.7.0
Christoph Hellwig
maintainerPATCH 3/3 nvme-rdma: Fix device removal handling
Quoted 66 lines · Sagi Grimberg
Device removal sequence may have crashed because the controller (and admin queue space) was freed before we destroyed the admin queue resources. Thus we want to destroy the admin queue and only then queue controller deletion and wait for it to complete.
More specifically we:
1. own the controller deletion (make sure we are not competing with another deletion).
2. get rid of inflight reconnects if exists (which also destroy and create queues).
3. destroy the queue.
4. safely queue controller deletion (and wait for it to complete).Signed-off-by Sagi Grimberg <sagi at grimberg.me>--- drivers/nvme/host/rdma.c | 49 ++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-)drivers/nvme/host/rdma.c +17
-4@@ -169,7 +169,6 @@ MODULE_PARM_DESC(register_always,static int nvme_rdma_cm_handler (struct rdma_cm_id *cm_id,struct rdma_cm_event *event);static void nvme_rdma_recv_done (struct ib_cq *cq,struct ib_wc *wc); -static int __nvme_rdma_del_ctrl (struct nvme_rdma_ctrl *ctrl);/* XXX: really should move to a generic header sooner or later.. */ static inline void put_unaligned_le24 (u32 val,u8 *p)@@ -1318,37 +1317,43 @@ out_destroy_queue_ib:* that caught the event. Since we hold the callout until the controller * deletion is completed, we'll deadlock if the controller deletion will * call rdma_destroy_id on this queue's cm_id. Thus, we claim ownership -* of destroying this queue before-hand, destroy the queue resources -* after the controller deletion completed with the exception of destroying -* the cm_id implicitely by returning a non-zero rc to the callout. +* of destroying this queue before-hand, destroy the queue resources, +* then queue the controller deletion which won't destroy this queue and +* we destroy the cm_id implicitely by returning a non-zero rc to the callout. */ static int nvme_rdma_device_unplug (struct nvme_rdma_queue *queue) {struct nvme_rdma_ctrl *ctrl = queue->ctrl; +int ret; +/* Own the controller deletion */ +if (!nvme_change_ctrl_state (&ctrl->ctrl,NVME_CTRL_DELETING )) +return 0 ; +dev_warn (ctrl->ctrl.device, +"Got rdma device removal event, deleting ctrl\n" ); +/* Get rid of reconnect work if its running */ +cancel_delayed_work_sync (&ctrl->reconnect_work); +/* Disable the queue so ctrl delete won't free it */ +if (!test_and_clear_bit (NVME_RDMA_Q_CONNECTED , &queue->flags)) { + ret =0 ; + goto queue_delete; } +/* Free this queue ourselves */ +nvme_rdma_stop_queue (queue);
drivers/nvme/host/rdma.c out_destroy_queue_ib: · line 1356
Quoted 10 lines
drivers/nvme/host/rdma.c +10
-0+nvme_rdma_destroy_queue_ib (queue); + +/* Return non-zero so the cm_id will destroy implicitly */ + ret =1 ; + +queue_delete: +/* queue controller deletion */ +queue_work (nvme_rdma_wq, &ctrl->delete_work); +flush_work (&ctrl->delete_work); +return ret;
Seems like we should be able to just skip the goto here:
/* Disable the queue so ctrl delete won't free it */
if (test_and_clear_bit(NVME_RDMA_Q_CONNECTED, &queue->flags)) {
/* Free this queue ourselves */
nvme_rdma_stop_queue(queue);
nvme_rdma_destroy_queue_ib(queue);
/* Return non-zero so the cm_id will destroy implicitly */
ret = 1;
}
/* queue controller deletion */
queue_work(nvme_rdma_wq, &ctrl->delete_work);
flush_work(&ctrl->delete_work);
return ret;
}And that opportunity could used to improve the comment for that if connected stop queue case a bit as well.
Otherwise this looks reasonable to me.
PATCH 3/3 nvme-rdma: Fix device removal handling
In reply to Sagi GrimbergQuoted 15 lines
Device removal sequence may have crashed because the controller (and admin queue space) was freed before we destroyed the admin queue resources. Thus we want to destroy the admin queue and only then queue controller deletion and wait for it to complete.
More specifically we:
1. own the controller deletion (make sure we are not competing with another deletion).
2. get rid of inflight reconnects if exists (which also destroy and create queues).
3. destroy the queue.
4. safely queue controller deletion (and wait for it to complete).
Quoted 3 lines
Signed-off-by Sagi Grimberg <sagi at grimberg.me>--- drivers/nvme/host/rdma.c | 49
++++++++++++++++++++++++++----------------------
Quoted 15 lines
1 file changed, 27 insertions(+), 22 deletions(-)drivers/nvme/host/rdma.c +0
-1@@ -169,7 +169,6 @@ MODULE_PARM_DESC(register_always,static int nvme_rdma_cm_handler (struct rdma_cm_id *cm_id,struct rdma_cm_event *event);static void nvme_rdma_recv_done (struct ib_cq *cq,struct ib_wc *wc); -static int __nvme_rdma_del_ctrl (struct nvme_rdma_ctrl *ctrl);/* XXX: really should move to a generic header sooner or later.. */ static inline void put_unaligned_le24 (u32 val,u8 *p)
drivers/nvme/host/rdma.c out_destroy_queue_ib: · line 1322
Quoted 9 lines
drivers/nvme/host/rdma.c +3
-3@@ -1318,37 +1317,43 @@ out_destroy_queue_ib:* that caught the event. Since we hold the callout until the controller * deletion is completed, we'll deadlock if the controller deletion will * call rdma_destroy_id on this queue's cm_id. Thus, we claim ownership -* of destroying this queue before-hand, destroy the queue resources -* after the controller deletion completed with the exception of destroying -* the cm_id implicitely by returning a non-zero rc to the callout. +* of destroying this queue before-hand, destroy the queue resources, +* then queue the controller deletion which won't destroy this queue and +* we destroy the cm_id implicitely by returning a non-zero rc to the
callout.
Quoted 41 lines
drivers/nvme/host/rdma.c +13
-18*/ static int nvme_rdma_device_unplug (struct nvme_rdma_queue *queue) {struct nvme_rdma_ctrl *ctrl = queue->ctrl; -int ret, ctrl_deleted =0 ; +int ret; -/* First disable the queue so ctrl delete won't free it */ -if (!test_and_clear_bit (NVME_RDMA_Q_CONNECTED , &queue->flags)) - goto out; +/* Own the controller deletion */ +if (!nvme_change_ctrl_state (&ctrl->ctrl,NVME_CTRL_DELETING )) +return 0 ; -/* delete the controller */ - ret =__nvme_rdma_del_ctrl (ctrl); -if (!ret) { -dev_warn (ctrl->ctrl.device, -"Got rdma device removal event, deleting ctrl\n" ); -flush_work (&ctrl->delete_work); +dev_warn (ctrl->ctrl.device, +"Got rdma device removal event, deleting ctrl\n" ); -/* Return non-zero so the cm_id will destroy implicitly */ - ctrl_deleted =1 ; +/* Get rid of reconnect work if its running */ +cancel_delayed_work_sync (&ctrl->reconnect_work); -/* Free this queue ourselves */ -rdma_disconnect (queue->cm_id); -ib_drain_qp (queue->qp); -nvme_rdma_destroy_queue_ib (queue); +/* Disable the queue so ctrl delete won't free it */ +if (!test_and_clear_bit (NVME_RDMA_Q_CONNECTED , &queue->flags)) { + ret =0 ; + goto queue_delete; } -out: -return ctrl_deleted; +/* Free this queue ourselves */
drivers/nvme/host/rdma.c out_destroy_queue_ib: · line 1355
Quoted 10 lines
drivers/nvme/host/rdma.c +10
-0+nvme_rdma_stop_queue (queue); +nvme_rdma_destroy_queue_ib (queue); + +/* Return non-zero so the cm_id will destroy implicitly */ + ret =1 ; + +queue_delete: +/* queue controller deletion */ +queue_work (nvme_rdma_wq, &ctrl->delete_work); +flush_work (&ctrl->delete_work);
Actually, since the queue_work() fires off the workq thread to delete the controller and its resources (on another cpu potentially), and think the flush_work() could end up being a touch-after-free because it accesses *ctrl, no?
Sagi Grimberg
maintainercover iwarp device removal deadlock fix
In reply to Steve WiseQuoted 3 lines
This RFC series attempts to address the deadlock issue discovered while testing nvmf/rdma handling rdma device removal events from the
rdma_cm.
Thanks for doing this Steve!
Quoted 13 lines
For a discussion of the deadlock that can happen, see
http://lists.infradead.org/pipermail/linux-nvme/2016-July/005440.html.
For my description of the deadlock itself, see this post in the above thread:
http://lists.infradead.org/pipermail/linux-nvme/2016-July/005465.html
In a nutshell,
iw_cxgb4and theiw_cmblock during qp/cm_id destruction until all references are removed. This combined with the iwarp CM passing disconnect events up to therdma_cmduring disconnect and/or qp/cm_id destruction leads to a deadlock.
Quoted 6 lines
My proposed solution is to remove the need for
iw_cxgb4andiw_cmto block during object destruction for the recnts to reach 0, but rather to let the freeing of the object memory be deferred when the last deref is done. This allows all the qps/cm_ids to be destroyed without blocking, and all the object memory freeing ends up happinging when the application'sdevice_removeevent handler function returns to therdma_cm.
This sounds like a very good approach moving forward.
Quoted 8 lines
Sean, I was hoping you could have a look at the
iwcm.cpatch particularly, to tell my why its broken. :) I spent some time trying to figure out why we really need theCALLBACK_DESTROYflag, but I concluded it really isn't needed. The one side effect I see with my change, is that the application could possibly get acm_idevent after it has destroyed thecm_id. There probably is a way to discard events that have a reference on thecm_idbut get processed after the app has destoyed thecm_idby having a new flag indicating "destroyed by app".
That sounds easy enough. Does this mean that iwcm relies on the driver to do this or is it inter-operable with the existing logic? If not this will need to take care of all the iWARP drivers.
cover iwarp device removal deadlock fix
Quoted 11 lines
Quoted 3 lines
This RFC series attempts to address the deadlock issue discovered while testing nvmf/rdma handling rdma device removal events from the
rdma_cm.Thanks for doing this Steve!
Quoted 5 lines
For a discussion of the deadlock that can happen, see
http://lists.infradead.org/pipermail/linux-nvme/2016-July/005440.html.
For my description of the deadlock itself, see this post in the above
thread:
Quoted 9 lines
Quoted 6 lines
http://lists.infradead.org/pipermail/linux-nvme/2016-July/005465.html
In a nutshell,
iw_cxgb4and theiw_cmblock during qp/cm_id destruction until all references are removed. This combined with the iwarp CM passing disconnect events up to therdma_cmduring disconnect and/or qp/cm_iddestruction
Quoted 2 lines
leads to a deadlock.
Quoted 18 lines
Quoted 6 lines
My proposed solution is to remove the need for
iw_cxgb4andiw_cmto block during object destruction for the recnts to reach 0, but rather to let the freeing of the object memory be deferred when the last deref is done. This allows all the qps/cm_ids to be destroyed without blocking, and all the object memory freeing ends up happinging when the application'sdevice_removeevent handler function returns to therdma_cm.This sounds like a very good approach moving forward.
Quoted 8 lines
Sean, I was hoping you could have a look at the
iwcm.cpatch particularly, to tell my why its broken. :) I spent some time trying to figure out why we really need theCALLBACK_DESTROYflag, but I concluded it really isn't needed. The one side effect I see with my change, is that the application could possibly get acm_idevent after it has destroyed thecm_id. There probably is a way to discard events that have a reference on thecm_idbut get processed after the app has destoyed thecm_idby having a new flag indicating "destroyed by app".
By the way, I think Sean is on sabbatical until 9/12.
Quoted 3 lines
That sounds easy enough. Does this mean that iwcm relies on the driver to do this or is it inter-operable with the existing logic? If not this will need to take care of all the iWARP drivers.
This can be handled all in the iw_cm module. In fact, I'm testing a new version of the iw_cm patch now.
Steve.