- Decreased number of target-to-initiator request send buffers from two
  to one such that a clear error message can be printed when the SRP
  initiator did not respond to the SRP_CRED_REQ information unit.
- Bug fix: don't modify ch->last_response_req_lim when sending an SRP
  information unit that contains the REQUEST LIMIT DELTA field failed.


git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1424 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Bart Van Assche
2010-01-02 12:27:32 +00:00
parent e1de778c3a
commit d124f1cc8e
2 changed files with 184 additions and 176 deletions

View File

@@ -134,11 +134,6 @@ MODULE_PARM_DESC(use_port_guid_in_session_name,
static void srpt_add_one(struct ib_device *device);
static void srpt_remove_one(struct ib_device *device);
static struct srpt_ioctx *srpt_get_req_ioctx(struct srpt_rdma_ch *ch);
static void srpt_put_req_ioctx(struct srpt_rdma_ch *ch,
struct srpt_ioctx *ioctx);
static int srpt_send_cred_req(struct srpt_rdma_ch *ch);
static void srpt_free_req_ring(struct srpt_rdma_ch *ch);
static void srpt_unregister_mad_agent(struct srpt_device *sdev);
#ifdef CONFIG_SCST_PROC
static void srpt_unregister_procfs_entry(struct scst_tgt_template *tgt);
@@ -1024,6 +1019,115 @@ out:
return ret;
}
/* Obtain an I/O context for sending a request to the initiator. */
static struct srpt_ioctx *srpt_get_req_ioctx(struct srpt_rdma_ch *ch)
{
int tail;
BUG_ON(!ch);
BUILD_BUG_ON(!IS_POWER_OF_2(REQ_IOCTX_COUNT));
tail = atomic_inc_return(&ch->req_ioctx_tail) - 1;
if (tail - atomic_read(&ch->req_ioctx_head) >= REQ_IOCTX_COUNT) {
atomic_dec(&ch->req_ioctx_tail);
return NULL;
}
return ch->req_ioctx_ring[tail & (REQ_IOCTX_COUNT - 1)];
}
/*
* Put back an I/O context that has been used for sending a request to the
* initiator.
*/
static void srpt_put_req_ioctx(struct srpt_rdma_ch *ch,
struct srpt_ioctx *ioctx)
{
int head;
BUG_ON(!ch);
BUILD_BUG_ON(!IS_POWER_OF_2(REQ_IOCTX_COUNT));
head = atomic_inc_return(&ch->req_ioctx_head);
}
/**
* srpt_req_lim_delta() - Compute by how much req_lim changed since the
* last time this function has been called. This value is necessary for
* filling in the REQUEST LIMIT DELTA field of an SRP_RSP response.
*
* Side Effect: Modifies ch->last_response_req_lim.
*/
static int srpt_req_lim_delta(struct srpt_rdma_ch *ch)
{
int req_lim;
int req_lim_delta;
req_lim = atomic_read(&ch->req_lim);
if (req_lim <= 1)
atomic_set(&ch->send_cred_req, 1);
req_lim_delta = req_lim - atomic_read(&ch->last_response_req_lim);
atomic_add(req_lim_delta, &ch->last_response_req_lim);
return req_lim_delta;
}
/* Send an SRP_CRED_REQ information unit to the initiator. */
static int srpt_send_cred_req(struct srpt_rdma_ch *ch,
struct srpt_ioctx *ioctx,
s32 req_lim_delta)
{
struct srp_cred_req *srp_cred_req;
BUG_ON(!ch);
srp_cred_req = ioctx->buf;
BUG_ON(!srp_cred_req);
memset(srp_cred_req, 0, sizeof(*srp_cred_req));
srp_cred_req->opcode = SRP_CRED_REQ;
srp_cred_req->sol_not = ch->crsolnt ? SRP_SOLNT : 0;
srp_cred_req->req_lim_delta = cpu_to_be32(req_lim_delta);
srp_cred_req->tag = cpu_to_be64(ch->cred_req_tag++);
return srpt_post_send(ch, ioctx, sizeof(*srp_cred_req));
}
static void srpt_alloc_and_send_cred_req(struct srpt_rdma_ch *ch)
{
s32 req_lim_delta;
req_lim_delta = srpt_req_lim_delta(ch);
if (req_lim_delta) {
struct srpt_ioctx *ioctx;
int res;
ioctx = srpt_get_req_ioctx(ch);
if (ioctx) {
res = srpt_send_cred_req(ch, ioctx,
req_lim_delta);
if (res == 0) {
PRINT_INFO("Sent SRP_CRED_REQ"
" with req_lim_delta"
" = %d and tag %lld",
req_lim_delta,
ch->cred_req_tag);
} else {
PRINT_ERROR("sending SRP_CRED_REQ"
" failed (res = %d)", res);
goto err;
}
} else {
PRINT_ERROR("%s",
"Sending SRP_CRED_REQ failed -- no I/O context"
" available ! Does the initiator have SRP_CRED_REQ"
" support ? This will sooner or later result in"
" an initiator lockup.");
goto err;
}
}
return;
err:
atomic_sub(req_lim_delta, &ch->last_response_req_lim);
return;
}
static void srpt_reset_ioctx(struct srpt_rdma_ch *ch, struct srpt_ioctx *ioctx)
{
srpt_unmap_sg_to_ib_sge(ch, ioctx);
@@ -1044,11 +1148,10 @@ static void srpt_reset_ioctx(struct srpt_rdma_ch *ch, struct srpt_ioctx *ioctx)
int req_lim;
req_lim = atomic_inc_return(&ch->req_lim);
WARN_ON(req_lim > SRPT_RQ_SIZE);
WARN_ON(req_lim < 0 || req_lim > SRPT_RQ_SIZE);
if (req_lim == SRPT_RQ_SIZE / 2
&& atomic_xchg(&ch->send_cred_req, 0)
&& srpt_send_cred_req(ch) < 0)
PRINT_ERROR("%s", "sending SRP_CRED_REQ failed");
&& atomic_xchg(&ch->send_cred_req, 0))
srpt_alloc_and_send_cred_req(ch);
}
}
@@ -1175,29 +1278,6 @@ static void srpt_handle_rdma_comp(struct srpt_rdma_ch *ch,
}
}
/**
* srpt_req_lim_delta() - Compute by how much req_lim changed since the
* last time this function has been called. This value is necessary for
* filling in the REQUEST LIMIT DELTA field of an SRP_RSP response.
*
* Note: It is possible that one or more requests will be received after
* sdev->req_lim has been read and hence that the req_lim value is larger than
* the actual value. This doesn't cause problems because the initiator
* decrements its request limit variable before sending.
*/
static int srpt_req_lim_delta(struct srpt_rdma_ch *ch)
{
int req_lim;
int req_lim_delta;
req_lim = atomic_read(&ch->req_lim);
if (req_lim <= 1)
atomic_set(&ch->send_cred_req, 1);
req_lim_delta = req_lim - atomic_read(&ch->last_response_req_lim);
atomic_add(req_lim_delta, &ch->last_response_req_lim);
return req_lim_delta;
}
/**
* srpt_build_cmd_rsp() - Build an SRP_RSP response.
* @ch: RDMA channel through which the request has been received.
@@ -1216,7 +1296,8 @@ static int srpt_req_lim_delta(struct srpt_rdma_ch *ch)
* response. See also SPC-2 for more information about sense data.
*/
static int srpt_build_cmd_rsp(struct srpt_rdma_ch *ch,
struct srpt_ioctx *ioctx, u64 tag, int status,
struct srpt_ioctx *ioctx, s32 req_lim_delta,
u64 tag, int status,
const u8 *sense_data, int sense_data_len)
{
struct srp_rsp *srp_rsp;
@@ -1241,7 +1322,7 @@ static int srpt_build_cmd_rsp(struct srpt_rdma_ch *ch,
= (ioctx->sol_not
& (status == SAM_STAT_GOOD ? SRP_SCSOLNT : SRP_UCSOLNT))
? SRP_SOLNT : 0;
srp_rsp->req_lim_delta = cpu_to_be32(srpt_req_lim_delta(ch) + 1);
srp_rsp->req_lim_delta = cpu_to_be32(req_lim_delta);
srp_rsp->tag = tag;
if (SCST_SENSE_VALID(sense_data)) {
@@ -1278,8 +1359,8 @@ static int srpt_build_cmd_rsp(struct srpt_rdma_ch *ch,
* response.
*/
static int srpt_build_tskmgmt_rsp(struct srpt_rdma_ch *ch,
struct srpt_ioctx *ioctx, u8 rsp_code,
u64 tag)
struct srpt_ioctx *ioctx, s32 req_lim_delta,
u8 rsp_code, u64 tag)
{
struct srp_rsp *srp_rsp;
int resp_data_len;
@@ -1301,7 +1382,7 @@ static int srpt_build_tskmgmt_rsp(struct srpt_rdma_ch *ch,
& (rsp_code == SRP_TSK_MGMT_SUCCESS
? SRP_SCSOLNT : SRP_UCSOLNT))
? SRP_SOLNT : 0;
srp_rsp->req_lim_delta = cpu_to_be32(srpt_req_lim_delta(ch) + 1);
srp_rsp->req_lim_delta = cpu_to_be32(req_lim_delta);
srp_rsp->tag = tag;
if (rsp_code != SRP_TSK_MGMT_SUCCESS) {
@@ -1313,42 +1394,6 @@ static int srpt_build_tskmgmt_rsp(struct srpt_rdma_ch *ch,
return resp_len;
}
static int srpt_send_cred_req(struct srpt_rdma_ch *ch)
{
int ret;
s32 req_lim_delta;
struct srpt_ioctx *ioctx;
struct srp_cred_req *srp_cred_req;
BUG_ON(!ch);
ret = -ENOMEM;
ioctx = srpt_get_req_ioctx(ch);
if (!ioctx) {
PRINT_ERROR("%s",
"sending SRP_CRED_REQ failed -- no I/O context"
" available !!");
goto out;
}
srp_cred_req = ioctx->buf;
BUG_ON(!srp_cred_req);
memset(srp_cred_req, 0, sizeof(*srp_cred_req));
req_lim_delta = srpt_req_lim_delta(ch);
PRINT_INFO("Sending SRP_CRED_REQ with req_lim_delta = %d and tag %lld",
req_lim_delta, ch->cred_req_tag);
srp_cred_req->opcode = SRP_CRED_REQ;
srp_cred_req->sol_not = ch->crsolnt ? SRP_SOLNT : 0;
srp_cred_req->req_lim_delta = cpu_to_be32(req_lim_delta);
srp_cred_req->tag = cpu_to_be64(ch->cred_req_tag++);
ret = srpt_post_send(ch, ioctx, sizeof(*srp_cred_req));
if (ret) {
PRINT_ERROR("sending SRP_CRED_REQ failed: ret = %d.", ret);
atomic_sub(req_lim_delta, &ch->last_response_req_lim);
}
out:
return ret;
}
/*
* Process SRP_CMD.
*/
@@ -1611,23 +1656,27 @@ err:
PRINT_ERROR("%s: channel is in state %d", __func__, ch_state);
srpt_reset_ioctx(ch, ioctx);
} else {
s32 req_lim_delta;
req_lim_delta = srpt_req_lim_delta(ch) + 1;
if (srp_cmd->opcode == SRP_TSK_MGMT) {
len = srpt_build_tskmgmt_rsp(ch, ioctx,
len = srpt_build_tskmgmt_rsp(ch, ioctx, req_lim_delta,
srp_tsk_mgmt_status,
((struct srp_tsk_mgmt *)srp_cmd)->tag);
} else if (ioctx->scmnd)
len = srpt_build_cmd_rsp(ch, ioctx, srp_cmd->tag,
srp_response_status,
len = srpt_build_cmd_rsp(ch, ioctx, req_lim_delta,
srp_cmd->tag, srp_response_status,
scst_cmd_get_sense_buffer(ioctx->scmnd),
scst_cmd_get_sense_buffer_len(ioctx->scmnd));
else {
len = srpt_build_cmd_rsp(ch, ioctx, srp_cmd->tag,
req_lim_delta,
srp_response_status,
NULL, 0);
}
if (srpt_post_send(ch, ioctx, len)) {
PRINT_ERROR("%s: sending SRP_RSP response failed",
__func__);
PRINT_ERROR("%s", "Sending SRP_RSP response failed.");
atomic_sub(req_lim_delta, &ch->last_response_req_lim);
srpt_reset_ioctx(ch, ioctx);
}
}
@@ -1701,7 +1750,6 @@ static void srpt_completion(struct ib_cq *cq, void *ctx)
srpt_schedule_thread(ioctx);
} else
srpt_handle_new_iu(ch, ioctx);
continue;
} else if (wc.wr_id & SRPT_OP_TXR) {
TRACE_DBG("received completion for request %llu",
wc.wr_id & ~SRPT_OP_TXR);
@@ -1717,28 +1765,26 @@ static void srpt_completion(struct ib_cq *cq, void *ctx)
atomic_add(ioctx->n_rdma,
&ch->qp_wr_avail);
}
}
if (wc.wr_id & SRPT_OP_TXR)
;
else if (thread) {
ioctx->ch = ch;
ioctx->op = wc.opcode;
srpt_schedule_thread(ioctx);
} else {
switch (wc.opcode) {
case IB_WC_SEND:
srpt_handle_send_comp(ch, ioctx,
scst_estimate_context());
break;
case IB_WC_RDMA_WRITE:
case IB_WC_RDMA_READ:
srpt_handle_rdma_comp(ch, ioctx);
break;
default:
PRINT_ERROR("received unrecognized IB WC"
" opcode %d", wc.opcode);
break;
if (thread) {
ioctx->ch = ch;
ioctx->op = wc.opcode;
srpt_schedule_thread(ioctx);
} else {
switch (wc.opcode) {
case IB_WC_SEND:
srpt_handle_send_comp(ch, ioctx,
scst_estimate_context());
break;
case IB_WC_RDMA_WRITE:
case IB_WC_RDMA_READ:
srpt_handle_rdma_comp(ch, ioctx);
break;
default:
PRINT_ERROR("received unrecognized"
" IB WC opcode %d",
wc.opcode);
break;
}
}
}
@@ -1874,40 +1920,6 @@ static struct srpt_rdma_ch *srpt_find_channel(struct srpt_device *sdev,
return ch;
}
/**
* Release all resources associated with an RDMA channel.
*
* Notes:
* - The caller must have removed the channel from the channel list before
* calling this function.
* - Must be called as a callback function via scst_unregister_session(). Never
* call this function directly because doing so would trigger several race
* conditions.
*/
static void srpt_release_channel(struct scst_session *scst_sess)
{
struct srpt_rdma_ch *ch;
TRACE_ENTRY();
ch = scst_sess_get_tgt_priv(scst_sess);
BUG_ON(!ch);
WARN_ON(srpt_find_channel(ch->sport->sdev, ch->cm_id) == ch);
WARN_ON(atomic_read(&ch->state) != RDMA_CHANNEL_DISCONNECTING);
TRACE_DBG("destroying cm_id %p", ch->cm_id);
BUG_ON(!ch->cm_id);
ib_destroy_cm_id(ch->cm_id);
ib_destroy_qp(ch->qp);
ib_destroy_cq(ch->cq);
srpt_free_req_ring(ch);
kfree(ch);
TRACE_EXIT();
}
/**
* srpt_alloc_req_ring() - Allocate a ring of SRP I/O contexts and set
* the SRPT_OP_TXR flag in the index of each I/O context.
@@ -1946,50 +1958,38 @@ static void srpt_free_req_ring(struct srpt_rdma_ch *ch)
TRACE_EXIT();
}
static struct srpt_ioctx *srpt_get_req_ioctx(struct srpt_rdma_ch *ch)
/**
* Release all resources associated with an RDMA channel.
*
* Notes:
* - The caller must have removed the channel from the channel list before
* calling this function.
* - Must be called as a callback function via scst_unregister_session(). Never
* call this function directly because doing so would trigger several race
* conditions.
*/
static void srpt_release_channel(struct scst_session *scst_sess)
{
int tail;
struct srpt_rdma_ch *ch;
TRACE_ENTRY();
ch = scst_sess_get_tgt_priv(scst_sess);
BUG_ON(!ch);
BUILD_BUG_ON(!IS_POWER_OF_2(REQ_IOCTX_COUNT));
WARN_ON(srpt_find_channel(ch->sport->sdev, ch->cm_id) == ch);
tail = atomic_inc_return(&ch->req_ioctx_tail) - 1;
if (tail - atomic_read(&ch->req_ioctx_head) >= REQ_IOCTX_COUNT) {
atomic_dec(&ch->req_ioctx_tail);
return NULL;
}
#if 0
TRACE_DBG("head = %d, tail = %d -> %d, result = %d",
atomic_read(&ch->req_ioctx_head), tail, tail + 1,
tail & (REQ_IOCTX_COUNT - 1));
#endif
return ch->req_ioctx_ring[tail & (REQ_IOCTX_COUNT - 1)];
}
WARN_ON(atomic_read(&ch->state) != RDMA_CHANNEL_DISCONNECTING);
static void srpt_put_req_ioctx(struct srpt_rdma_ch *ch,
struct srpt_ioctx *ioctx)
{
#ifdef CONFIG_SCST_EXTRACHECKS
int i;
#endif
int head;
TRACE_DBG("destroying cm_id %p", ch->cm_id);
BUG_ON(!ch->cm_id);
ib_destroy_cm_id(ch->cm_id);
BUG_ON(!ch);
BUILD_BUG_ON(!IS_POWER_OF_2(REQ_IOCTX_COUNT));
ib_destroy_qp(ch->qp);
ib_destroy_cq(ch->cq);
srpt_free_req_ring(ch);
kfree(ch);
#ifdef CONFIG_SCST_EXTRACHECKS
for (i = 0; i < ARRAY_SIZE(ch->req_ioctx_ring); ++i)
if (ioctx == ch->req_ioctx_ring[i])
break;
WARN_ON(i == ARRAY_SIZE(ch->req_ioctx_ring));
WARN_ON(i != (atomic_read(&ch->req_ioctx_head)
& (REQ_IOCTX_COUNT - 1)));
#endif
head = atomic_inc_return(&ch->req_ioctx_head);
#if 0
TRACE_DBG("head = %d, tail = %d, result = %d",
head - 1, head, atomic_read(&ch->req_ioctx_tail));
#endif
TRACE_EXIT();
}
/**
@@ -2751,6 +2751,7 @@ static int srpt_xmit_response(struct scst_cmd *scmnd)
{
struct srpt_rdma_ch *ch;
struct srpt_ioctx *ioctx;
s32 req_lim_delta;
int ret = SCST_TGT_RES_SUCCESS;
int dir;
int resp_len;
@@ -2791,7 +2792,8 @@ static int srpt_xmit_response(struct scst_cmd *scmnd)
scst_check_convert_sense(scmnd);
resp_len = srpt_build_cmd_rsp(ch, ioctx,
req_lim_delta = srpt_req_lim_delta(ch) + 1;
resp_len = srpt_build_cmd_rsp(ch, ioctx, req_lim_delta,
scst_cmd_get_tag(scmnd),
scst_cmd_get_status(scmnd),
scst_cmd_get_sense_buffer(scmnd),
@@ -2801,6 +2803,7 @@ static int srpt_xmit_response(struct scst_cmd *scmnd)
PRINT_ERROR("%s[%d]: ch->state= %d tag= %lld",
__func__, __LINE__, atomic_read(&ch->state),
(unsigned long long)scst_cmd_get_tag(scmnd));
atomic_sub(req_lim_delta, &ch->last_response_req_lim);
ret = SCST_TGT_RES_FATAL_ERROR;
}
@@ -2819,6 +2822,7 @@ static void srpt_tsk_mgmt_done(struct scst_mgmt_cmd *mcmnd)
struct srpt_rdma_ch *ch;
struct srpt_mgmt_ioctx *mgmt_ioctx;
struct srpt_ioctx *ioctx;
s32 req_lim_delta;
int rsp_len;
mgmt_ioctx = scst_mgmt_cmd_get_tgt_priv(mcmnd);
@@ -2838,13 +2842,17 @@ static void srpt_tsk_mgmt_done(struct scst_mgmt_cmd *mcmnd)
== SRPT_STATE_ABORTED)
goto out;
rsp_len = srpt_build_tskmgmt_rsp(ch, ioctx,
req_lim_delta = srpt_req_lim_delta(ch) + 1;
rsp_len = srpt_build_tskmgmt_rsp(ch, ioctx, req_lim_delta,
(scst_mgmt_cmd_get_status(mcmnd) ==
SCST_MGMT_STATUS_SUCCESS) ?
SRP_TSK_MGMT_SUCCESS :
SRP_TSK_MGMT_FAILED,
mgmt_ioctx->tag);
srpt_post_send(ch, ioctx, rsp_len);
if (srpt_post_send(ch, ioctx, rsp_len)) {
PRINT_ERROR("%s", "Sending SRP_RSP response failed.");
atomic_sub(req_lim_delta, &ch->last_response_req_lim);
}
scst_mgmt_cmd_set_tgt_priv(mcmnd, NULL);

View File

@@ -126,7 +126,7 @@ enum {
* Number of I/O contexts to be allocated for sending back requests
* from the target to the initiator.
*/
REQ_IOCTX_COUNT = 2,
REQ_IOCTX_COUNT = 1,
};
/* wr_id / wc_id flag for marking requests sent to the initiator. */