From eae031851220f66141d4609d2fad738665ce4c3e Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Thu, 24 Apr 2014 11:11:26 +0000 Subject: [PATCH] ib_srpt: Make srpt_disconnect_ch() close sessions properly that have not yet reached the connected state git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@5472 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- srpt/src/ib_srpt.c | 68 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index a91e01b68..86f5fcbd2 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -2177,23 +2177,6 @@ static void srpt_destroy_ch_ib(struct srpt_rdma_ch *ch) ib_destroy_cq(ch->cq); } -static int srpt_disconnect_ch(struct srpt_rdma_ch *ch) -{ - int ret; - - srpt_set_ch_state(ch, CH_DISCONNECTING); - - if (ch->using_rdma_cm) { - ret = rdma_disconnect(ch->rdma_cm.cm_id); - } else { - ret = ib_send_cm_dreq(ch->ib_cm.cm_id, NULL, 0); - if (ret < 0) - ret = ib_send_cm_drep(ch->ib_cm.cm_id, NULL, 0); - } - - return ret; -} - /** * srpt_close_ch() - Close an RDMA channel. * @@ -2229,6 +2212,36 @@ static bool srpt_close_ch(struct srpt_rdma_ch *ch) return true; } +/* + * Change the channel state into CH_DISCONNECTING. If a channel has not yet + * reached the connected state, close it. If a channel is in the connected + * state, send a DREQ. If a DREQ has been received, send a DREP. Note: it is + * the responsibility of the caller to ensure that this function is not + * invoked concurrently with the code that accepts a connection. This means + * that this function must either be invoked from inside a CM callback + * function or that it must be invoked with the srpt_tgt.mutex held. + */ +static int srpt_disconnect_ch(struct srpt_rdma_ch *ch) +{ + int ret; + + if (!srpt_set_ch_state(ch, CH_DISCONNECTING)) + return -ENOTCONN; + + if (ch->using_rdma_cm) { + ret = rdma_disconnect(ch->rdma_cm.cm_id); + } else { + ret = ib_send_cm_dreq(ch->ib_cm.cm_id, NULL, 0); + if (ret < 0) + ret = ib_send_cm_drep(ch->ib_cm.cm_id, NULL, 0); + } + + if (ret < 0 && srpt_close_ch(ch)) + ret = 0; + + return ret; +} + static void __srpt_close_all_ch(struct srpt_tgt *srpt_tgt) { struct srpt_nexus *nexus; @@ -2647,10 +2660,20 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, rep_param->ib_cm.initiator_depth = 4; } - if (ch->using_rdma_cm) - ret = rdma_accept(rdma_cm_id, &rep_param->rdma_cm); - else - ret = ib_send_cm_rep(ib_cm_id, &rep_param->ib_cm); + /* + * Hold the srpt_tgt mutex while accepting a connection to avoid that + * srpt_disconnect_ch() is invoked concurrently with this code. + */ + mutex_lock(&srpt_tgt->mutex); + if (srpt_tgt->enabled && ch->state == CH_CONNECTING) { + if (ch->using_rdma_cm) + ret = rdma_accept(rdma_cm_id, &rep_param->rdma_cm); + else + ret = ib_send_cm_rep(ib_cm_id, &rep_param->ib_cm); + } else { + ret = -EINVAL; + } + mutex_unlock(&srpt_tgt->mutex); switch (ret) { case 0: @@ -3592,8 +3615,11 @@ static int srpt_detect(struct scst_tgt_template *tp) static int srpt_close_session(struct scst_session *sess) { struct srpt_rdma_ch *ch = scst_sess_get_tgt_priv(sess); + struct srpt_tgt *srpt_tgt = ch->srpt_tgt; + mutex_lock(&srpt_tgt->mutex); srpt_disconnect_ch(ch); + mutex_unlock(&srpt_tgt->mutex); return 0; }