Re: [PATCH] rdma: fix self-deadlock in dtr_cma_event_handler calling rdma_destroy_id

Philipp Reisner <[email protected]> Fri, 5 Jun 2026 15:26:56 +0200
Newsgroups dev.linux.lists.drbd-dev
Message-ID <CADGDV=WgH8dXJt+mwqMjFfjLWThSgqiOkiEDokysrGcg2sFDKw@mail.gmail.com>
Merged. Thanks!

On Thu, May 28, 2026 at 5:24=E2=80=AFAM zhengbing.huang
<[email protected]> wrote:
>
> deadlock stack:
> [<0>] rdma_destroy_id+0x24/0x48 [rdma_cm]
> [<0>] __dtr_destroy_cm+0x118/0x160 [drbd_transport_rdma]
> [<0>] dtr_remove_cm_from_path+0xf4/0x100 [drbd_transport_rdma]
> [<0>] dtr_cma_retry_connect+0x24/0xa0 [drbd_transport_rdma]
> [<0>] dtr_cma_event_handler+0x2fc/0x788 [drbd_transport_rdma]
> [<0>] cma_cm_event_handler+0x38/0xf0 [rdma_cm]
> [<0>] cma_ib_handler+0xa0/0x2c8 [rdma_cm]
> [<0>] cm_process_work+0x30/0x278 [ib_cm]
> [<0>] cm_queue_work_unlock+0x80/0x160 [ib_cm]
> [<0>] cm_rej_handler+0xf4/0x288 [ib_cm]
> [<0>] cm_work_handler+0x108/0x420 [ib_cm]
>
> dtr_cma_event_handler() is called from the IB CM workqueue which holds
> handler_mutex.  On error events (REJECTED, CONNECT_ERROR, etc.) it
> called dtr_cma_retry_connect() -> dtr_remove_cm_from_path() ->
> kref_put(dtr_destroy_cm), which drops the path->cm ref.  When that was
> the last kref, __dtr_destroy_cm() -> rdma_destroy_id() tried to lock
> handler_mutex again, causing self-deadlock.
>
> Fix by taking a shield kref before dtr_cma_retry_connect(), so the
> kref_put inside dtr_remove_cm_from_path() never reaches zero.  The
> shield ref is then released at function exit via dtr_destroy_cm_keep_id
> (which does not call rdma_destroy_id).  In the connecting case, the
> shield ref is dropped explicitly while refcount is still >=3D 2, so
> dtr_destroy_cm is not invoked either.
>
> Signed-off-by: zhengbing.huang <[email protected]>
> ---
>  drbd/drbd_transport_rdma.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/drbd/drbd_transport_rdma.c b/drbd/drbd_transport_rdma.c
> index 9099fee93..e513339a7 100644
> --- a/drbd/drbd_transport_rdma.c
> +++ b/drbd/drbd_transport_rdma.c
> @@ -1313,11 +1313,13 @@ static int dtr_cma_event_handler(struct rdma_cm_i=
d *cm_id, struct rdma_cm_event
>                 // pr_info("event =3D %d, status =3D %d\n", event->event,=
 event->status);
>                 set_bit(DSB_ERROR, &cm->state);
>
> +               /* shield ref: prevent the last kref put calling rdma_des=
troy_id() here */
> +               kref_get(&cm->kref);
>                 dtr_cma_retry_connect(cm->path, cm);
>                 connecting =3D test_and_clear_bit(DSB_CONNECTING, &cm->st=
ate) ||
>                         test_and_clear_bit(DSB_CONNECT_REQ, &cm->state);
> -               if (!connecting)
> -                       return 0; /* keep ref; __dtr_disconnect_path() wo=
n */
> +               if (connecting)
> +                       kref_put(&cm->kref, dtr_destroy_cm);
>                 break;
>
>         case RDMA_CM_EVENT_DISCONNECTED:
> --
> 2.43.0
>