aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/infiniband
diff options
context:
space:
mode:
authorBart Van Assche <bvanassche@acm.org>2012-08-14 09:18:53 -0400
committerRoland Dreier <roland@purestorage.com>2012-08-15 15:00:48 -0400
commit220329916c72ee3d54ae7262b215a050f04a18fc (patch)
tree0d89f6e3cc8f17b070c16665d3df4e883fda9ddc /drivers/infiniband
parent0d7614f09c1ebdbaa1599a5aba7593f147bf96ee (diff)
IB/srp: Fix a race condition
Avoid a crash caused by the scmnd->scsi_done(scmnd) call in srp_process_rsp() being invoked with scsi_done == NULL. This can happen if a reply is received during or after a command abort. Reported-by: Joseph Glanville <joseph.glanville@orionvm.com.au> Reference: http://marc.info/?l=linux-rdma&m=134314367801595 Cc: <stable@vger.kernel.org> Acked-by: David Dillow <dillowda@ornl.gov> Signed-off-by: Bart Van Assche <bvanassche@acm.org> Signed-off-by: Roland Dreier <roland@purestorage.com>
Diffstat (limited to 'drivers/infiniband')
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c87
1 files changed, 63 insertions, 24 deletions
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index bcbf22ee0aa7..1b5b0c730054 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -586,24 +586,62 @@ static void srp_unmap_data(struct scsi_cmnd *scmnd,
586 scmnd->sc_data_direction); 586 scmnd->sc_data_direction);
587} 587}
588 588
589static void srp_remove_req(struct srp_target_port *target, 589/**
590 struct srp_request *req, s32 req_lim_delta) 590 * srp_claim_req - Take ownership of the scmnd associated with a request.
591 * @target: SRP target port.
592 * @req: SRP request.
593 * @scmnd: If NULL, take ownership of @req->scmnd. If not NULL, only take
594 * ownership of @req->scmnd if it equals @scmnd.
595 *
596 * Return value:
597 * Either NULL or a pointer to the SCSI command the caller became owner of.
598 */
599static struct scsi_cmnd *srp_claim_req(struct srp_target_port *target,
600 struct srp_request *req,
601 struct scsi_cmnd *scmnd)
602{
603 unsigned long flags;
604
605 spin_lock_irqsave(&target->lock, flags);
606 if (!scmnd) {
607 scmnd = req->scmnd;
608 req->scmnd = NULL;
609 } else if (req->scmnd == scmnd) {
610 req->scmnd = NULL;
611 } else {
612 scmnd = NULL;
613 }
614 spin_unlock_irqrestore(&target->lock, flags);
615
616 return scmnd;
617}
618
619/**
620 * srp_free_req() - Unmap data and add request to the free request list.
621 */
622static void srp_free_req(struct srp_target_port *target,
623 struct srp_request *req, struct scsi_cmnd *scmnd,
624 s32 req_lim_delta)
591{ 625{
592 unsigned long flags; 626 unsigned long flags;
593 627
594 srp_unmap_data(req->scmnd, target, req); 628 srp_unmap_data(scmnd, target, req);
629
595 spin_lock_irqsave(&target->lock, flags); 630 spin_lock_irqsave(&target->lock, flags);
596 target->req_lim += req_lim_delta; 631 target->req_lim += req_lim_delta;
597 req->scmnd = NULL;
598 list_add_tail(&req->list, &target->free_reqs); 632 list_add_tail(&req->list, &target->free_reqs);
599 spin_unlock_irqrestore(&target->lock, flags); 633 spin_unlock_irqrestore(&target->lock, flags);
600} 634}
601 635
602static void srp_reset_req(struct srp_target_port *target, struct srp_request *req) 636static void srp_reset_req(struct srp_target_port *target, struct srp_request *req)
603{ 637{
604 req->scmnd->result = DID_RESET << 16; 638 struct scsi_cmnd *scmnd = srp_claim_req(target, req, NULL);
605 req->scmnd->scsi_done(req->scmnd); 639
606 srp_remove_req(target, req, 0); 640 if (scmnd) {
641 scmnd->result = DID_RESET << 16;
642 scmnd->scsi_done(scmnd);
643 srp_free_req(target, req, scmnd, 0);
644 }
607} 645}
608 646
609static int srp_reconnect_target(struct srp_target_port *target) 647static int srp_reconnect_target(struct srp_target_port *target)
@@ -1073,11 +1111,18 @@ static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp)
1073 complete(&target->tsk_mgmt_done); 1111 complete(&target->tsk_mgmt_done);
1074 } else { 1112 } else {
1075 req = &target->req_ring[rsp->tag]; 1113 req = &target->req_ring[rsp->tag];
1076 scmnd = req->scmnd; 1114 scmnd = srp_claim_req(target, req, NULL);
1077 if (!scmnd) 1115 if (!scmnd) {
1078 shost_printk(KERN_ERR, target->scsi_host, 1116 shost_printk(KERN_ERR, target->scsi_host,
1079 "Null scmnd for RSP w/tag %016llx\n", 1117 "Null scmnd for RSP w/tag %016llx\n",
1080 (unsigned long long) rsp->tag); 1118 (unsigned long long) rsp->tag);
1119
1120 spin_lock_irqsave(&target->lock, flags);
1121 target->req_lim += be32_to_cpu(rsp->req_lim_delta);
1122 spin_unlock_irqrestore(&target->lock, flags);
1123
1124 return;
1125 }
1081 scmnd->result = rsp->status; 1126 scmnd->result = rsp->status;
1082 1127
1083 if (rsp->flags & SRP_RSP_FLAG_SNSVALID) { 1128 if (rsp->flags & SRP_RSP_FLAG_SNSVALID) {
@@ -1092,7 +1137,9 @@ static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp)
1092 else if (rsp->flags & (SRP_RSP_FLAG_DIOVER | SRP_RSP_FLAG_DIUNDER)) 1137 else if (rsp->flags & (SRP_RSP_FLAG_DIOVER | SRP_RSP_FLAG_DIUNDER))
1093 scsi_set_resid(scmnd, be32_to_cpu(rsp->data_in_res_cnt)); 1138 scsi_set_resid(scmnd, be32_to_cpu(rsp->data_in_res_cnt));
1094 1139
1095 srp_remove_req(target, req, be32_to_cpu(rsp->req_lim_delta)); 1140 srp_free_req(target, req, scmnd,
1141 be32_to_cpu(rsp->req_lim_delta));
1142
1096 scmnd->host_scribble = NULL; 1143 scmnd->host_scribble = NULL;
1097 scmnd->scsi_done(scmnd); 1144 scmnd->scsi_done(scmnd);
1098 } 1145 }
@@ -1631,25 +1678,17 @@ static int srp_abort(struct scsi_cmnd *scmnd)
1631{ 1678{
1632 struct srp_target_port *target = host_to_target(scmnd->device->host); 1679 struct srp_target_port *target = host_to_target(scmnd->device->host);
1633 struct srp_request *req = (struct srp_request *) scmnd->host_scribble; 1680 struct srp_request *req = (struct srp_request *) scmnd->host_scribble;
1634 int ret = SUCCESS;
1635 1681
1636 shost_printk(KERN_ERR, target->scsi_host, "SRP abort called\n"); 1682 shost_printk(KERN_ERR, target->scsi_host, "SRP abort called\n");
1637 1683
1638 if (!req || target->qp_in_error) 1684 if (!req || target->qp_in_error || !srp_claim_req(target, req, scmnd))
1639 return FAILED; 1685 return FAILED;
1640 if (srp_send_tsk_mgmt(target, req->index, scmnd->device->lun, 1686 srp_send_tsk_mgmt(target, req->index, scmnd->device->lun,
1641 SRP_TSK_ABORT_TASK)) 1687 SRP_TSK_ABORT_TASK);
1642 return FAILED; 1688 srp_free_req(target, req, scmnd, 0);
1643 1689 scmnd->result = DID_ABORT << 16;
1644 if (req->scmnd) {
1645 if (!target->tsk_mgmt_status) {
1646 srp_remove_req(target, req, 0);
1647 scmnd->result = DID_ABORT << 16;
1648 } else
1649 ret = FAILED;
1650 }
1651 1690
1652 return ret; 1691 return SUCCESS;
1653} 1692}
1654 1693
1655static int srp_reset_device(struct scsi_cmnd *scmnd) 1694static int srp_reset_device(struct scsi_cmnd *scmnd)