[SCSI] iscsi: fix writepsace race

We can race and misset the suspend bit if iscsi_write_space is
called then iscsi_send returns with a failure indicating
there is no space.

To handle this this patch returns a error upwards allowing xmitworker
to decide if we need to try and transmit again. For the no
write space case xmitworker will not retry, and instead
let iscsi_write_space queue it back up if needed (this relies
on the work queue code to properly requeue us if needed).

Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
This commit is contained in:
Mike Christie 2006-05-30 00:37:28 -05:00 committed by James Bottomley
parent 040515f53c
commit 3219e52941
3 changed files with 126 additions and 95 deletions

View File

@ -1006,7 +1006,6 @@ iscsi_write_space(struct sock *sk)
tcp_conn->old_write_space(sk); tcp_conn->old_write_space(sk);
debug_tcp("iscsi_write_space: cid %d\n", conn->id); debug_tcp("iscsi_write_space: cid %d\n", conn->id);
clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
scsi_queue_work(conn->session->host, &conn->xmitwork); scsi_queue_work(conn->session->host, &conn->xmitwork);
} }
@ -1056,7 +1055,7 @@ iscsi_send(struct iscsi_conn *conn, struct iscsi_buf *buf, int size, int flags)
{ {
struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct socket *sk = tcp_conn->sock; struct socket *sk = tcp_conn->sock;
int offset = buf->sg.offset + buf->sent; int offset = buf->sg.offset + buf->sent, res;
/* /*
* if we got use_sg=0 or are sending something we kmallocd * if we got use_sg=0 or are sending something we kmallocd
@ -1067,10 +1066,22 @@ iscsi_send(struct iscsi_conn *conn, struct iscsi_buf *buf, int size, int flags)
* slab case. * slab case.
*/ */
if (buf->use_sendmsg) if (buf->use_sendmsg)
return sock_no_sendpage(sk, buf->sg.page, offset, size, flags); res = sock_no_sendpage(sk, buf->sg.page, offset, size, flags);
else else
return tcp_conn->sendpage(sk, buf->sg.page, offset, size, res = tcp_conn->sendpage(sk, buf->sg.page, offset, size, flags);
flags);
if (res >= 0) {
conn->txdata_octets += res;
buf->sent += res;
return res;
}
tcp_conn->sendpage_failures_cnt++;
if (res == -EAGAIN)
res = -ENOBUFS;
else
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
return res;
} }
/** /**
@ -1085,7 +1096,6 @@ iscsi_send(struct iscsi_conn *conn, struct iscsi_buf *buf, int size, int flags)
static inline int static inline int
iscsi_sendhdr(struct iscsi_conn *conn, struct iscsi_buf *buf, int datalen) iscsi_sendhdr(struct iscsi_conn *conn, struct iscsi_buf *buf, int datalen)
{ {
struct iscsi_tcp_conn *tcp_conn;
int flags = 0; /* MSG_DONTWAIT; */ int flags = 0; /* MSG_DONTWAIT; */
int res, size; int res, size;
@ -1097,17 +1107,10 @@ iscsi_sendhdr(struct iscsi_conn *conn, struct iscsi_buf *buf, int datalen)
res = iscsi_send(conn, buf, size, flags); res = iscsi_send(conn, buf, size, flags);
debug_tcp("sendhdr %d bytes, sent %d res %d\n", size, buf->sent, res); debug_tcp("sendhdr %d bytes, sent %d res %d\n", size, buf->sent, res);
if (res >= 0) { if (res >= 0) {
conn->txdata_octets += res;
buf->sent += res;
if (size != res) if (size != res)
return -EAGAIN; return -EAGAIN;
return 0; return 0;
} else if (res == -EAGAIN) { }
tcp_conn = conn->dd_data;
tcp_conn->sendpage_failures_cnt++;
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
} else if (res == -EPIPE)
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
return res; return res;
} }
@ -1126,7 +1129,6 @@ static inline int
iscsi_sendpage(struct iscsi_conn *conn, struct iscsi_buf *buf, iscsi_sendpage(struct iscsi_conn *conn, struct iscsi_buf *buf,
int *count, int *sent) int *count, int *sent)
{ {
struct iscsi_tcp_conn *tcp_conn;
int flags = 0; /* MSG_DONTWAIT; */ int flags = 0; /* MSG_DONTWAIT; */
int res, size; int res, size;
@ -1141,19 +1143,12 @@ iscsi_sendpage(struct iscsi_conn *conn, struct iscsi_buf *buf,
debug_tcp("sendpage: %d bytes, sent %d left %d sent %d res %d\n", debug_tcp("sendpage: %d bytes, sent %d left %d sent %d res %d\n",
size, buf->sent, *count, *sent, res); size, buf->sent, *count, *sent, res);
if (res >= 0) { if (res >= 0) {
conn->txdata_octets += res;
buf->sent += res;
*count -= res; *count -= res;
*sent += res; *sent += res;
if (size != res) if (size != res)
return -EAGAIN; return -EAGAIN;
return 0; return 0;
} else if (res == -EAGAIN) { }
tcp_conn = conn->dd_data;
tcp_conn->sendpage_failures_cnt++;
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
} else if (res == -EPIPE)
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
return res; return res;
} }
@ -1342,6 +1337,7 @@ static int
iscsi_tcp_mtask_xmit(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask) iscsi_tcp_mtask_xmit(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
{ {
struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data; struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data;
int rc;
debug_scsi("mtask deq [cid %d state %x itt 0x%x]\n", debug_scsi("mtask deq [cid %d state %x itt 0x%x]\n",
conn->id, tcp_mtask->xmstate, mtask->itt); conn->id, tcp_mtask->xmstate, mtask->itt);
@ -1355,12 +1351,13 @@ iscsi_tcp_mtask_xmit(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
conn->hdrdgst_en) conn->hdrdgst_en)
iscsi_hdr_digest(conn, &tcp_mtask->headbuf, iscsi_hdr_digest(conn, &tcp_mtask->headbuf,
(u8*)tcp_mtask->hdrext); (u8*)tcp_mtask->hdrext);
if (iscsi_sendhdr(conn, &tcp_mtask->headbuf, rc = iscsi_sendhdr(conn, &tcp_mtask->headbuf,
mtask->data_count)) { mtask->data_count);
if (rc) {
tcp_mtask->xmstate |= XMSTATE_IMM_HDR; tcp_mtask->xmstate |= XMSTATE_IMM_HDR;
if (mtask->data_count) if (mtask->data_count)
tcp_mtask->xmstate &= ~XMSTATE_IMM_DATA; tcp_mtask->xmstate &= ~XMSTATE_IMM_DATA;
return -EAGAIN; return rc;
} }
} }
@ -1371,10 +1368,13 @@ iscsi_tcp_mtask_xmit(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
* Virtual buffer could be spreaded across multiple pages... * Virtual buffer could be spreaded across multiple pages...
*/ */
do { do {
if (iscsi_sendpage(conn, &tcp_mtask->sendbuf, int rc;
&mtask->data_count, &tcp_mtask->sent)) {
rc = iscsi_sendpage(conn, &tcp_mtask->sendbuf,
&mtask->data_count, &tcp_mtask->sent);
if (rc) {
tcp_mtask->xmstate |= XMSTATE_IMM_DATA; tcp_mtask->xmstate |= XMSTATE_IMM_DATA;
return -EAGAIN; return rc;
} }
} while (mtask->data_count); } while (mtask->data_count);
} }
@ -1396,16 +1396,19 @@ static inline int
handle_xmstate_r_hdr(struct iscsi_conn *conn, handle_xmstate_r_hdr(struct iscsi_conn *conn,
struct iscsi_tcp_cmd_task *tcp_ctask) struct iscsi_tcp_cmd_task *tcp_ctask)
{ {
int rc;
tcp_ctask->xmstate &= ~XMSTATE_R_HDR; tcp_ctask->xmstate &= ~XMSTATE_R_HDR;
if (conn->hdrdgst_en) if (conn->hdrdgst_en)
iscsi_hdr_digest(conn, &tcp_ctask->headbuf, iscsi_hdr_digest(conn, &tcp_ctask->headbuf,
(u8*)tcp_ctask->hdrext); (u8*)tcp_ctask->hdrext);
if (!iscsi_sendhdr(conn, &tcp_ctask->headbuf, 0)) { rc = iscsi_sendhdr(conn, &tcp_ctask->headbuf, 0);
if (!rc) {
BUG_ON(tcp_ctask->xmstate != XMSTATE_IDLE); BUG_ON(tcp_ctask->xmstate != XMSTATE_IDLE);
return 0; /* wait for Data-In */ return 0; /* wait for Data-In */
} }
tcp_ctask->xmstate |= XMSTATE_R_HDR; tcp_ctask->xmstate |= XMSTATE_R_HDR;
return -EAGAIN; return rc;
} }
static inline int static inline int
@ -1413,16 +1416,16 @@ handle_xmstate_w_hdr(struct iscsi_conn *conn,
struct iscsi_cmd_task *ctask) struct iscsi_cmd_task *ctask)
{ {
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
int rc;
tcp_ctask->xmstate &= ~XMSTATE_W_HDR; tcp_ctask->xmstate &= ~XMSTATE_W_HDR;
if (conn->hdrdgst_en) if (conn->hdrdgst_en)
iscsi_hdr_digest(conn, &tcp_ctask->headbuf, iscsi_hdr_digest(conn, &tcp_ctask->headbuf,
(u8*)tcp_ctask->hdrext); (u8*)tcp_ctask->hdrext);
if (iscsi_sendhdr(conn, &tcp_ctask->headbuf, ctask->imm_count)) { rc = iscsi_sendhdr(conn, &tcp_ctask->headbuf, ctask->imm_count);
if (rc)
tcp_ctask->xmstate |= XMSTATE_W_HDR; tcp_ctask->xmstate |= XMSTATE_W_HDR;
return -EAGAIN; return rc;
}
return 0;
} }
static inline int static inline int
@ -1430,17 +1433,19 @@ handle_xmstate_data_digest(struct iscsi_conn *conn,
struct iscsi_cmd_task *ctask) struct iscsi_cmd_task *ctask)
{ {
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
int rc;
tcp_ctask->xmstate &= ~XMSTATE_DATA_DIGEST; tcp_ctask->xmstate &= ~XMSTATE_DATA_DIGEST;
debug_tcp("resent data digest 0x%x\n", tcp_ctask->datadigest); debug_tcp("resent data digest 0x%x\n", tcp_ctask->datadigest);
if (iscsi_digest_final_send(conn, ctask, &tcp_ctask->immbuf, rc = iscsi_digest_final_send(conn, ctask, &tcp_ctask->immbuf,
&tcp_ctask->datadigest, 0)) { &tcp_ctask->datadigest, 0);
if (rc) {
tcp_ctask->xmstate |= XMSTATE_DATA_DIGEST; tcp_ctask->xmstate |= XMSTATE_DATA_DIGEST;
debug_tcp("resent data digest 0x%x fail!\n", debug_tcp("resent data digest 0x%x fail!\n",
tcp_ctask->datadigest); tcp_ctask->datadigest);
return -EAGAIN;
} }
return 0;
return rc;
} }
static inline int static inline int
@ -1448,6 +1453,7 @@ handle_xmstate_imm_data(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
{ {
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
int rc;
BUG_ON(!ctask->imm_count); BUG_ON(!ctask->imm_count);
tcp_ctask->xmstate &= ~XMSTATE_IMM_DATA; tcp_ctask->xmstate &= ~XMSTATE_IMM_DATA;
@ -1458,8 +1464,9 @@ handle_xmstate_imm_data(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
} }
for (;;) { for (;;) {
if (iscsi_sendpage(conn, &tcp_ctask->sendbuf, &ctask->imm_count, rc = iscsi_sendpage(conn, &tcp_ctask->sendbuf,
&tcp_ctask->sent)) { &ctask->imm_count, &tcp_ctask->sent);
if (rc) {
tcp_ctask->xmstate |= XMSTATE_IMM_DATA; tcp_ctask->xmstate |= XMSTATE_IMM_DATA;
if (conn->datadgst_en) { if (conn->datadgst_en) {
crypto_digest_final(tcp_conn->data_tx_tfm, crypto_digest_final(tcp_conn->data_tx_tfm,
@ -1467,7 +1474,7 @@ handle_xmstate_imm_data(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
debug_tcp("tx imm sendpage fail 0x%x\n", debug_tcp("tx imm sendpage fail 0x%x\n",
tcp_ctask->datadigest); tcp_ctask->datadigest);
} }
return -EAGAIN; return rc;
} }
if (conn->datadgst_en) if (conn->datadgst_en)
crypto_digest_update(tcp_conn->data_tx_tfm, crypto_digest_update(tcp_conn->data_tx_tfm,
@ -1480,11 +1487,12 @@ handle_xmstate_imm_data(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
} }
if (conn->datadgst_en && !(tcp_ctask->xmstate & XMSTATE_W_PAD)) { if (conn->datadgst_en && !(tcp_ctask->xmstate & XMSTATE_W_PAD)) {
if (iscsi_digest_final_send(conn, ctask, &tcp_ctask->immbuf, rc = iscsi_digest_final_send(conn, ctask, &tcp_ctask->immbuf,
&tcp_ctask->immdigest, 1)) { &tcp_ctask->immdigest, 1);
if (rc) {
debug_tcp("sending imm digest 0x%x fail!\n", debug_tcp("sending imm digest 0x%x fail!\n",
tcp_ctask->immdigest); tcp_ctask->immdigest);
return -EAGAIN; return rc;
} }
debug_tcp("sending imm digest 0x%x\n", tcp_ctask->immdigest); debug_tcp("sending imm digest 0x%x\n", tcp_ctask->immdigest);
} }
@ -1497,6 +1505,7 @@ handle_xmstate_uns_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
{ {
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_data_task *dtask; struct iscsi_data_task *dtask;
int rc;
tcp_ctask->xmstate |= XMSTATE_UNS_DATA; tcp_ctask->xmstate |= XMSTATE_UNS_DATA;
if (tcp_ctask->xmstate & XMSTATE_UNS_INIT) { if (tcp_ctask->xmstate & XMSTATE_UNS_INIT) {
@ -1507,10 +1516,12 @@ handle_xmstate_uns_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
(u8*)dtask->hdrext); (u8*)dtask->hdrext);
tcp_ctask->xmstate &= ~XMSTATE_UNS_INIT; tcp_ctask->xmstate &= ~XMSTATE_UNS_INIT;
} }
if (iscsi_sendhdr(conn, &tcp_ctask->headbuf, ctask->data_count)) {
rc = iscsi_sendhdr(conn, &tcp_ctask->headbuf, ctask->data_count);
if (rc) {
tcp_ctask->xmstate &= ~XMSTATE_UNS_DATA; tcp_ctask->xmstate &= ~XMSTATE_UNS_DATA;
tcp_ctask->xmstate |= XMSTATE_UNS_HDR; tcp_ctask->xmstate |= XMSTATE_UNS_HDR;
return -EAGAIN; return rc;
} }
debug_scsi("uns dout [itt 0x%x dlen %d sent %d]\n", debug_scsi("uns dout [itt 0x%x dlen %d sent %d]\n",
@ -1524,6 +1535,7 @@ handle_xmstate_uns_data(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_data_task *dtask = tcp_ctask->dtask; struct iscsi_data_task *dtask = tcp_ctask->dtask;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
int rc;
BUG_ON(!ctask->data_count); BUG_ON(!ctask->data_count);
tcp_ctask->xmstate &= ~XMSTATE_UNS_DATA; tcp_ctask->xmstate &= ~XMSTATE_UNS_DATA;
@ -1536,8 +1548,9 @@ handle_xmstate_uns_data(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
for (;;) { for (;;) {
int start = tcp_ctask->sent; int start = tcp_ctask->sent;
if (iscsi_sendpage(conn, &tcp_ctask->sendbuf, rc = iscsi_sendpage(conn, &tcp_ctask->sendbuf,
&ctask->data_count, &tcp_ctask->sent)) { &ctask->data_count, &tcp_ctask->sent);
if (rc) {
ctask->unsol_count -= tcp_ctask->sent - start; ctask->unsol_count -= tcp_ctask->sent - start;
tcp_ctask->xmstate |= XMSTATE_UNS_DATA; tcp_ctask->xmstate |= XMSTATE_UNS_DATA;
/* will continue with this ctask later.. */ /* will continue with this ctask later.. */
@ -1547,7 +1560,7 @@ handle_xmstate_uns_data(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
debug_tcp("tx uns data fail 0x%x\n", debug_tcp("tx uns data fail 0x%x\n",
dtask->digest); dtask->digest);
} }
return -EAGAIN; return rc;
} }
BUG_ON(tcp_ctask->sent > ctask->total_length); BUG_ON(tcp_ctask->sent > ctask->total_length);
@ -1574,12 +1587,13 @@ handle_xmstate_uns_data(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
*/ */
if (ctask->unsol_count) { if (ctask->unsol_count) {
if (conn->datadgst_en) { if (conn->datadgst_en) {
if (iscsi_digest_final_send(conn, ctask, rc = iscsi_digest_final_send(conn, ctask,
&dtask->digestbuf, &dtask->digestbuf,
&dtask->digest, 1)) { &dtask->digest, 1);
if (rc) {
debug_tcp("send uns digest 0x%x fail\n", debug_tcp("send uns digest 0x%x fail\n",
dtask->digest); dtask->digest);
return -EAGAIN; return rc;
} }
debug_tcp("sending uns digest 0x%x, more uns\n", debug_tcp("sending uns digest 0x%x, more uns\n",
dtask->digest); dtask->digest);
@ -1589,12 +1603,13 @@ handle_xmstate_uns_data(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
} }
if (conn->datadgst_en && !(tcp_ctask->xmstate & XMSTATE_W_PAD)) { if (conn->datadgst_en && !(tcp_ctask->xmstate & XMSTATE_W_PAD)) {
if (iscsi_digest_final_send(conn, ctask, rc = iscsi_digest_final_send(conn, ctask,
&dtask->digestbuf, &dtask->digestbuf,
&dtask->digest, 1)) { &dtask->digest, 1);
if (rc) {
debug_tcp("send last uns digest 0x%x fail\n", debug_tcp("send last uns digest 0x%x fail\n",
dtask->digest); dtask->digest);
return -EAGAIN; return rc;
} }
debug_tcp("sending uns digest 0x%x\n",dtask->digest); debug_tcp("sending uns digest 0x%x\n",dtask->digest);
} }
@ -1610,7 +1625,7 @@ handle_xmstate_sol_data(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_r2t_info *r2t = tcp_ctask->r2t; struct iscsi_r2t_info *r2t = tcp_ctask->r2t;
struct iscsi_data_task *dtask = &r2t->dtask; struct iscsi_data_task *dtask = &r2t->dtask;
int left; int left, rc;
tcp_ctask->xmstate &= ~XMSTATE_SOL_DATA; tcp_ctask->xmstate &= ~XMSTATE_SOL_DATA;
tcp_ctask->dtask = dtask; tcp_ctask->dtask = dtask;
@ -1626,7 +1641,8 @@ solicit_again:
if (!r2t->data_count) if (!r2t->data_count)
goto data_out_done; goto data_out_done;
if (iscsi_sendpage(conn, &r2t->sendbuf, &r2t->data_count, &r2t->sent)) { rc = iscsi_sendpage(conn, &r2t->sendbuf, &r2t->data_count, &r2t->sent);
if (rc) {
tcp_ctask->xmstate |= XMSTATE_SOL_DATA; tcp_ctask->xmstate |= XMSTATE_SOL_DATA;
/* will continue with this ctask later.. */ /* will continue with this ctask later.. */
if (conn->datadgst_en) { if (conn->datadgst_en) {
@ -1634,7 +1650,7 @@ solicit_again:
(u8 *)&dtask->digest); (u8 *)&dtask->digest);
debug_tcp("r2t data send fail 0x%x\n", dtask->digest); debug_tcp("r2t data send fail 0x%x\n", dtask->digest);
} }
return -EAGAIN; return rc;
} }
BUG_ON(r2t->data_count < 0); BUG_ON(r2t->data_count < 0);
@ -1661,12 +1677,13 @@ data_out_done:
left = r2t->data_length - r2t->sent; left = r2t->data_length - r2t->sent;
if (left) { if (left) {
if (conn->datadgst_en) { if (conn->datadgst_en) {
if (iscsi_digest_final_send(conn, ctask, rc = iscsi_digest_final_send(conn, ctask,
&dtask->digestbuf, &dtask->digestbuf,
&dtask->digest, 1)) { &dtask->digest, 1);
if (rc) {
debug_tcp("send r2t data digest 0x%x" debug_tcp("send r2t data digest 0x%x"
"fail\n", dtask->digest); "fail\n", dtask->digest);
return -EAGAIN; return rc;
} }
debug_tcp("r2t data send digest 0x%x\n", debug_tcp("r2t data send digest 0x%x\n",
dtask->digest); dtask->digest);
@ -1683,11 +1700,12 @@ data_out_done:
*/ */
BUG_ON(tcp_ctask->r2t_data_count - r2t->data_length < 0); BUG_ON(tcp_ctask->r2t_data_count - r2t->data_length < 0);
if (conn->datadgst_en) { if (conn->datadgst_en) {
if (iscsi_digest_final_send(conn, ctask, &dtask->digestbuf, rc = iscsi_digest_final_send(conn, ctask, &dtask->digestbuf,
&dtask->digest, 1)) { &dtask->digest, 1);
if (rc) {
debug_tcp("send last r2t data digest 0x%x" debug_tcp("send last r2t data digest 0x%x"
"fail\n", dtask->digest); "fail\n", dtask->digest);
return -EAGAIN; return rc;
} }
debug_tcp("r2t done dout digest 0x%x\n", dtask->digest); debug_tcp("r2t done dout digest 0x%x\n", dtask->digest);
} }
@ -1713,15 +1731,16 @@ handle_xmstate_w_pad(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct iscsi_data_task *dtask = tcp_ctask->dtask; struct iscsi_data_task *dtask = tcp_ctask->dtask;
int sent; int sent, rc;
tcp_ctask->xmstate &= ~XMSTATE_W_PAD; tcp_ctask->xmstate &= ~XMSTATE_W_PAD;
iscsi_buf_init_iov(&tcp_ctask->sendbuf, (char*)&tcp_ctask->pad, iscsi_buf_init_iov(&tcp_ctask->sendbuf, (char*)&tcp_ctask->pad,
tcp_ctask->pad_count); tcp_ctask->pad_count);
if (iscsi_sendpage(conn, &tcp_ctask->sendbuf, &tcp_ctask->pad_count, rc = iscsi_sendpage(conn, &tcp_ctask->sendbuf, &tcp_ctask->pad_count,
&sent)) { &sent);
if (rc) {
tcp_ctask->xmstate |= XMSTATE_W_PAD; tcp_ctask->xmstate |= XMSTATE_W_PAD;
return -EAGAIN; return rc;
} }
if (conn->datadgst_en) { if (conn->datadgst_en) {
@ -1729,22 +1748,24 @@ handle_xmstate_w_pad(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
&tcp_ctask->sendbuf.sg, 1); &tcp_ctask->sendbuf.sg, 1);
/* imm data? */ /* imm data? */
if (!dtask) { if (!dtask) {
if (iscsi_digest_final_send(conn, ctask, rc = iscsi_digest_final_send(conn, ctask,
&tcp_ctask->immbuf, &tcp_ctask->immbuf,
&tcp_ctask->immdigest, 1)) { &tcp_ctask->immdigest, 1);
if (rc) {
debug_tcp("send padding digest 0x%x" debug_tcp("send padding digest 0x%x"
"fail!\n", tcp_ctask->immdigest); "fail!\n", tcp_ctask->immdigest);
return -EAGAIN; return rc;
} }
debug_tcp("done with padding, digest 0x%x\n", debug_tcp("done with padding, digest 0x%x\n",
tcp_ctask->datadigest); tcp_ctask->datadigest);
} else { } else {
if (iscsi_digest_final_send(conn, ctask, rc = iscsi_digest_final_send(conn, ctask,
&dtask->digestbuf, &dtask->digestbuf,
&dtask->digest, 1)) { &dtask->digest, 1);
if (rc) {
debug_tcp("send padding digest 0x%x" debug_tcp("send padding digest 0x%x"
"fail\n", dtask->digest); "fail\n", dtask->digest);
return -EAGAIN; return rc;
} }
debug_tcp("done with padding, digest 0x%x\n", debug_tcp("done with padding, digest 0x%x\n",
dtask->digest); dtask->digest);
@ -1769,10 +1790,8 @@ iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
if (ctask->mtask) if (ctask->mtask)
return rc; return rc;
if (tcp_ctask->xmstate & XMSTATE_R_HDR) { if (tcp_ctask->xmstate & XMSTATE_R_HDR)
rc = handle_xmstate_r_hdr(conn, tcp_ctask); return handle_xmstate_r_hdr(conn, tcp_ctask);
return rc;
}
if (tcp_ctask->xmstate & XMSTATE_W_HDR) { if (tcp_ctask->xmstate & XMSTATE_W_HDR) {
rc = handle_xmstate_w_hdr(conn, ctask); rc = handle_xmstate_w_hdr(conn, ctask);
@ -1824,10 +1843,11 @@ solicit_head_again:
if (conn->hdrdgst_en) if (conn->hdrdgst_en)
iscsi_hdr_digest(conn, &r2t->headbuf, iscsi_hdr_digest(conn, &r2t->headbuf,
(u8*)r2t->dtask.hdrext); (u8*)r2t->dtask.hdrext);
if (iscsi_sendhdr(conn, &r2t->headbuf, r2t->data_count)) { rc = iscsi_sendhdr(conn, &r2t->headbuf, r2t->data_count);
if (rc) {
tcp_ctask->xmstate &= ~XMSTATE_SOL_DATA; tcp_ctask->xmstate &= ~XMSTATE_SOL_DATA;
tcp_ctask->xmstate |= XMSTATE_SOL_HDR; tcp_ctask->xmstate |= XMSTATE_SOL_HDR;
return -EAGAIN; return rc;
} }
debug_scsi("sol dout [dsn %d itt 0x%x dlen %d sent %d]\n", debug_scsi("sol dout [dsn %d itt 0x%x dlen %d sent %d]\n",

View File

@ -513,10 +513,11 @@ EXPORT_SYMBOL_GPL(iscsi_conn_failure);
static int iscsi_data_xmit(struct iscsi_conn *conn) static int iscsi_data_xmit(struct iscsi_conn *conn)
{ {
struct iscsi_transport *tt; struct iscsi_transport *tt;
int rc = 0;
if (unlikely(conn->suspend_tx)) { if (unlikely(conn->suspend_tx)) {
debug_scsi("conn %d Tx suspended!\n", conn->id); debug_scsi("conn %d Tx suspended!\n", conn->id);
return 0; return -ENODATA;
} }
tt = conn->session->tt; tt = conn->session->tt;
@ -536,13 +537,15 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
BUG_ON(conn->ctask && conn->mtask); BUG_ON(conn->ctask && conn->mtask);
if (conn->ctask) { if (conn->ctask) {
if (tt->xmit_cmd_task(conn, conn->ctask)) rc = tt->xmit_cmd_task(conn, conn->ctask);
if (rc)
goto again; goto again;
/* done with this in-progress ctask */ /* done with this in-progress ctask */
conn->ctask = NULL; conn->ctask = NULL;
} }
if (conn->mtask) { if (conn->mtask) {
if (tt->xmit_mgmt_task(conn, conn->mtask)) rc = tt->xmit_mgmt_task(conn, conn->mtask);
if (rc)
goto again; goto again;
/* done with this in-progress mtask */ /* done with this in-progress mtask */
conn->mtask = NULL; conn->mtask = NULL;
@ -556,7 +559,8 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
list_add_tail(&conn->mtask->running, list_add_tail(&conn->mtask->running,
&conn->mgmt_run_list); &conn->mgmt_run_list);
spin_unlock_bh(&conn->session->lock); spin_unlock_bh(&conn->session->lock);
if (tt->xmit_mgmt_task(conn, conn->mtask)) rc = tt->xmit_mgmt_task(conn, conn->mtask);
if (rc)
goto again; goto again;
} }
/* done with this mtask */ /* done with this mtask */
@ -574,7 +578,8 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
if (list_empty(&conn->ctask->running)) if (list_empty(&conn->ctask->running))
list_add_tail(&conn->ctask->running, &conn->run_list); list_add_tail(&conn->ctask->running, &conn->run_list);
spin_unlock_bh(&conn->session->lock); spin_unlock_bh(&conn->session->lock);
if (tt->xmit_cmd_task(conn, conn->ctask)) rc = tt->xmit_cmd_task(conn, conn->ctask);
if (rc)
goto again; goto again;
} }
/* done with this ctask */ /* done with this ctask */
@ -588,32 +593,34 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
list_add_tail(&conn->mtask->running, list_add_tail(&conn->mtask->running,
&conn->mgmt_run_list); &conn->mgmt_run_list);
spin_unlock_bh(&conn->session->lock); spin_unlock_bh(&conn->session->lock);
if (tt->xmit_mgmt_task(conn, conn->mtask)) rc = tt->xmit_mgmt_task(conn, conn->mtask);
if (rc)
goto again; goto again;
} }
/* done with this mtask */ /* done with this mtask */
conn->mtask = NULL; conn->mtask = NULL;
} }
return 0; return -ENODATA;
again: again:
if (unlikely(conn->suspend_tx)) if (unlikely(conn->suspend_tx))
return 0; return -ENODATA;
return -EAGAIN; return rc;
} }
static void iscsi_xmitworker(void *data) static void iscsi_xmitworker(void *data)
{ {
struct iscsi_conn *conn = data; struct iscsi_conn *conn = data;
int rc;
/* /*
* serialize Xmit worker on a per-connection basis. * serialize Xmit worker on a per-connection basis.
*/ */
mutex_lock(&conn->xmitmutex); mutex_lock(&conn->xmitmutex);
if (iscsi_data_xmit(conn)) do {
scsi_queue_work(conn->session->host, &conn->xmitwork); rc = iscsi_data_xmit(conn);
} while (rc >= 0 || rc == -EAGAIN);
mutex_unlock(&conn->xmitmutex); mutex_unlock(&conn->xmitmutex);
} }

View File

@ -57,8 +57,12 @@ struct iscsi_mgmt_task;
* Called from queuecommand with session lock held. * Called from queuecommand with session lock held.
* @init_mgmt_task: Initialize a iscsi_mgmt_task and any internal structs. * @init_mgmt_task: Initialize a iscsi_mgmt_task and any internal structs.
* Called from iscsi_conn_send_generic with xmitmutex. * Called from iscsi_conn_send_generic with xmitmutex.
* @xmit_cmd_task: requests LLD to transfer cmd task * @xmit_cmd_task: Requests LLD to transfer cmd task. Returns 0 or the
* @xmit_mgmt_task: requests LLD to transfer mgmt task * the number of bytes transferred on success, and -Exyz
* value on error.
* @xmit_mgmt_task: Requests LLD to transfer mgmt task. Returns 0 or the
* the number of bytes transferred on success, and -Exyz
* value on error.
* @cleanup_cmd_task: requests LLD to fail cmd task. Called with xmitmutex * @cleanup_cmd_task: requests LLD to fail cmd task. Called with xmitmutex
* and session->lock after the connection has been * and session->lock after the connection has been
* suspended and terminated during recovery. If called * suspended and terminated during recovery. If called