Avoid that the scmnd->scsi_done(scmnd) call in srp_process_rsp() can trigger a crash by being invoked with scsi_done == NULL. That could happen if a reply is received during or after a command abort.
BUG: unable to handle kernel NULL pointer dereference at (null) Call Trace: <IRQ> [<ffffffffa04ab636>] ? srp_handle_recv+0x216/0x480 [ib_srp] [<ffffffffa04ab8ea>] srp_recv_completion+0x4a/0xb0 [ib_srp] [<ffffffffa032b497>] mlx4_ib_cq_comp+0x17/0x20 [mlx4_ib] [<ffffffffa02519f0>] mlx4_cq_completion+0x40/0x80 [mlx4_core] [<ffffffffa02529d3>] mlx4_eq_int+0x543/0x920 [mlx4_core] [<ffffffff81071acf>] ? local_clock+0x4f/0x60 [<ffffffffa0252dc4>] mlx4_msi_x_interrupt+0x14/0x20 [mlx4_core] [<ffffffff810b82b5>] handle_irq_event_percpu+0x75/0x240 [<ffffffff810b84ce>] handle_irq_event+0x4e/0x80 [<ffffffff810badf5>] handle_edge_irq+0x85/0x130 [<ffffffff81004355>] handle_irq+0x25/0x40 [<ffffffff814153cd>] do_IRQ+0x5d/0xe0 [<ffffffff8140b96c>] common_interrupt+0x6c/0x6c <EOI> Kernel panic - not syncing: Fatal exception in interrupt Reported-by: Joseph Glanville <[email protected]> Reference: http://marc.info/?l=linux-rdma&m=134314367801595 Signed-off-by: Bart Van Assche <[email protected]> Cc: David Dillow <[email protected]> Cc: Roland Dreier <[email protected]> Cc: <[email protected]> --- drivers/infiniband/ulp/srp/ib_srp.c | 82 ++++++++++++++++++++++++---------- 1 files changed, 58 insertions(+), 24 deletions(-) diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index bcbf22e..9a61be2 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -586,24 +586,61 @@ static void srp_unmap_data(struct scsi_cmnd *scmnd, scmnd->sc_data_direction); } -static void srp_remove_req(struct srp_target_port *target, - struct srp_request *req, s32 req_lim_delta) +/** + * srp_claim_req - Take ownership of the scmnd associated with a request. + * @target: SRP target port. + * @req: SRP request. + * @scmnd: If NULL, take ownership of @req->scmnd. If not NULL, only take + * ownership of @req->scmnd if it equals @scmnd. + * @req_lim_delta: target->req_lim_delta increment. + * + * Return value: + * Either NULL or a pointer to the SCSI command the caller became owner of. + */ +static struct scsi_cmnd *srp_claim_req(struct srp_target_port *target, + struct srp_request *req, + struct scsi_cmnd *scmnd, + s32 req_lim_delta) { unsigned long flags; - srp_unmap_data(req->scmnd, target, req); spin_lock_irqsave(&target->lock, flags); target->req_lim += req_lim_delta; - req->scmnd = NULL; + if (!scmnd) + swap(scmnd, req->scmnd); + else if (req->scmnd == scmnd) + req->scmnd = NULL; + else + scmnd = NULL; + spin_unlock_irqrestore(&target->lock, flags); + + return scmnd; +} + +/** + * srp_free_req() - Unmap data and add request to the free request list. + */ +static void srp_free_req(struct srp_target_port *target, + struct srp_request *req, struct scsi_cmnd *scmnd) +{ + unsigned long flags; + + srp_unmap_data(scmnd, target, req); + + spin_lock_irqsave(&target->lock, flags); list_add_tail(&req->list, &target->free_reqs); spin_unlock_irqrestore(&target->lock, flags); } static void srp_reset_req(struct srp_target_port *target, struct srp_request *req) { - req->scmnd->result = DID_RESET << 16; - req->scmnd->scsi_done(req->scmnd); - srp_remove_req(target, req, 0); + struct scsi_cmnd *scmnd = req->scmnd; + + if (srp_claim_req(target, req, scmnd, 0)) { + scmnd->result = DID_RESET << 16; + scmnd->scsi_done(scmnd); + srp_free_req(target, req, scmnd); + } } static int srp_reconnect_target(struct srp_target_port *target) @@ -1073,11 +1110,14 @@ static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp) complete(&target->tsk_mgmt_done); } else { req = &target->req_ring[rsp->tag]; - scmnd = req->scmnd; - if (!scmnd) + scmnd = srp_claim_req(target, req, NULL, + be32_to_cpu(rsp->req_lim_delta)); + if (!scmnd) { shost_printk(KERN_ERR, target->scsi_host, "Null scmnd for RSP w/tag %016llx\n", (unsigned long long) rsp->tag); + return; + } scmnd->result = rsp->status; if (rsp->flags & SRP_RSP_FLAG_SNSVALID) { @@ -1092,7 +1132,8 @@ static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp) else if (rsp->flags & (SRP_RSP_FLAG_DIOVER | SRP_RSP_FLAG_DIUNDER)) scsi_set_resid(scmnd, be32_to_cpu(rsp->data_in_res_cnt)); - srp_remove_req(target, req, be32_to_cpu(rsp->req_lim_delta)); + srp_free_req(target, req, scmnd); + scmnd->host_scribble = NULL; scmnd->scsi_done(scmnd); } @@ -1631,25 +1672,18 @@ static int srp_abort(struct scsi_cmnd *scmnd) { struct srp_target_port *target = host_to_target(scmnd->device->host); struct srp_request *req = (struct srp_request *) scmnd->host_scribble; - int ret = SUCCESS; shost_printk(KERN_ERR, target->scsi_host, "SRP abort called\n"); - if (!req || target->qp_in_error) + if (!req || target->qp_in_error || + !srp_claim_req(target, req, scmnd, 0)) return FAILED; - if (srp_send_tsk_mgmt(target, req->index, scmnd->device->lun, - SRP_TSK_ABORT_TASK)) - return FAILED; - - if (req->scmnd) { - if (!target->tsk_mgmt_status) { - srp_remove_req(target, req, 0); - scmnd->result = DID_ABORT << 16; - } else - ret = FAILED; - } + srp_send_tsk_mgmt(target, req->index, scmnd->device->lun, + SRP_TSK_ABORT_TASK); + srp_free_req(target, req, scmnd); + scmnd->result = DID_ABORT << 16; - return ret; + return SUCCESS; } static int srp_reset_device(struct scsi_cmnd *scmnd) -- 1.7.7 -- 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
