In the current implementation of ib_srp the req_lim field of
struct srp_target_port can be manipulated in a non-atomic way by
more than one CPU at a time: one CPU can be modifying req_lim in
function srp_process_rsp() while another CPU can concurrently be
decrementing req_lim in function __srp_get_tx_iu(). This is a
race condition which can result in incorrect manipulation of the
req_lim field. The patch below fixes this race condition by
converting all manipulations of req_lim into atomic operations.

Signed-off-by: Bart Van Assche <[email protected]>
Cc: Roland Dreier <[email protected]>

diff --git a/drivers/infiniband/ulp/srp/ib_srp.c 
b/drivers/infiniband/ulp/srp/ib_srp.c
index ed3f9eb..3d334b5 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -822,7 +822,7 @@ static void srp_process_rsp(struct srp_target_port *target, 
struct srp_rsp *rsp)
 
        spin_lock_irqsave(target->scsi_host->host_lock, flags);
 
-       target->req_lim += delta;
+       atomic_add(delta, &target->req_lim);
 
        req = &target->req_ring[rsp->tag & ~SRP_TAG_TSK_MGMT];
 
@@ -1008,7 +1008,7 @@ static struct srp_iu *__srp_get_tx_iu(struct 
srp_target_port *target,
        if (target->tx_head - target->tx_tail >= SRP_SQ_SIZE)
                return NULL;
 
-       if (target->req_lim < min) {
+       if (atomic_read(&target->req_lim) < min) {
                ++target->zero_req_lim;
                return NULL;
        }
@@ -1042,7 +1042,7 @@ static int __srp_post_send(struct srp_target_port *target,
 
        if (!ret) {
                ++target->tx_head;
-               --target->req_lim;
+               atomic_dec(&target->req_lim);
        }
 
        return ret;
@@ -1266,10 +1266,12 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, 
struct ib_cm_event *event)
                        struct srp_login_rsp *rsp = event->private_data;
 
                        target->max_ti_iu_len = be32_to_cpu(rsp->max_ti_iu_len);
-                       target->req_lim       = be32_to_cpu(rsp->req_lim_delta);
+                       atomic_set(&target->req_lim,
+                                  be32_to_cpu(rsp->req_lim_delta));
 
-                       target->scsi_host->can_queue = min(target->req_lim,
-                                                          
target->scsi_host->can_queue);
+                       target->scsi_host->can_queue
+                               = min(atomic_read(&target->req_lim),
+                                     target->scsi_host->can_queue);
                } else {
                        shost_printk(KERN_WARNING, target->scsi_host,
                                    PFX "Unhandled RSP opcode %#x\n", opcode);
diff --git a/drivers/infiniband/ulp/srp/ib_srp.h 
b/drivers/infiniband/ulp/srp/ib_srp.h
index 5a80eac..048f213 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.h
+++ b/drivers/infiniband/ulp/srp/ib_srp.h
@@ -135,7 +135,7 @@ struct srp_target_port {
        struct ib_qp           *qp;
 
        int                     max_ti_iu_len;
-       s32                     req_lim;
+       atomic_t                req_lim;
 
        int                     zero_req_lim;
 
--
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