Protect the channel state against concurrent access via locking instead
of atomic operations.

Signed-off-by: Bart Van Assche <[email protected]>
---
 drivers/scst/srpt/ib_srpt.c |   62 ++++++++++++++++++++++++++++++++----------
 drivers/scst/srpt/ib_srpt.h |    4 +-
 2 files changed, 49 insertions(+), 17 deletions(-)

diff --git a/drivers/scst/srpt/ib_srpt.c b/drivers/scst/srpt/ib_srpt.c
index 66f11f6..edd90f1 100644
--- a/drivers/scst/srpt/ib_srpt.c
+++ b/drivers/scst/srpt/ib_srpt.c
@@ -151,6 +151,30 @@ static struct ib_client srpt_client = {
        .remove = srpt_remove_one
 };
 
+static enum rdma_ch_state srpt_get_ch_state(struct srpt_rdma_ch *ch)
+{
+       unsigned long flags;
+       enum rdma_ch_state state;
+
+       spin_lock_irqsave(&ch->spinlock, flags);
+       state = ch->state;
+       spin_unlock_irqrestore(&ch->spinlock, flags);
+       return state;
+}
+
+static enum rdma_ch_state
+srpt_set_ch_state(struct srpt_rdma_ch *ch, enum rdma_ch_state new_state)
+{
+       unsigned long flags;
+       enum rdma_ch_state prev;
+
+       spin_lock_irqsave(&ch->spinlock, flags);
+       prev = ch->state;
+       ch->state = new_state;
+       spin_unlock_irqrestore(&ch->spinlock, flags);
+       return prev;
+}
+
 /**
  * srpt_test_and_set_channel_state() - Test and set the channel state.
  *
@@ -166,7 +190,15 @@ srpt_test_and_set_channel_state(struct srpt_rdma_ch *ch,
                                enum rdma_ch_state old,
                                enum rdma_ch_state new)
 {
-       return atomic_cmpxchg(&ch->state, old, new) == old;
+       unsigned long flags;
+       enum rdma_ch_state prev;
+
+       spin_lock_irqsave(&ch->spinlock, flags);
+       prev = ch->state;
+       if (prev == old)
+               ch->state = new;
+       spin_unlock_irqrestore(&ch->spinlock, flags);
+       return prev == old;
 }
 
 /**
@@ -237,7 +269,7 @@ static void srpt_qp_event(struct ib_event *event, struct 
srpt_rdma_ch *ch)
 {
        TRACE_DBG("QP event %d on cm_id=%p sess_name=%s state=%d",
                  event->event, ch->cm_id, ch->sess_name,
-                 atomic_read(&ch->state));
+                 srpt_get_ch_state(ch));
 
        switch (event->event) {
        case IB_EVENT_COMM_EST:
@@ -1668,7 +1700,7 @@ static void srpt_handle_new_iu(struct srpt_rdma_ch *ch,
                                   recv_ioctx->ioctx.dma, srp_max_req_size,
                                   DMA_FROM_DEVICE);
 
-       ch_state = atomic_read(&ch->state);
+       ch_state = srpt_get_ch_state(ch);
        srp_cmd = recv_ioctx->ioctx.buf;
        if (unlikely(ch_state == CH_CONNECTING)) {
                list_add_tail(&recv_ioctx->wait_list, &ch->cmd_wait_list);
@@ -1796,7 +1828,7 @@ static void srpt_process_send_completion(struct ib_cq *cq,
 
        while (unlikely(opcode == IB_WC_SEND
                        && !list_empty(&ch->cmd_wait_list)
-                       && atomic_read(&ch->state) == CH_LIVE
+                       && srpt_get_ch_state(ch) == CH_LIVE
                        && (send_ioctx = srpt_get_send_ioctx(ch)) != NULL)) {
                struct srpt_recv_ioctx *recv_ioctx;
 
@@ -1988,7 +2020,7 @@ static void srpt_unregister_channel(struct srpt_rdma_ch 
*ch)
 
        sdev = ch->sport->sdev;
        list_del(&ch->list);
-       atomic_set(&ch->state, CH_DISCONNECTING);
+       srpt_set_ch_state(ch, CH_DISCONNECTING);
        spin_unlock_irq(&sdev->spinlock);
 
        ret = srpt_ch_qp_err(ch);
@@ -2088,7 +2120,7 @@ static void srpt_release_channel(struct scst_session 
*scst_sess)
 
        ch = scst_sess_get_tgt_priv(scst_sess);
        BUG_ON(!ch);
-       WARN_ON(atomic_read(&ch->state) != CH_DISCONNECTING);
+       WARN_ON(srpt_get_ch_state(ch) != CH_DISCONNECTING);
 
        TRACE_DBG("destroying cm_id %p", ch->cm_id);
        BUG_ON(!ch->cm_id);
@@ -2230,9 +2262,9 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id,
                                TRACE_DBG("Found existing channel name= %s"
                                          " cm_id= %p state= %d",
                                          ch->sess_name, ch->cm_id,
-                                         atomic_read(&ch->state));
+                                         srpt_get_ch_state(ch));
 
-                               prev_state = atomic_xchg(&ch->state,
+                               prev_state = srpt_set_ch_state(ch,
                                                CH_DISCONNECTING);
                                if (prev_state == CH_CONNECTING)
                                        srpt_unregister_channel(ch);
@@ -2297,10 +2329,10 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id,
         */
        ch->rq_size = min(SRPT_RQ_SIZE, scst_get_max_lun_commands(NULL, 0));
        atomic_set(&ch->processing_compl, 0);
