mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-23 05:31:28 +00:00
git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@2275 d57e44dd-8a1f-0410-8b47-8ef2f437770f
913 lines
27 KiB
Bash
Executable File
913 lines
27 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Generates a 2.6.34.*, 2.6.35.* or 2.6.36* kernel tree with the SRP_CRED_REQ
|
|
# and several other patches applied to the ib_srp initiator.
|
|
#
|
|
# NOTE: the kernel source tree generated by this patch is NOT suited for
|
|
# general use. All SCSI commands are assigned serial number 1, which will
|
|
# break any SCSI LLD that expects that monotonically increasing serial numbers
|
|
# are assigned to SCSI commands.
|
|
|
|
# Adjust this variable such that it points to the directory with
|
|
# linux-x.y.z.tar.bz2 and patch-x.y.z.p.bz2 files on your system
|
|
TARBALLDIR=/home/bart/software/downloads
|
|
|
|
# Local functions
|
|
|
|
usage() {
|
|
echo "$0 <kernel-version>"
|
|
}
|
|
|
|
apply_locking_per_lun_patch() {
|
|
patch -p1 <<EOF
|
|
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
|
|
index a2935e3..b8815e7 100644
|
|
--- a/drivers/infiniband/ulp/srp/ib_srp.c
|
|
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
|
|
@@ -441,18 +441,30 @@ static void srp_disconnect_target(struct srp_target_port *target)
|
|
wait_for_completion(&target->done);
|
|
}
|
|
|
|
+static bool srp_port_change_state(struct srp_target_port *target,
|
|
+ enum srp_target_state old,
|
|
+ enum srp_target_state new)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ bool ret;
|
|
+
|
|
+ spin_lock_irqsave(&target->lock, flags);
|
|
+ if (target->state == old) {
|
|
+ target->state = new;
|
|
+ ret = true;
|
|
+ } else
|
|
+ ret = false;
|
|
+ spin_unlock_irqrestore(&target->lock, flags);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
static void srp_remove_work(struct work_struct *work)
|
|
{
|
|
struct srp_target_port *target =
|
|
container_of(work, struct srp_target_port, work);
|
|
|
|
- spin_lock_irq(target->scsi_host->host_lock);
|
|
- if (target->state != SRP_TARGET_DEAD) {
|
|
- spin_unlock_irq(target->scsi_host->host_lock);
|
|
+ if (!srp_port_change_state(target, SRP_TARGET_DEAD, SRP_TARGET_REMOVED))
|
|
return;
|
|
- }
|
|
- target->state = SRP_TARGET_REMOVED;
|
|
- spin_unlock_irq(target->scsi_host->host_lock);
|
|
|
|
spin_lock(&target->srp_host->target_lock);
|
|
list_del(&target->list);
|
|
@@ -541,8 +553,13 @@ static void srp_unmap_data(struct scsi_cmnd *scmnd,
|
|
|
|
static void srp_remove_req(struct srp_target_port *target, struct srp_request *req)
|
|
{
|
|
+ unsigned long flags;
|
|
+
|
|
srp_unmap_data(req->scmnd, target, req);
|
|
- list_move_tail(&req->list, &target->free_reqs);
|
|
+ req->scmnd = NULL;
|
|
+ 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)
|
|
@@ -555,17 +572,14 @@ static void srp_reset_req(struct srp_target_port *target, struct srp_request *re
|
|
static int srp_reconnect_target(struct srp_target_port *target)
|
|
{
|
|
struct ib_qp_attr qp_attr;
|
|
- struct srp_request *req, *tmp;
|
|
struct ib_wc wc;
|
|
int ret;
|
|
+ int i;
|
|
|
|
- spin_lock_irq(target->scsi_host->host_lock);
|
|
- if (target->state != SRP_TARGET_LIVE) {
|
|
- spin_unlock_irq(target->scsi_host->host_lock);
|
|
+ if (!srp_port_change_state(target, SRP_TARGET_LIVE,
|
|
+ SRP_TARGET_CONNECTING)) {
|
|
return -EAGAIN;
|
|
}
|
|
- target->state = SRP_TARGET_CONNECTING;
|
|
- spin_unlock_irq(target->scsi_host->host_lock);
|
|
|
|
srp_disconnect_target(target);
|
|
/*
|
|
@@ -590,27 +604,20 @@ static int srp_reconnect_target(struct srp_target_port *target)
|
|
while (ib_poll_cq(target->send_cq, 1, &wc) > 0)
|
|
; /* nothing */
|
|
|
|
- spin_lock_irq(target->scsi_host->host_lock);
|
|
- list_for_each_entry_safe(req, tmp, &target->req_queue, list)
|
|
- srp_reset_req(target, req);
|
|
- spin_unlock_irq(target->scsi_host->host_lock);
|
|
-
|
|
- target->rx_head = 0;
|
|
- target->tx_head = 0;
|
|
- target->tx_tail = 0;
|
|
+ for (i = 0; i < ARRAY_SIZE(target->req_ring); ++i)
|
|
+ if (target->req_ring[i].scmnd)
|
|
+ srp_reset_req(target, &target->req_ring[i]);
|
|
|
|
target->qp_in_error = 0;
|
|
ret = srp_connect_target(target);
|
|
if (ret)
|
|
goto err;
|
|
|
|
- spin_lock_irq(target->scsi_host->host_lock);
|
|
- if (target->state == SRP_TARGET_CONNECTING) {
|
|
+ if (srp_port_change_state(target, SRP_TARGET_CONNECTING,
|
|
+ SRP_TARGET_LIVE))
|
|
ret = 0;
|
|
- target->state = SRP_TARGET_LIVE;
|
|
- } else
|
|
+ else
|
|
ret = -EAGAIN;
|
|
- spin_unlock_irq(target->scsi_host->host_lock);
|
|
|
|
return ret;
|
|
|
|
@@ -624,13 +631,11 @@ err:
|
|
* be in the context of the SCSI error handler now, which
|
|
* would deadlock if we call scsi_remove_host().
|
|
*/
|
|
- spin_lock_irq(target->scsi_host->host_lock);
|
|
- if (target->state == SRP_TARGET_CONNECTING) {
|
|
- target->state = SRP_TARGET_DEAD;
|
|
+ if (srp_port_change_state(target, SRP_TARGET_CONNECTING,
|
|
+ SRP_TARGET_DEAD)) {
|
|
INIT_WORK(&target->work, srp_remove_work);
|
|
schedule_work(&target->work);
|
|
}
|
|
- spin_unlock_irq(target->scsi_host->host_lock);
|
|
|
|
return ret;
|
|
}
|
|
@@ -811,20 +816,12 @@ static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_target_port *target,
|
|
return len;
|
|
}
|
|
|
|
-static int srp_post_recv(struct srp_target_port *target)
|
|
+static int srp_post_recv(struct srp_target_port *target, struct srp_iu *iu)
|
|
{
|
|
- unsigned long flags;
|
|
- struct srp_iu *iu;
|
|
struct ib_sge list;
|
|
struct ib_recv_wr wr, *bad_wr;
|
|
- unsigned int next;
|
|
- int ret;
|
|
|
|
- spin_lock_irqsave(target->scsi_host->host_lock, flags);
|
|
-
|
|
- next = target->rx_head & SRP_RQ_MASK;
|
|
- wr.wr_id = next;
|
|
- iu = target->rx_ring[next];
|
|
+ wr.wr_id = iu->index;
|
|
|
|
list.addr = iu->dma;
|
|
list.length = iu->size;
|
|
@@ -834,13 +831,7 @@ static int srp_post_recv(struct srp_target_port *target)
|
|
wr.sg_list = &list;
|
|
wr.num_sge = 1;
|
|
|
|
- ret = ib_post_recv(target->qp, &wr, &bad_wr);
|
|
- if (!ret)
|
|
- ++target->rx_head;
|
|
-
|
|
- spin_unlock_irqrestore(target->scsi_host->host_lock, flags);
|
|
-
|
|
- return ret;
|
|
+ return ib_post_recv(target->qp, &wr, &bad_wr);
|
|
}
|
|
|
|
static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp)
|
|
@@ -852,9 +843,9 @@ static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp)
|
|
|
|
delta = (s32) be32_to_cpu(rsp->req_lim_delta);
|
|
|
|
- spin_lock_irqsave(target->scsi_host->host_lock, flags);
|
|
-
|
|
+ spin_lock_irqsave(&target->lock, flags);
|
|
target->req_lim += delta;
|
|
+ spin_unlock_irqrestore(&target->lock, flags);
|
|
|
|
req = &target->req_ring[rsp->tag & ~SRP_TAG_TSK_MGMT];
|
|
|
|
@@ -892,14 +883,12 @@ static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp)
|
|
} else
|
|
req->cmd_done = 1;
|
|
}
|
|
-
|
|
- spin_unlock_irqrestore(target->scsi_host->host_lock, flags);
|
|
}
|
|
|
|
/*
|
|
- * Must be called with target->scsi_host->host_lock held to protect
|
|
- * req_lim and tx_head. Lock cannot be dropped between call here and
|
|
- * call to __srp_post_send().
|
|
+ * It is the responsability of the caller to put back the returned information
|
|
+ * unit in the target->tx_free list and to increment target->req_lim if an
|
|
+ * error occurs before __srp_post_send() is called.
|
|
*
|
|
* Note:
|
|
* An upper limit for the number of allocated information units for each
|
|
@@ -914,24 +903,28 @@ static struct srp_iu *__srp_get_tx_iu(struct srp_target_port *target,
|
|
enum srp_tx_iu_type iu_type)
|
|
{
|
|
s32 rsv = (iu_type == SRP_IU_TSK_MGMT) ? 0 : SRP_TSK_MGMT_SQ_SIZE;
|
|
+ struct srp_iu *res = NULL;
|
|
+ unsigned long flags;
|
|
|
|
srp_send_completion(target->send_cq, target);
|
|
|
|
- if (target->tx_head - target->tx_tail >= SRP_SQ_SIZE)
|
|
- return NULL;
|
|
+ spin_lock_irqsave(&target->lock, flags);
|
|
+
|
|
+ if (list_empty(&target->tx_free))
|
|
+ goto out;
|
|
|
|
if (iu_type != SRP_IU_RSP && target->req_lim <= rsv) {
|
|
++target->zero_req_lim;
|
|
- return NULL;
|
|
+ goto out;
|
|
}
|
|
|
|
- return target->tx_ring[target->tx_head & SRP_SQ_MASK];
|
|
+ res = list_first_entry(&target->tx_free, struct srp_iu, list);
|
|
+ list_del(&res->list);
|
|
+out:
|
|
+ spin_unlock_irqrestore(&target->lock, flags);
|
|
+ return res;
|
|
}
|
|
|
|
-/*
|
|
- * Must be called with target->scsi_host->host_lock held to protect
|
|
- * req_lim and tx_head.
|
|
- */
|
|
static int __srp_post_send(struct srp_target_port *target,
|
|
struct srp_iu *iu, int len,
|
|
enum srp_send_iu_type iu_type)
|
|
@@ -945,7 +938,7 @@ static int __srp_post_send(struct srp_target_port *target,
|
|
list.lkey = target->srp_host->srp_dev->mr->lkey;
|
|
|
|
wr.next = NULL;
|
|
- wr.wr_id = target->tx_head & SRP_SQ_MASK;
|
|
+ wr.wr_id = iu->index;
|
|
wr.sg_list = &list;
|
|
wr.num_sge = 1;
|
|
wr.opcode = IB_WR_SEND;
|
|
@@ -953,43 +946,45 @@ static int __srp_post_send(struct srp_target_port *target,
|
|
|
|
ret = ib_post_send(target->qp, &wr, &bad_wr);
|
|
|
|
- if (!ret) {
|
|
- ++target->tx_head;
|
|
+ if (ret) {
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&target->lock, flags);
|
|
if (iu_type == SRP_SEND_REQ)
|
|
- --target->req_lim;
|
|
+ target->req_lim++;
|
|
+ list_add(&iu->list, &target->tx_free);
|
|
+ spin_unlock_irqrestore(&target->lock, flags);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
-/*
|
|
- * Must be called with target->scsi_host->host_lock locked to protect
|
|
- * target->req_lim.
|
|
- */
|
|
-static void __srp_handle_cred_req(struct srp_target_port *target,
|
|
- void *req_ptr, void *rsp_ptr)
|
|
+static void srp_handle_cred_req(struct srp_target_port *target,
|
|
+ void *req_ptr, void *rsp_ptr)
|
|
{
|
|
+ unsigned long flags;
|
|
struct srp_cred_req *req = req_ptr;
|
|
struct srp_cred_rsp *rsp = rsp_ptr;
|
|
|
|
+ spin_lock_irqsave(&target->lock, flags);
|
|
target->req_lim += be32_to_cpu(req->req_lim_delta);
|
|
+ spin_unlock_irqrestore(&target->lock, flags);
|
|
|
|
memset(rsp, 0, sizeof *rsp);
|
|
rsp->opcode = SRP_CRED_RSP;
|
|
rsp->tag = req->tag;
|
|
}
|
|
|
|
-/*
|
|
- * Must be called with target->scsi_host->host_lock locked to protect
|
|
- * target->req_lim.
|
|
- */
|
|
-static void __srp_handle_aer_req(struct srp_target_port *target,
|
|
- void *req_ptr, void *rsp_ptr)
|
|
+static void srp_handle_aer_req(struct srp_target_port *target,
|
|
+ void *req_ptr, void *rsp_ptr)
|
|
{
|
|
+ unsigned long flags;
|
|
struct srp_aer_req *req = req_ptr;
|
|
struct srp_aer_rsp *rsp = rsp_ptr;
|
|
|
|
+ spin_lock_irqsave(&target->lock, flags);
|
|
target->req_lim += be32_to_cpu(req->req_lim_delta);
|
|
+ spin_unlock_irqrestore(&target->lock, flags);
|
|
|
|
shost_printk(KERN_ERR, target->scsi_host,
|
|
PFX "ignoring AER for LUN %llu\n", be64_to_cpu(req->lun));
|
|
@@ -1006,7 +1001,6 @@ static void srp_handle_req(struct srp_target_port *target,
|
|
{
|
|
struct ib_device *dev;
|
|
u8 *req_buf;
|
|
- unsigned long flags;
|
|
struct srp_iu *rsp_iu;
|
|
u8 *rsp_buf;
|
|
int res;
|
|
@@ -1014,11 +1008,9 @@ static void srp_handle_req(struct srp_target_port *target,
|
|
dev = target->srp_host->srp_dev->dev;
|
|
req_buf = req_iu->buf;
|
|
|
|
- spin_lock_irqsave(target->scsi_host->host_lock, flags);
|
|
-
|
|
rsp_iu = __srp_get_tx_iu(target, SRP_IU_RSP);
|
|
if (!rsp_iu)
|
|
- goto out_unlock;
|
|
+ return;
|
|
|
|
rsp_buf = rsp_iu->buf;
|
|
|
|
@@ -1031,9 +1023,6 @@ static void srp_handle_req(struct srp_target_port *target,
|
|
if (res)
|
|
shost_printk(KERN_ERR, target->scsi_host,
|
|
PFX "Sending response failed -- res = %d\n", res);
|
|
-
|
|
-out_unlock:
|
|
- spin_unlock_irqrestore(target->scsi_host->host_lock, flags);
|
|
}
|
|
|
|
static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc)
|
|
@@ -1070,11 +1059,11 @@ static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc)
|
|
break;
|
|
|
|
case SRP_CRED_REQ:
|
|
- srp_handle_req(target, iu, __srp_handle_cred_req);
|
|
+ srp_handle_req(target, iu, srp_handle_cred_req);
|
|
break;
|
|
|
|
case SRP_AER_REQ:
|
|
- srp_handle_req(target, iu, __srp_handle_aer_req);
|
|
+ srp_handle_req(target, iu, srp_handle_aer_req);
|
|
break;
|
|
|
|
default:
|
|
@@ -1086,7 +1075,7 @@ static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc)
|
|
ib_dma_sync_single_for_device(dev, iu->dma, target->max_ti_iu_len,
|
|
DMA_FROM_DEVICE);
|
|
|
|
- res = srp_post_recv(target);
|
|
+ res = srp_post_recv(target, iu);
|
|
if (res != 0)
|
|
shost_printk(KERN_ERR, target->scsi_host,
|
|
PFX "Recv failed with error code %d\n", res);
|
|
@@ -1095,38 +1084,53 @@ static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc)
|
|
static void srp_recv_completion(struct ib_cq *cq, void *target_ptr)
|
|
{
|
|
struct srp_target_port *target = target_ptr;
|
|
- struct ib_wc wc;
|
|
+ struct ib_wc wc[16];
|
|
+ int i, n;
|
|
|
|
ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
|
|
- while (ib_poll_cq(cq, 1, &wc) > 0) {
|
|
- if (wc.status) {
|
|
- shost_printk(KERN_ERR, target->scsi_host,
|
|
- PFX "failed receive status %d\n",
|
|
- wc.status);
|
|
- target->qp_in_error = 1;
|
|
- break;
|
|
- }
|
|
+ while ((n = ib_poll_cq(cq, ARRAY_SIZE(wc), wc)) > 0) {
|
|
+ for (i = 0; i < n; ++i) {
|
|
+ if (wc[i].status) {
|
|
+ shost_printk(KERN_ERR, target->scsi_host,
|
|
+ PFX "failed receive status %d\n",
|
|
+ wc[i].status);
|
|
+ target->qp_in_error = 1;
|
|
+ goto out;
|
|
+ }
|
|
|
|
- srp_handle_recv(target, &wc);
|
|
+ srp_handle_recv(target, &wc[i]);
|
|
+ }
|
|
}
|
|
+out:
|
|
+ return;
|
|
}
|
|
|
|
static void srp_send_completion(struct ib_cq *cq, void *target_ptr)
|
|
{
|
|
struct srp_target_port *target = target_ptr;
|
|
- struct ib_wc wc;
|
|
+ struct ib_wc wc[16];
|
|
+ int i, n;
|
|
+ unsigned long flags;
|
|
|
|
- while (ib_poll_cq(cq, 1, &wc) > 0) {
|
|
- if (wc.status) {
|
|
- shost_printk(KERN_ERR, target->scsi_host,
|
|
- PFX "failed send status %d\n",
|
|
- wc.status);
|
|
- target->qp_in_error = 1;
|
|
- break;
|
|
- }
|
|
+ while ((n = ib_poll_cq(cq, ARRAY_SIZE(wc), wc)) > 0) {
|
|
+ spin_lock_irqsave(&target->lock, flags);
|
|
+ for (i = 0; i < n; ++i) {
|
|
+ if (wc[i].status) {
|
|
+ shost_printk(KERN_ERR, target->scsi_host,
|
|
+ PFX "failed send status %d\n",
|
|
+ wc[i].status);
|
|
+ target->qp_in_error = 1;
|
|
+ spin_unlock_irqrestore(&target->lock, flags);
|
|
+ goto out;
|
|
+ }
|
|
|
|
- ++target->tx_tail;
|
|
+ list_add_tail(&target->tx_ring[wc[i].wr_id]->list,
|
|
+ &target->tx_free);
|
|
+ }
|
|
+ spin_unlock_irqrestore(&target->lock, flags);
|
|
}
|
|
+out:
|
|
+ return;
|
|
}
|
|
|
|
static int srp_queuecommand(struct scsi_cmnd *scmnd,
|
|
@@ -1138,6 +1142,7 @@ static int srp_queuecommand(struct scsi_cmnd *scmnd,
|
|
struct srp_cmd *cmd;
|
|
struct ib_device *dev;
|
|
int len;
|
|
+ unsigned long flags;
|
|
|
|
if (target->state == SRP_TARGET_CONNECTING)
|
|
goto err;
|
|
@@ -1157,7 +1162,10 @@ static int srp_queuecommand(struct scsi_cmnd *scmnd,
|
|
ib_dma_sync_single_for_cpu(dev, iu->dma, srp_max_iu_len,
|
|
DMA_TO_DEVICE);
|
|
|
|
+ spin_lock_irqsave(&target->lock, flags);
|
|
req = list_first_entry(&target->free_reqs, struct srp_request, list);
|
|
+ list_del(&req->list);
|
|
+ spin_unlock_irqrestore(&target->lock, flags);
|
|
|
|
scmnd->scsi_done = done;
|
|
scmnd->result = 0;
|
|
@@ -1180,7 +1188,7 @@ static int srp_queuecommand(struct scsi_cmnd *scmnd,
|
|
if (len < 0) {
|
|
shost_printk(KERN_ERR, target->scsi_host,
|
|
PFX "Failed to map data\n");
|
|
- goto err;
|
|
+ goto err_putback;
|
|
}
|
|
|
|
ib_dma_sync_single_for_device(dev, iu->dma, srp_max_iu_len,
|
|
@@ -1188,16 +1196,22 @@ static int srp_queuecommand(struct scsi_cmnd *scmnd,
|
|
|
|
if (__srp_post_send(target, iu, len, SRP_SEND_REQ)) {
|
|
shost_printk(KERN_ERR, target->scsi_host, PFX "Send failed\n");
|
|
+ spin_lock_irqsave(&target->lock, flags);
|
|
+ list_del(&req->list);
|
|
+ spin_unlock_irqrestore(&target->lock, flags);
|
|
goto err_unmap;
|
|
}
|
|
|
|
- list_move_tail(&req->list, &target->req_queue);
|
|
-
|
|
return 0;
|
|
|
|
err_unmap:
|
|
srp_unmap_data(scmnd, target, req);
|
|
|
|
+err_putback:
|
|
+ spin_lock_irqsave(&target->lock, flags);
|
|
+ list_add(&req->list, &target->free_reqs);
|
|
+ spin_unlock_irqrestore(&target->lock, flags);
|
|
+
|
|
err:
|
|
return SCSI_MLQUEUE_HOST_BUSY;
|
|
}
|
|
@@ -1212,14 +1226,19 @@ static int srp_alloc_iu_bufs(struct srp_target_port *target)
|
|
GFP_KERNEL, DMA_FROM_DEVICE);
|
|
if (!target->rx_ring[i])
|
|
goto err;
|
|
+ target->rx_ring[i]->index = i;
|
|
}
|
|
|
|
+ INIT_LIST_HEAD(&target->tx_free);
|
|
+
|
|
for (i = 0; i < SRP_SQ_SIZE; ++i) {
|
|
target->tx_ring[i] = srp_alloc_iu(target->srp_host,
|
|
srp_max_iu_len,
|
|
GFP_KERNEL, DMA_TO_DEVICE);
|
|
if (!target->tx_ring[i])
|
|
goto err;
|
|
+ target->tx_ring[i]->index = i;
|
|
+ list_add_tail(&target->tx_ring[i]->list, &target->tx_free);
|
|
}
|
|
|
|
return 0;
|
|
@@ -1381,7 +1400,8 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
|
|
break;
|
|
|
|
for (i = 0; i < SRP_RQ_SIZE; i++) {
|
|
- target->status = srp_post_recv(target);
|
|
+ target->status = srp_post_recv(target,
|
|
+ target->rx_ring[i]);
|
|
if (target->status)
|
|
break;
|
|
}
|
|
@@ -1451,8 +1471,6 @@ static int srp_send_tsk_mgmt(struct srp_target_port *target,
|
|
struct srp_iu *iu;
|
|
struct srp_tsk_mgmt *tsk_mgmt;
|
|
|
|
- spin_lock_irq(target->scsi_host->host_lock);
|
|
-
|
|
if (target->state == SRP_TARGET_DEAD ||
|
|
target->state == SRP_TARGET_REMOVED) {
|
|
req->scmnd->result = DID_BAD_TARGET << 16;
|
|
@@ -1479,8 +1497,6 @@ static int srp_send_tsk_mgmt(struct srp_target_port *target,
|
|
|
|
req->tsk_mgmt = iu;
|
|
|
|
- spin_unlock_irq(target->scsi_host->host_lock);
|
|
-
|
|
if (!wait_for_completion_timeout(&req->done,
|
|
msecs_to_jiffies(SRP_ABORT_TIMEOUT_MS)))
|
|
return -1;
|
|
@@ -1488,7 +1504,6 @@ static int srp_send_tsk_mgmt(struct srp_target_port *target,
|
|
return 0;
|
|
|
|
out:
|
|
- spin_unlock_irq(target->scsi_host->host_lock);
|
|
return -1;
|
|
}
|
|
|
|
@@ -1519,8 +1534,6 @@ static int srp_abort(struct scsi_cmnd *scmnd)
|
|
if (srp_send_tsk_mgmt(target, req, SRP_TSK_ABORT_TASK))
|
|
return FAILED;
|
|
|
|
- spin_lock_irq(target->scsi_host->host_lock);
|
|
-
|
|
if (req->cmd_done) {
|
|
srp_remove_req(target, req);
|
|
scmnd->scsi_done(scmnd);
|
|
@@ -1530,15 +1543,14 @@ static int srp_abort(struct scsi_cmnd *scmnd)
|
|
} else
|
|
ret = FAILED;
|
|
|
|
- spin_unlock_irq(target->scsi_host->host_lock);
|
|
-
|
|
return ret;
|
|
}
|
|
|
|
static int srp_reset_device(struct scsi_cmnd *scmnd)
|
|
{
|
|
struct srp_target_port *target = host_to_target(scmnd->device->host);
|
|
- struct srp_request *req, *tmp;
|
|
+ struct srp_request *req;
|
|
+ int i;
|
|
|
|
shost_printk(KERN_ERR, target->scsi_host, "SRP reset_device called\n");
|
|
|
|
@@ -1551,14 +1563,10 @@ static int srp_reset_device(struct scsi_cmnd *scmnd)
|
|
if (req->tsk_status)
|
|
return FAILED;
|
|
|
|
- spin_lock_irq(target->scsi_host->host_lock);
|
|
-
|
|
- list_for_each_entry_safe(req, tmp, &target->req_queue, list)
|
|
- if (req->scmnd->device == scmnd->device)
|
|
+ for (i = 0; i < ARRAY_SIZE(target->req_ring); ++i)
|
|
+ if (req->scmnd && req->scmnd->device == scmnd->device)
|
|
srp_reset_req(target, req);
|
|
|
|
- spin_unlock_irq(target->scsi_host->host_lock);
|
|
-
|
|
return SUCCESS;
|
|
}
|
|
|
|
@@ -1981,6 +1989,7 @@ static ssize_t srp_create_target(struct device *dev,
|
|
target_host->transportt = ib_srp_transport_template;
|
|
target_host->max_lun = SRP_MAX_LUN;
|
|
target_host->max_cmd_len = sizeof ((struct srp_cmd *) (void *) 0L)->cdb;
|
|
+ target_host->unlocked_qcmds = true,
|
|
|
|
target = host_to_target(target_host);
|
|
|
|
@@ -1988,8 +1997,9 @@ static ssize_t srp_create_target(struct device *dev,
|
|
target->scsi_host = target_host;
|
|
target->srp_host = host;
|
|
|
|
+ spin_lock_init(&target->lock);
|
|
+
|
|
INIT_LIST_HEAD(&target->free_reqs);
|
|
- INIT_LIST_HEAD(&target->req_queue);
|
|
for (i = 0; i < SRP_CMD_SQ_SIZE; ++i) {
|
|
target->req_ring[i].index = i;
|
|
list_add_tail(&target->req_ring[i].list, &target->free_reqs);
|
|
@@ -2200,6 +2210,7 @@ static void srp_remove_one(struct ib_device *device)
|
|
struct srp_host *host, *tmp_host;
|
|
LIST_HEAD(target_list);
|
|
struct srp_target_port *target, *tmp_target;
|
|
+ unsigned long flags;
|
|
|
|
srp_dev = ib_get_client_data(device, &srp_client);
|
|
|
|
@@ -2217,9 +2228,9 @@ static void srp_remove_one(struct ib_device *device)
|
|
*/
|
|
spin_lock(&host->target_lock);
|
|
list_for_each_entry(target, &host->target_list, list) {
|
|
- spin_lock_irq(target->scsi_host->host_lock);
|
|
+ spin_lock_irqsave(&target->lock, flags);
|
|
target->state = SRP_TARGET_REMOVED;
|
|
- spin_unlock_irq(target->scsi_host->host_lock);
|
|
+ spin_unlock_irqrestore(&target->lock, flags);
|
|
}
|
|
spin_unlock(&host->target_lock);
|
|
|
|
diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h
|
|
index 854ec81..f665cea 100644
|
|
--- a/drivers/infiniband/ulp/srp/ib_srp.h
|
|
+++ b/drivers/infiniband/ulp/srp/ib_srp.h
|
|
@@ -148,19 +148,19 @@ struct srp_target_port {
|
|
struct ib_qp *qp;
|
|
|
|
int max_ti_iu_len;
|
|
+
|
|
+ spinlock_t lock;
|
|
+
|
|
s32 req_lim;
|
|
|
|
int zero_req_lim;
|
|
|
|
- unsigned rx_head;
|
|
struct srp_iu *rx_ring[SRP_RQ_SIZE];
|
|
|
|
- unsigned tx_head;
|
|
- unsigned tx_tail;
|
|
+ struct list_head tx_free;
|
|
struct srp_iu *tx_ring[SRP_SQ_SIZE];
|
|
|
|
struct list_head free_reqs;
|
|
- struct list_head req_queue;
|
|
struct srp_request req_ring[SRP_CMD_SQ_SIZE];
|
|
|
|
struct work_struct work;
|
|
@@ -173,9 +173,11 @@ struct srp_target_port {
|
|
};
|
|
|
|
struct srp_iu {
|
|
+ struct list_head list;
|
|
u64 dma;
|
|
void *buf;
|
|
size_t size;
|
|
+ short index;
|
|
enum dma_data_direction direction;
|
|
};
|
|
|
|
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
|
|
index 8a8f803..716f704 100644
|
|
--- a/drivers/scsi/hosts.c
|
|
+++ b/drivers/scsi/hosts.c
|
|
@@ -65,7 +65,7 @@ static struct class shost_class = {
|
|
**/
|
|
int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state)
|
|
{
|
|
- enum scsi_host_state oldstate = shost->shost_state;
|
|
+ enum scsi_host_state oldstate = atomic_read(&shost->shost_state);
|
|
|
|
if (state == oldstate)
|
|
return 0;
|
|
@@ -136,7 +136,7 @@ int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state)
|
|
}
|
|
break;
|
|
}
|
|
- shost->shost_state = state;
|
|
+ atomic_set(&shost->shost_state, state);
|
|
return 0;
|
|
|
|
illegal:
|
|
@@ -341,7 +341,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
|
|
|
|
shost->host_lock = &shost->default_lock;
|
|
spin_lock_init(shost->host_lock);
|
|
- shost->shost_state = SHOST_CREATED;
|
|
+ atomic_set(&shost->shost_state, SHOST_CREATED);
|
|
INIT_LIST_HEAD(&shost->__devices);
|
|
INIT_LIST_HEAD(&shost->__targets);
|
|
INIT_LIST_HEAD(&shost->eh_cmd_q);
|
|
@@ -503,7 +503,7 @@ EXPORT_SYMBOL(scsi_host_lookup);
|
|
**/
|
|
struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost)
|
|
{
|
|
- if ((shost->shost_state == SHOST_DEL) ||
|
|
+ if (atomic_read(&shost->shost_state) == SHOST_DEL ||
|
|
!get_device(&shost->shost_gendev))
|
|
return NULL;
|
|
return shost;
|
|
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
|
|
index ad0ed21..c150a3d 100644
|
|
--- a/drivers/scsi/scsi.c
|
|
+++ b/drivers/scsi/scsi.c
|
|
@@ -737,23 +737,22 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
|
|
goto out;
|
|
}
|
|
|
|
- spin_lock_irqsave(host->host_lock, flags);
|
|
- /*
|
|
- * AK: unlikely race here: for some reason the timer could
|
|
- * expire before the serial number is set up below.
|
|
- *
|
|
- * TODO: kill serial or move to blk layer
|
|
- */
|
|
- scsi_cmd_get_serial(host, cmd);
|
|
+ cmd->serial_number = 1;
|
|
|
|
- if (unlikely(host->shost_state == SHOST_DEL)) {
|
|
+ if (unlikely(atomic_read(&host->shost_state) == SHOST_DEL)) {
|
|
cmd->result = (DID_NO_CONNECT << 16);
|
|
+ spin_lock_irqsave(host->host_lock, flags);
|
|
scsi_done(cmd);
|
|
+ spin_unlock_irqrestore(host->host_lock, flags);
|
|
} else {
|
|
trace_scsi_dispatch_cmd_start(cmd);
|
|
+ if (!host->unlocked_qcmds)
|
|
+ spin_lock_irqsave(host->host_lock, flags);
|
|
rtn = host->hostt->queuecommand(cmd, scsi_done);
|
|
+ if (!host->unlocked_qcmds)
|
|
+ spin_unlock_irqrestore(host->host_lock, flags);
|
|
}
|
|
- spin_unlock_irqrestore(host->host_lock, flags);
|
|
+
|
|
if (rtn) {
|
|
trace_scsi_dispatch_cmd_error(cmd, rtn);
|
|
if (rtn != SCSI_MLQUEUE_DEVICE_BUSY &&
|
|
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
|
|
index c3f6737..cbcb096 100644
|
|
--- a/drivers/scsi/scsi_sysfs.c
|
|
+++ b/drivers/scsi/scsi_sysfs.c
|
|
@@ -188,7 +188,7 @@ static ssize_t
|
|
show_shost_state(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
struct Scsi_Host *shost = class_to_shost(dev);
|
|
- const char *name = scsi_host_state_name(shost->shost_state);
|
|
+ const char *name = scsi_host_state_name(atomic_read(&shost->shost_state));
|
|
|
|
if (!name)
|
|
return -EINVAL;
|
|
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
|
|
index b7bdecb..d899f57 100644
|
|
--- a/include/scsi/scsi_host.h
|
|
+++ b/include/scsi/scsi_host.h
|
|
@@ -636,6 +636,9 @@ struct Scsi_Host {
|
|
/* Asynchronous scan in progress */
|
|
unsigned async_scan:1;
|
|
|
|
+ /* call queuecommand without Scsi_Host lock held */
|
|
+ unsigned unlocked_qcmds:1;
|
|
+
|
|
/*
|
|
* Optional work queue to be utilized by the transport
|
|
*/
|
|
@@ -670,7 +673,7 @@ struct Scsi_Host {
|
|
unsigned int irq;
|
|
|
|
|
|
- enum scsi_host_state shost_state;
|
|
+ atomic_t shost_state; /* enum scsi_host_state */
|
|
|
|
/* ldm bits */
|
|
struct device shost_gendev, shost_dev;
|
|
@@ -730,9 +733,10 @@ static inline struct Scsi_Host *dev_to_shost(struct device *dev)
|
|
|
|
static inline int scsi_host_in_recovery(struct Scsi_Host *shost)
|
|
{
|
|
- return shost->shost_state == SHOST_RECOVERY ||
|
|
- shost->shost_state == SHOST_CANCEL_RECOVERY ||
|
|
- shost->shost_state == SHOST_DEL_RECOVERY ||
|
|
+ const int shost_state = atomic_read(&shost->shost_state);
|
|
+ return shost_state == SHOST_RECOVERY ||
|
|
+ shost_state == SHOST_CANCEL_RECOVERY ||
|
|
+ shost_state == SHOST_DEL_RECOVERY ||
|
|
shost->tmf_in_progress;
|
|
}
|
|
|
|
@@ -770,7 +774,7 @@ static inline struct device *scsi_get_device(struct Scsi_Host *shost)
|
|
**/
|
|
static inline int scsi_host_scan_allowed(struct Scsi_Host *shost)
|
|
{
|
|
- return shost->shost_state == SHOST_RUNNING;
|
|
+ return atomic_read(&shost->shost_state) == SHOST_RUNNING;
|
|
}
|
|
|
|
extern void scsi_unblock_requests(struct Scsi_Host *);
|
|
EOF
|
|
}
|
|
|
|
# Argument processing
|
|
|
|
if [ $# != 1 ]; then
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
if [ "${1#[0-9]*.[0-9]*.[0-9]*.[0-9]*}" != "$1" ]; then
|
|
kernel_version="${1%.[0-9]*}"
|
|
patch_level="${1#${kernel_version}.}"
|
|
else
|
|
kernel_version="$1"
|
|
fi
|
|
|
|
# Actual kernel source generation
|
|
|
|
if [ -e linux-${kernel_version} -o -e linux-$1 ]; then
|
|
echo "Error: directoy not clean."
|
|
exit 1
|
|
fi
|
|
|
|
echo "Extracting kernel sources ..."
|
|
tar xaf $TARBALLDIR/linux-${kernel_version}.tar.bz2 || exit $?
|
|
if [ "${patch_level}" != "" ]; then
|
|
mv -i linux-${kernel_version} linux-$1 || exit $?
|
|
fi
|
|
cd linux-$1 || exit $?
|
|
|
|
if [ "${patch_level}" != "" ]; then
|
|
patchfile="patch-${kernel_version}.${patch_level}"
|
|
echo "Applying ${patchfile} ..."
|
|
bzip2 -cd <${TARBALLDIR}/${patchfile}.bz2 | \
|
|
patch -p1 -s
|
|
fi
|
|
if [ "${kernel_version}" "<" "2.6.36" ]; then
|
|
# IB/srp: Use print_hex_dump()
|
|
# IB/srp: Make receive buffer handling more robust
|
|
# IB/srp: Export req_lim via sysfs
|
|
for commit in \
|
|
7a7008110b94dfaa90db4b0cc5b0c3f964c80506 \
|
|
c996bb47bb419b7c2f75499e11750142775e5da9 \
|
|
89de74866b846cc48780fda3de7fd223296aaca9
|
|
do
|
|
echo "Applying patch $commit ..."
|
|
url="http://git.kernel.org/?p=linux/kernel/git/roland/infiniband.git;a=patch;h=$commit"
|
|
wget -O- -q "$url" | \
|
|
awk 'BEGIN{h=1}h==0{print}/^diff /{h=0}' | \
|
|
patch -p1 -s
|
|
done
|
|
fi
|
|
# IB/srp: Preparation for transmit ring response allocation
|
|
# IB/srp: Implement SRP_CRED_REQ and SRP_AER_REQ
|
|
# IB/srp: Eliminate two forward declarations
|
|
# IB/srp: Reduce number of BUSY conditions
|
|
# IB/srp: Introduce list_first_entry()
|
|
for p in \
|
|
143381 \
|
|
143391 \
|
|
143401 \
|
|
143411 \
|
|
143421
|
|
do
|
|
echo "Applying patch $p ..."
|
|
url="https://patchwork.kernel.org/patch/$p/raw/"
|
|
wget -O- -q "$url" | patch -p1 -s
|
|
done
|
|
|
|
echo "Applying locking-per-lun patch ..."
|
|
apply_locking_per_lun_patch
|
|
|
|
echo "Applying Jens' block layer optimization patches ..."
|
|
if [ "${kernel_version}" "<" "2.6.38" ]; then
|
|
# block: kill request batching
|
|
# block: add separate sync/async rq allocation mempools
|
|
# block: get rid of request_list->starved[]
|
|
# block: optimize rq allocation path for less queue locking
|
|
for commit in \
|
|
5a00f237eb167a5e98d5f3bb56e11e4da406a5bc \
|
|
76241c12f6a730241b9fa6a795dff55f826ce391 \
|
|
35f2046ac858ca165a8aba477c9236e53a8dbffa \
|
|
38bb177765247024dad4b70a2abe0044d0574998
|
|
do
|
|
echo "Applying patch $commit ..."
|
|
url="http://git.kernel.dk/?p=linux-2.6-block.git;a=patch;h=$commit"
|
|
wget -O- -q "$url" | \
|
|
awk 'BEGIN{h=1}h==0{print}/^diff /{h=0}' | \
|
|
patch -p1 -s
|
|
done
|
|
fi
|