aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/infiniband
diff options
context:
space:
mode:
authorBart Van Assche <bart.vanassche@sandisk.com>2017-02-14 13:56:31 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-03-14 22:02:44 -0400
commit48e2181b0b8d1a1e226b2932a11d6f94aef28fb8 (patch)
tree385c3afebb8539db45577c369fffb2ebf77e76ac /drivers/infiniband
parentd5d1d2cc4be76abdabb58e1bdad84fc06090042c (diff)
IB/srp: Fix race conditions related to task management
commit 0a6fdbdeb1c25e31763c1fb333fa2723a7d2aba6 upstream. Avoid that srp_process_rsp() overwrites the status information in ch if the SRP target response timed out and processing of another task management function has already started. Avoid that issuing multiple task management functions concurrently triggers list corruption. This patch prevents that the following stack trace appears in the system log: WARNING: CPU: 8 PID: 9269 at lib/list_debug.c:52 __list_del_entry_valid+0xbc/0xc0 list_del corruption. prev->next should be ffffc90004bb7b00, but was ffff8804052ecc68 CPU: 8 PID: 9269 Comm: sg_reset Tainted: G W 4.10.0-rc7-dbg+ #3 Call Trace: dump_stack+0x68/0x93 __warn+0xc6/0xe0 warn_slowpath_fmt+0x4a/0x50 __list_del_entry_valid+0xbc/0xc0 wait_for_completion_timeout+0x12e/0x170 srp_send_tsk_mgmt+0x1ef/0x2d0 [ib_srp] srp_reset_device+0x5b/0x110 [ib_srp] scsi_ioctl_reset+0x1c7/0x290 scsi_ioctl+0x12a/0x420 sd_ioctl+0x9d/0x100 blkdev_ioctl+0x51e/0x9f0 block_ioctl+0x38/0x40 do_vfs_ioctl+0x8f/0x700 SyS_ioctl+0x3c/0x70 entry_SYSCALL_64_fastpath+0x18/0xad Signed-off-by: Bart Van Assche <bart.vanassche@sandisk.com> Cc: Israel Rukshin <israelr@mellanox.com> Cc: Max Gurtovoy <maxg@mellanox.com> Cc: Laurence Oberman <loberman@redhat.com> Cc: Steve Feeley <Steve.Feeley@sandisk.com> Signed-off-by: Doug Ledford <dledford@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/infiniband')
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c45
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.h1
2 files changed, 30 insertions, 16 deletions
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index a8239656e571..1eee8f7e75ca 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -1872,12 +1872,17 @@ static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp)
1872 if (unlikely(rsp->tag & SRP_TAG_TSK_MGMT)) { 1872 if (unlikely(rsp->tag & SRP_TAG_TSK_MGMT)) {
1873 spin_lock_irqsave(&ch->lock, flags); 1873 spin_lock_irqsave(&ch->lock, flags);
1874 ch->req_lim += be32_to_cpu(rsp->req_lim_delta); 1874 ch->req_lim += be32_to_cpu(rsp->req_lim_delta);
1875 if (rsp->tag == ch->tsk_mgmt_tag) {
1876 ch->tsk_mgmt_status = -1;
1877 if (be32_to_cpu(rsp->resp_data_len) >= 4)
1878 ch->tsk_mgmt_status = rsp->data[3];
1879 complete(&ch->tsk_mgmt_done);
1880 } else {
1881 shost_printk(KERN_ERR, target->scsi_host,
1882 "Received tsk mgmt response too late for tag %#llx\n",
1883 rsp->tag);
1884 }
1875 spin_unlock_irqrestore(&ch->lock, flags); 1885 spin_unlock_irqrestore(&ch->lock, flags);
1876
1877 ch->tsk_mgmt_status = -1;
1878 if (be32_to_cpu(rsp->resp_data_len) >= 4)
1879 ch->tsk_mgmt_status = rsp->data[3];
1880 complete(&ch->tsk_mgmt_done);
1881 } else { 1886 } else {
1882 scmnd = scsi_host_find_tag(target->scsi_host, rsp->tag); 1887 scmnd = scsi_host_find_tag(target->scsi_host, rsp->tag);
1883 if (scmnd && scmnd->host_scribble) { 1888 if (scmnd && scmnd->host_scribble) {
@@ -2516,19 +2521,18 @@ srp_change_queue_depth(struct scsi_device *sdev, int qdepth)
2516} 2521}
2517 2522
2518static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun, 2523static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun,
2519 u8 func) 2524 u8 func, u8 *status)
2520{ 2525{
2521 struct srp_target_port *target = ch->target; 2526 struct srp_target_port *target = ch->target;
2522 struct srp_rport *rport = target->rport; 2527 struct srp_rport *rport = target->rport;
2523 struct ib_device *dev = target->srp_host->srp_dev->dev; 2528 struct ib_device *dev = target->srp_host->srp_dev->dev;
2524 struct srp_iu *iu; 2529 struct srp_iu *iu;
2525 struct srp_tsk_mgmt *tsk_mgmt; 2530 struct srp_tsk_mgmt *tsk_mgmt;
2531 int res;
2526 2532
2527 if (!ch->connected || target->qp_in_error) 2533 if (!ch->connected || target->qp_in_error)
2528 return -1; 2534 return -1;
2529 2535
2530 init_completion(&ch->tsk_mgmt_done);
2531
2532 /* 2536 /*
2533 * Lock the rport mutex to avoid that srp_create_ch_ib() is 2537 * Lock the rport mutex to avoid that srp_create_ch_ib() is
2534 * invoked while a task management function is being sent. 2538 * invoked while a task management function is being sent.
@@ -2551,10 +2555,16 @@ static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun,
2551 2555
2552 tsk_mgmt->opcode = SRP_TSK_MGMT; 2556 tsk_mgmt->opcode = SRP_TSK_MGMT;
2553 int_to_scsilun(lun, &tsk_mgmt->lun); 2557 int_to_scsilun(lun, &tsk_mgmt->lun);
2554 tsk_mgmt->tag = req_tag | SRP_TAG_TSK_MGMT;
2555 tsk_mgmt->tsk_mgmt_func = func; 2558 tsk_mgmt->tsk_mgmt_func = func;
2556 tsk_mgmt->task_tag = req_tag; 2559 tsk_mgmt->task_tag = req_tag;
2557 2560
2561 spin_lock_irq(&ch->lock);
2562 ch->tsk_mgmt_tag = (ch->tsk_mgmt_tag + 1) | SRP_TAG_TSK_MGMT;
2563 tsk_mgmt->tag = ch->tsk_mgmt_tag;
2564 spin_unlock_irq(&ch->lock);
2565
2566 init_completion(&ch->tsk_mgmt_done);
2567
2558 ib_dma_sync_single_for_device(dev, iu->dma, sizeof *tsk_mgmt, 2568 ib_dma_sync_single_for_device(dev, iu->dma, sizeof *tsk_mgmt,
2559 DMA_TO_DEVICE); 2569 DMA_TO_DEVICE);
2560 if (srp_post_send(ch, iu, sizeof(*tsk_mgmt))) { 2570 if (srp_post_send(ch, iu, sizeof(*tsk_mgmt))) {
@@ -2563,13 +2573,15 @@ static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun,
2563 2573
2564 return -1; 2574 return -1;
2565 } 2575 }
2576 res = wait_for_completion_timeout(&ch->tsk_mgmt_done,
2577 msecs_to_jiffies(SRP_ABORT_TIMEOUT_MS));
2578 if (res > 0 && status)
2579 *status = ch->tsk_mgmt_status;
2566 mutex_unlock(&rport->mutex); 2580 mutex_unlock(&rport->mutex);
2567 2581
2568 if (!wait_for_completion_timeout(&ch->tsk_mgmt_done, 2582 WARN_ON_ONCE(res < 0);
2569 msecs_to_jiffies(SRP_ABORT_TIMEOUT_MS)))
2570 return -1;
2571 2583
2572 return 0; 2584 return res > 0 ? 0 : -1;
2573} 2585}
2574 2586
2575static int srp_abort(struct scsi_cmnd *scmnd) 2587static int srp_abort(struct scsi_cmnd *scmnd)
@@ -2595,7 +2607,7 @@ static int srp_abort(struct scsi_cmnd *scmnd)
2595 shost_printk(KERN_ERR, target->scsi_host, 2607 shost_printk(KERN_ERR, target->scsi_host,
2596 "Sending SRP abort for tag %#x\n", tag); 2608 "Sending SRP abort for tag %#x\n", tag);
2597 if (srp_send_tsk_mgmt(ch, tag, scmnd->device->lun, 2609 if (srp_send_tsk_mgmt(ch, tag, scmnd->device->lun,
2598 SRP_TSK_ABORT_TASK) == 0) 2610 SRP_TSK_ABORT_TASK, NULL) == 0)
2599 ret = SUCCESS; 2611 ret = SUCCESS;
2600 else if (target->rport->state == SRP_RPORT_LOST) 2612 else if (target->rport->state == SRP_RPORT_LOST)
2601 ret = FAST_IO_FAIL; 2613 ret = FAST_IO_FAIL;
@@ -2613,14 +2625,15 @@ static int srp_reset_device(struct scsi_cmnd *scmnd)
2613 struct srp_target_port *target = host_to_target(scmnd->device->host); 2625 struct srp_target_port *target = host_to_target(scmnd->device->host);
2614 struct srp_rdma_ch *ch; 2626 struct srp_rdma_ch *ch;
2615 int i; 2627 int i;
2628 u8 status;
2616 2629
2617 shost_printk(KERN_ERR, target->scsi_host, "SRP reset_device called\n"); 2630 shost_printk(KERN_ERR, target->scsi_host, "SRP reset_device called\n");
2618 2631
2619 ch = &target->ch[0]; 2632 ch = &target->ch[0];
2620 if (srp_send_tsk_mgmt(ch, SRP_TAG_NO_REQ, scmnd->device->lun, 2633 if (srp_send_tsk_mgmt(ch, SRP_TAG_NO_REQ, scmnd->device->lun,
2621 SRP_TSK_LUN_RESET)) 2634 SRP_TSK_LUN_RESET, &status))
2622 return FAILED; 2635 return FAILED;
2623 if (ch->tsk_mgmt_status) 2636 if (status)
2624 return FAILED; 2637 return FAILED;
2625 2638
2626 for (i = 0; i < target->ch_count; i++) { 2639 for (i = 0; i < target->ch_count; i++) {
diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h
index 21c69695f9d4..32ed40db3ca2 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.h
+++ b/drivers/infiniband/ulp/srp/ib_srp.h
@@ -163,6 +163,7 @@ struct srp_rdma_ch {
163 int max_ti_iu_len; 163 int max_ti_iu_len;
164 int comp_vector; 164 int comp_vector;
165 165
166 u64 tsk_mgmt_tag;
166 struct completion tsk_mgmt_done; 167 struct completion tsk_mgmt_done;
167 u8 tsk_mgmt_status; 168 u8 tsk_mgmt_status;
168 bool connected; 169 bool connected;