-       atomic_set(&ch->state, CH_CONNECTING);
+       spin_lock_init(&ch->spinlock);
+       ch->state = CH_CONNECTING;
        INIT_LIST_HEAD(&ch->cmd_wait_list);
 
-       spin_lock_init(&ch->spinlock);
        ch->ioctx_ring = (struct srpt_send_ioctx **)
                srpt_alloc_ioctx_ring(ch->sport->sdev, ch->rq_size,
                                      sizeof(*ch->ioctx_ring[0]),
@@ -2408,7 +2440,7 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id,
        goto out;
 
 release_channel:
-       atomic_set(&ch->state, CH_DISCONNECTING);
+       srpt_set_ch_state(ch, CH_DISCONNECTING);
        scst_unregister_session(ch->scst_sess, 0, NULL);
        ch->scst_sess = NULL;
 
@@ -2477,7 +2509,7 @@ static void srpt_cm_rtu_recv(struct ib_cm_id *cm_id)
                        CH_DISCONNECTING)) {
                        TRACE_DBG("cm_id=%p sess_name=%s state=%d",
                                  cm_id, ch->sess_name,
-                                 atomic_read(&ch->state));
+                                 srpt_get_ch_state(ch));
                        ib_send_cm_dreq(ch->cm_id, NULL, 0);
                }
        }
@@ -2512,9 +2544,9 @@ static void srpt_cm_dreq_recv(struct ib_cm_id *cm_id)
                goto out;
        }
 
-       TRACE_DBG("cm_id= %p ch->state= %d", cm_id, atomic_read(&ch->state));
+       TRACE_DBG("cm_id= %p ch->state= %d", cm_id, srpt_get_ch_state(ch));
 
-       switch (atomic_read(&ch->state)) {
+       switch (srpt_get_ch_state(ch)) {
        case CH_LIVE:
        case CH_CONNECTING:
                ib_send_cm_drep(ch->cm_id, NULL, 0);
@@ -2985,7 +3017,7 @@ static int srpt_rdy_to_xfer(struct scst_cmd *scmnd)
        WARN_ON(ch != scst_sess_get_tgt_priv(scst_cmd_get_session(scmnd)));
        BUG_ON(!ch);
 
-       ch_state = atomic_read(&ch->state);
+       ch_state = srpt_get_ch_state(ch);
        if (ch_state == CH_DISCONNECTING) {
                TRACE_DBG("cmd with tag %lld: channel disconnecting",
                          scst_cmd_get_tag(scmnd));
diff --git a/drivers/scst/srpt/ib_srpt.h b/drivers/scst/srpt/ib_srpt.h
index 8f69345..e14d192 100644
--- a/drivers/scst/srpt/ib_srpt.h
+++ b/drivers/scst/srpt/ib_srpt.h
@@ -256,7 +256,7 @@ enum rdma_ch_state {
  * @cmd_wait_list: list of SCST commands that arrived before the RTU event. 
This
  *                 list contains struct srpt_ioctx elements and is protected
  *                 against concurrent modification by the cm_id spinlock.
- * @spinlock:      Protects free_list.
+ * @spinlock:      Protects free_list and state.
  * @free_list:     Head of list with free send I/O contexts.
  * @scst_sess:     SCST session information associated with this SRP channel.
  * @sess_name:     SCST session name.
@@ -280,7 +280,7 @@ struct srpt_rdma_ch {
        struct list_head        free_list;
        struct srpt_send_ioctx  **ioctx_ring;
        struct ib_wc            wc[16];
-       atomic_t                state;
+       enum rdma_ch_state      state;
        struct list_head        list;
        struct list_head        cmd_wait_list;
 
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to