Newer
Older
* Use rd_desc to pass 'conn' to iscsi_tcp_data_recv.
* We set count to 1 because we want the network layer to
* hand us all the skbs that are available. iscsi_tcp_data_recv
* handled pdus that cross buffers or pdus that still need data.
*/
rd_desc.arg.data = conn;
tcp_read_sock(sk, &rd_desc, iscsi_tcp_data_recv);
read_unlock(&sk->sk_callback_lock);
}
static void
iscsi_tcp_state_change(struct sock *sk)
{
struct iscsi_tcp_conn *tcp_conn;
struct iscsi_conn *conn;
struct iscsi_session *session;
void (*old_state_change)(struct sock *);
read_lock(&sk->sk_callback_lock);
conn = (struct iscsi_conn*)sk->sk_user_data;
session = conn->session;
if ((sk->sk_state == TCP_CLOSE_WAIT ||
sk->sk_state == TCP_CLOSE) &&
!atomic_read(&sk->sk_rmem_alloc)) {
debug_tcp("iscsi_tcp_state_change: TCP_CLOSE|TCP_CLOSE_WAIT\n");
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
}
tcp_conn = conn->dd_data;
old_state_change = tcp_conn->old_state_change;
read_unlock(&sk->sk_callback_lock);
old_state_change(sk);
}
/**
* iscsi_write_space - Called when more output buffer space is available
* @sk: socket space is available for
**/
static void
iscsi_write_space(struct sock *sk)
{
struct iscsi_conn *conn = (struct iscsi_conn*)sk->sk_user_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
tcp_conn->old_write_space(sk);
debug_tcp("iscsi_write_space: cid %d\n", conn->id);
scsi_queue_work(conn->session->host, &conn->xmitwork);
}
static void
iscsi_conn_set_callbacks(struct iscsi_conn *conn)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct sock *sk = tcp_conn->sock->sk;
/* assign new callbacks */
write_lock_bh(&sk->sk_callback_lock);
sk->sk_user_data = conn;
tcp_conn->old_data_ready = sk->sk_data_ready;
tcp_conn->old_state_change = sk->sk_state_change;
tcp_conn->old_write_space = sk->sk_write_space;
sk->sk_data_ready = iscsi_tcp_data_ready;
sk->sk_state_change = iscsi_tcp_state_change;
sk->sk_write_space = iscsi_write_space;
write_unlock_bh(&sk->sk_callback_lock);
}
static void
iscsi_conn_restore_callbacks(struct iscsi_tcp_conn *tcp_conn)
struct sock *sk = tcp_conn->sock->sk;
/* restore socket callbacks, see also: iscsi_conn_set_callbacks() */
write_lock_bh(&sk->sk_callback_lock);
sk->sk_user_data = NULL;
sk->sk_data_ready = tcp_conn->old_data_ready;
sk->sk_state_change = tcp_conn->old_state_change;
sk->sk_write_space = tcp_conn->old_write_space;
sk->sk_no_check = 0;
write_unlock_bh(&sk->sk_callback_lock);
}
/**
* iscsi_send - generic send routine
* @sk: kernel's socket
* @buf: buffer to write from
* @size: actual size to write
* @flags: socket's flags
*/
static inline int
iscsi_send(struct iscsi_conn *conn, struct iscsi_buf *buf, int size, int flags)
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct socket *sk = tcp_conn->sock;
int offset = buf->sg.offset + buf->sent, res;
/*
* if we got use_sg=0 or are sending something we kmallocd
* then we did not have to do kmap (kmap returns page_address)
*
* if we got use_sg > 0, but had to drop down, we do not
* set clustering so this should only happen for that
* slab case.
*/
if (buf->use_sendmsg)
res = sock_no_sendpage(sk, buf->sg.page, offset, size, flags);
res = tcp_conn->sendpage(sk, buf->sg.page, offset, size, 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;
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
}
/**
* iscsi_sendhdr - send PDU Header via tcp_sendpage()
* @conn: iscsi connection
* @buf: buffer to write from
* @datalen: lenght of data to be sent after the header
*
* Notes:
* (Tx, Fast Path)
**/
static inline int
iscsi_sendhdr(struct iscsi_conn *conn, struct iscsi_buf *buf, int datalen)
{
int flags = 0; /* MSG_DONTWAIT; */
int res, size;
size = buf->sg.length - buf->sent;
BUG_ON(buf->sent + size > buf->sg.length);
if (buf->sent + size != buf->sg.length || datalen)
flags |= MSG_MORE;
res = iscsi_send(conn, buf, size, flags);
debug_tcp("sendhdr %d bytes, sent %d res %d\n", size, buf->sent, res);
if (res >= 0) {
if (size != res)
return -EAGAIN;
return 0;
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
return res;
}
/**
* iscsi_sendpage - send one page of iSCSI Data-Out.
* @conn: iscsi connection
* @buf: buffer to write from
* @count: remaining data
* @sent: number of bytes sent
*
* Notes:
* (Tx, Fast Path)
**/
static inline int
iscsi_sendpage(struct iscsi_conn *conn, struct iscsi_buf *buf,
int *count, int *sent)
{
int flags = 0; /* MSG_DONTWAIT; */
int res, size;
size = buf->sg.length - buf->sent;
BUG_ON(buf->sent + size > buf->sg.length);
if (size > *count)
size = *count;
if (buf->sent + size != buf->sg.length || *count != size)
flags |= MSG_MORE;
res = iscsi_send(conn, buf, size, flags);
debug_tcp("sendpage: %d bytes, sent %d left %d sent %d res %d\n",
size, buf->sent, *count, *sent, res);
if (res >= 0) {
*count -= res;
*sent += res;
if (size != res)
return -EAGAIN;
return 0;
return res;
}
static inline void
iscsi_data_digest_init(struct iscsi_tcp_conn *tcp_conn,
struct iscsi_tcp_cmd_task *tcp_ctask)
crypto_hash_init(&tcp_conn->tx_hash);
tcp_ctask->digest_count = 4;
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
}
/**
* iscsi_solicit_data_cont - initialize next Data-Out
* @conn: iscsi connection
* @ctask: scsi command task
* @r2t: R2T info
* @left: bytes left to transfer
*
* Notes:
* Initialize next Data-Out within this R2T sequence and continue
* to process next Scatter-Gather element(if any) of this SCSI command.
*
* Called under connection lock.
**/
static void
iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
struct iscsi_r2t_info *r2t, int left)
{
struct iscsi_data *hdr;
struct scsi_cmnd *sc = ctask->sc;
int new_offset;
memset(hdr, 0, sizeof(struct iscsi_data));
hdr->ttt = r2t->ttt;
hdr->datasn = cpu_to_be32(r2t->solicit_datasn);
r2t->solicit_datasn++;
hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun));
hdr->itt = ctask->hdr->itt;
hdr->exp_statsn = r2t->exp_statsn;
new_offset = r2t->data_offset + r2t->sent;
hdr->offset = cpu_to_be32(new_offset);
if (left > conn->max_xmit_dlength) {
hton24(hdr->dlength, conn->max_xmit_dlength);
r2t->data_count = conn->max_xmit_dlength;
} else {
hton24(hdr->dlength, left);
r2t->data_count = left;
hdr->flags = ISCSI_FLAG_CMD_FINAL;
}
conn->dataout_pdus_cnt++;
iscsi_buf_init_iov(&r2t->headbuf, (char*)hdr,
if (iscsi_buf_left(&r2t->sendbuf))
return;
if (sc->use_sg) {
iscsi_buf_init_sg(&r2t->sendbuf, r2t->sg);
r2t->sg += 1;
} else {
iscsi_buf_init_iov(&r2t->sendbuf,
(char*)sc->request_buffer + new_offset,
r2t->data_count);
r2t->sg = NULL;
}
static void iscsi_set_padding(struct iscsi_tcp_cmd_task *tcp_ctask,
unsigned long len)
tcp_ctask->pad_count = len & (ISCSI_PAD_LEN - 1);
if (!tcp_ctask->pad_count)
return;
tcp_ctask->pad_count = ISCSI_PAD_LEN - tcp_ctask->pad_count;
debug_scsi("write padding %d bytes\n", tcp_ctask->pad_count);
tcp_ctask->xmstate |= XMSTATE_W_PAD;
}
/**
* iscsi_tcp_cmd_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
* @conn: iscsi connection
* @ctask: scsi command task
* @sc: scsi command
**/
static void
iscsi_tcp_cmd_init(struct iscsi_cmd_task *ctask)
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
BUG_ON(__kfifo_len(tcp_ctask->r2tqueue));
tcp_ctask->xmstate = XMSTATE_CMD_HDR_INIT;
}
/**
* iscsi_tcp_mtask_xmit - xmit management(immediate) task
* @conn: iscsi connection
* @mtask: task management task
*
* Notes:
* The function can return -EAGAIN in which case caller must
* call it again later, or recover. '0' return code means successful
* xmit.
*
* Management xmit state machine consists of these states:
* XMSTATE_IMM_HDR_INIT - calculate digest of PDU Header
* XMSTATE_IMM_HDR - PDU Header xmit in progress
* XMSTATE_IMM_DATA - PDU Data xmit in progress
* XMSTATE_IDLE - management PDU is done
**/
static int
iscsi_tcp_mtask_xmit(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data;
debug_scsi("mtask deq [cid %d state %x itt 0x%x]\n",
conn->id, tcp_mtask->xmstate, mtask->itt);
if (tcp_mtask->xmstate & XMSTATE_IMM_HDR_INIT) {
iscsi_buf_init_iov(&tcp_mtask->headbuf, (char*)mtask->hdr,
sizeof(struct iscsi_hdr));
if (mtask->data_count) {
tcp_mtask->xmstate |= XMSTATE_IMM_DATA;
iscsi_buf_init_iov(&tcp_mtask->sendbuf,
(char*)mtask->data,
mtask->data_count);
}
if (conn->c_stage != ISCSI_CONN_INITIAL_STAGE &&
iscsi_hdr_digest(conn, &tcp_mtask->headbuf,
(u8*)tcp_mtask->hdrext);
tcp_mtask->sent = 0;
tcp_mtask->xmstate &= ~XMSTATE_IMM_HDR_INIT;
tcp_mtask->xmstate |= XMSTATE_IMM_HDR;
}
if (tcp_mtask->xmstate & XMSTATE_IMM_HDR) {
rc = iscsi_sendhdr(conn, &tcp_mtask->headbuf,
mtask->data_count);
tcp_mtask->xmstate &= ~XMSTATE_IMM_HDR;
if (tcp_mtask->xmstate & XMSTATE_IMM_DATA) {
BUG_ON(!mtask->data_count);
tcp_mtask->xmstate &= ~XMSTATE_IMM_DATA;
/* FIXME: implement.
* Virtual buffer could be spreaded across multiple pages...
*/
do {
int rc;
rc = iscsi_sendpage(conn, &tcp_mtask->sendbuf,
&mtask->data_count, &tcp_mtask->sent);
if (rc) {
tcp_mtask->xmstate |= XMSTATE_IMM_DATA;
}
} while (mtask->data_count);
}
BUG_ON(tcp_mtask->xmstate != XMSTATE_IDLE);
struct iscsi_session *session = conn->session;
spin_lock_bh(&session->lock);
list_del(&conn->mtask->running);
__kfifo_put(session->mgmtpool.queue, (void*)&conn->mtask,
sizeof(void*));
spin_unlock_bh(&session->lock);
}
return 0;
}
static int
iscsi_send_cmd_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
struct scsi_cmnd *sc = ctask->sc;
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
int rc = 0;
if (tcp_ctask->xmstate & XMSTATE_CMD_HDR_INIT) {
tcp_ctask->sent = 0;
tcp_ctask->sg_count = 0;
tcp_ctask->exp_datasn = 0;
if (sc->sc_data_direction == DMA_TO_DEVICE) {
if (sc->use_sg) {
struct scatterlist *sg = sc->request_buffer;
iscsi_buf_init_sg(&tcp_ctask->sendbuf, sg);
tcp_ctask->sg = sg + 1;
tcp_ctask->bad_sg = sg + sc->use_sg;
} else {
iscsi_buf_init_iov(&tcp_ctask->sendbuf,
sc->request_buffer,
sc->request_bufflen);
tcp_ctask->sg = NULL;
tcp_ctask->bad_sg = NULL;
}
debug_scsi("cmd [itt 0x%x total %d imm_data %d "
"unsol count %d, unsol offset %d]\n",
ctask->itt, sc->request_bufflen,
ctask->imm_count, ctask->unsol_count,
ctask->unsol_offset);
}
iscsi_buf_init_iov(&tcp_ctask->headbuf, (char*)ctask->hdr,
sizeof(struct iscsi_hdr));
if (conn->hdrdgst_en)
iscsi_hdr_digest(conn, &tcp_ctask->headbuf,
(u8*)tcp_ctask->hdrext);
tcp_ctask->xmstate &= ~XMSTATE_CMD_HDR_INIT;
tcp_ctask->xmstate |= XMSTATE_CMD_HDR_XMIT;
}
if (tcp_ctask->xmstate & XMSTATE_CMD_HDR_XMIT) {
rc = iscsi_sendhdr(conn, &tcp_ctask->headbuf, ctask->imm_count);
if (rc)
return rc;
tcp_ctask->xmstate &= ~XMSTATE_CMD_HDR_XMIT;
if (sc->sc_data_direction != DMA_TO_DEVICE)
return 0;
if (ctask->imm_count) {
tcp_ctask->xmstate |= XMSTATE_IMM_DATA;
iscsi_set_padding(tcp_ctask, ctask->imm_count);
if (ctask->conn->datadgst_en) {
iscsi_data_digest_init(ctask->conn->dd_data,
tcp_ctask);
tcp_ctask->immdigest = 0;
}
}
if (ctask->unsol_count)
tcp_ctask->xmstate |=
XMSTATE_UNS_HDR | XMSTATE_UNS_INIT;
}
return rc;
static int
iscsi_send_padding(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
int sent = 0, rc;
if (tcp_ctask->xmstate & XMSTATE_W_PAD) {
iscsi_buf_init_iov(&tcp_ctask->sendbuf, (char*)&tcp_ctask->pad,
tcp_ctask->pad_count);
if (conn->datadgst_en)
crypto_hash_update(&tcp_conn->tx_hash,
&tcp_ctask->sendbuf.sg,
tcp_ctask->sendbuf.sg.length);
} else if (!(tcp_ctask->xmstate & XMSTATE_W_RESEND_PAD))
return 0;
tcp_ctask->xmstate &= ~XMSTATE_W_PAD;
tcp_ctask->xmstate &= ~XMSTATE_W_RESEND_PAD;
debug_scsi("sending %d pad bytes for itt 0x%x\n",
tcp_ctask->pad_count, ctask->itt);
rc = iscsi_sendpage(conn, &tcp_ctask->sendbuf, &tcp_ctask->pad_count,
&sent);
debug_scsi("padding send failed %d\n", rc);
tcp_ctask->xmstate |= XMSTATE_W_RESEND_PAD;
static int
iscsi_send_digest(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
struct iscsi_buf *buf, uint32_t *digest)
struct iscsi_tcp_cmd_task *tcp_ctask;
struct iscsi_tcp_conn *tcp_conn;
int rc, sent = 0;
if (!conn->datadgst_en)
return 0;
tcp_ctask = ctask->dd_data;
tcp_conn = conn->dd_data;
if (!(tcp_ctask->xmstate & XMSTATE_W_RESEND_DATA_DIGEST)) {
crypto_hash_final(&tcp_conn->tx_hash, (u8*)digest);
iscsi_buf_init_iov(buf, (char*)digest, 4);
}
tcp_ctask->xmstate &= ~XMSTATE_W_RESEND_DATA_DIGEST;
rc = iscsi_sendpage(conn, buf, &tcp_ctask->digest_count, &sent);
if (!rc)
debug_scsi("sent digest 0x%x for itt 0x%x\n", *digest,
ctask->itt);
else {
debug_scsi("sending digest 0x%x failed for itt 0x%x!\n",
*digest, ctask->itt);
tcp_ctask->xmstate |= XMSTATE_W_RESEND_DATA_DIGEST;
return rc;
}
static int
iscsi_send_data(struct iscsi_cmd_task *ctask, struct iscsi_buf *sendbuf,
struct scatterlist **sg, int *sent, int *count,
struct iscsi_buf *digestbuf, uint32_t *digest)
{
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_conn *conn = ctask->conn;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
int rc, buf_sent, offset;
while (*count) {
buf_sent = 0;
offset = sendbuf->sent;
rc = iscsi_sendpage(conn, sendbuf, count, &buf_sent);
*sent = *sent + buf_sent;
if (buf_sent && conn->datadgst_en)
partial_sg_digest_update(&tcp_conn->tx_hash,
&sendbuf->sg, sendbuf->sg.offset + offset,
buf_sent);
if (!iscsi_buf_left(sendbuf) && *sg != tcp_ctask->bad_sg) {
iscsi_buf_init_sg(sendbuf, *sg);
*sg = *sg + 1;
if (rc)
return rc;
rc = iscsi_send_padding(conn, ctask);
if (rc)
return rc;
return iscsi_send_digest(conn, ctask, digestbuf, digest);
static int
iscsi_send_unsol_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_data_task *dtask;
tcp_ctask->xmstate |= XMSTATE_UNS_DATA;
if (tcp_ctask->xmstate & XMSTATE_UNS_INIT) {
dtask = &tcp_ctask->unsol_dtask;
iscsi_prep_unsolicit_data_pdu(ctask, &dtask->hdr);
iscsi_buf_init_iov(&tcp_ctask->headbuf, (char*)&dtask->hdr,
sizeof(struct iscsi_hdr));
iscsi_hdr_digest(conn, &tcp_ctask->headbuf,
tcp_ctask->xmstate &= ~XMSTATE_UNS_INIT;
iscsi_set_padding(tcp_ctask, 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_HDR;
if (conn->datadgst_en) {
dtask = &tcp_ctask->unsol_dtask;
iscsi_data_digest_init(ctask->conn->dd_data, tcp_ctask);
dtask->digest = 0;
}
debug_scsi("uns dout [itt 0x%x dlen %d sent %d]\n",
ctask->itt, ctask->unsol_count, tcp_ctask->sent);
return 0;
}
static int
iscsi_send_unsol_pdu(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
if (tcp_ctask->xmstate & XMSTATE_UNS_HDR) {
BUG_ON(!ctask->unsol_count);
tcp_ctask->xmstate &= ~XMSTATE_UNS_HDR;
send_hdr:
rc = iscsi_send_unsol_hdr(conn, ctask);
if (rc)
return rc;
if (tcp_ctask->xmstate & XMSTATE_UNS_DATA) {
struct iscsi_data_task *dtask = &tcp_ctask->unsol_dtask;
int start = tcp_ctask->sent;
rc = iscsi_send_data(ctask, &tcp_ctask->sendbuf, &tcp_ctask->sg,
&tcp_ctask->sent, &ctask->data_count,
&dtask->digestbuf, &dtask->digest);
ctask->unsol_count -= tcp_ctask->sent - start;
if (rc)
return rc;
tcp_ctask->xmstate &= ~XMSTATE_UNS_DATA;
* Done with the Data-Out. Next, check if we need
* to send another unsolicited Data-Out.
if (ctask->unsol_count) {
debug_scsi("sending more uns\n");
tcp_ctask->xmstate |= XMSTATE_UNS_INIT;
goto send_hdr;
}
}
return 0;
}
static int iscsi_send_sol_pdu(struct iscsi_conn *conn,
struct iscsi_cmd_task *ctask)
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_session *session = conn->session;
struct iscsi_r2t_info *r2t;
struct iscsi_data_task *dtask;
if (tcp_ctask->xmstate & XMSTATE_SOL_HDR_INIT) {
if (!tcp_ctask->r2t) {
spin_lock_bh(&session->lock);
__kfifo_get(tcp_ctask->r2tqueue, (void*)&tcp_ctask->r2t,
sizeof(void*));
spin_unlock_bh(&session->lock);
}
send_hdr:
r2t = tcp_ctask->r2t;
dtask = &r2t->dtask;
if (conn->hdrdgst_en)
iscsi_hdr_digest(conn, &r2t->headbuf,
(u8*)dtask->hdrext);
tcp_ctask->xmstate &= ~XMSTATE_SOL_HDR_INIT;
tcp_ctask->xmstate |= XMSTATE_SOL_HDR;
}
if (tcp_ctask->xmstate & XMSTATE_SOL_HDR) {
r2t = tcp_ctask->r2t;
dtask = &r2t->dtask;
rc = iscsi_sendhdr(conn, &r2t->headbuf, r2t->data_count);
tcp_ctask->xmstate &= ~XMSTATE_SOL_HDR;
tcp_ctask->xmstate |= XMSTATE_SOL_DATA;
if (conn->datadgst_en) {
iscsi_data_digest_init(conn->dd_data, tcp_ctask);
dtask->digest = 0;
iscsi_set_padding(tcp_ctask, r2t->data_count);
debug_scsi("sol dout [dsn %d itt 0x%x dlen %d sent %d]\n",
r2t->solicit_datasn - 1, ctask->itt, r2t->data_count,
r2t->sent);
if (tcp_ctask->xmstate & XMSTATE_SOL_DATA) {
r2t = tcp_ctask->r2t;
dtask = &r2t->dtask;
rc = iscsi_send_data(ctask, &r2t->sendbuf, &r2t->sg,
&r2t->sent, &r2t->data_count,
&dtask->digestbuf, &dtask->digest);
if (rc)
return rc;
tcp_ctask->xmstate &= ~XMSTATE_SOL_DATA;
/*
* Done with this Data-Out. Next, check if we have
* to send another Data-Out for this R2T.
*/
BUG_ON(r2t->data_length - r2t->sent < 0);
left = r2t->data_length - r2t->sent;
if (left) {
iscsi_solicit_data_cont(conn, ctask, r2t, left);
goto send_hdr;
}
/*
* Done with this R2T. Check if there are more
* outstanding R2Ts ready to be processed.
*/
spin_lock_bh(&session->lock);
tcp_ctask->r2t = NULL;
__kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
sizeof(void*));
if (__kfifo_get(tcp_ctask->r2tqueue, (void*)&r2t,
sizeof(void*))) {
tcp_ctask->r2t = r2t;
spin_unlock_bh(&session->lock);
goto send_hdr;
spin_unlock_bh(&session->lock);
}
return 0;
}
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
/**
* iscsi_tcp_ctask_xmit - xmit normal PDU task
* @conn: iscsi connection
* @ctask: iscsi command task
*
* Notes:
* The function can return -EAGAIN in which case caller must
* call it again later, or recover. '0' return code means successful
* xmit.
* The function is devided to logical helpers (above) for the different
* xmit stages.
*
*iscsi_send_cmd_hdr()
* XMSTATE_CMD_HDR_INIT - prepare Header and Data buffers Calculate
* Header Digest
* XMSTATE_CMD_HDR_XMIT - Transmit header in progress
*
*iscsi_send_padding
* XMSTATE_W_PAD - Prepare and send pading
* XMSTATE_W_RESEND_PAD - retry send pading
*
*iscsi_send_digest
* XMSTATE_W_RESEND_DATA_DIGEST - Finalize and send Data Digest
* XMSTATE_W_RESEND_DATA_DIGEST - retry sending digest
*
*iscsi_send_unsol_hdr
* XMSTATE_UNS_INIT - prepare un-solicit data header and digest
* XMSTATE_UNS_HDR - send un-solicit header
*
*iscsi_send_unsol_pdu
* XMSTATE_UNS_DATA - send un-solicit data in progress
*
*iscsi_send_sol_pdu
* XMSTATE_SOL_HDR_INIT - solicit data header and digest initialize
* XMSTATE_SOL_HDR - send solicit header
* XMSTATE_SOL_DATA - send solicit data
*
*iscsi_tcp_ctask_xmit
* XMSTATE_IMM_DATA - xmit managment data (??)
**/
iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
int rc = 0;
debug_scsi("ctask deq [cid %d xmstate %x itt 0x%x]\n",
conn->id, tcp_ctask->xmstate, ctask->itt);
/*
* serialize with TMF AbortTask
*/
if (ctask->mtask)
return rc;
rc = iscsi_send_cmd_hdr(conn, ctask);
if (rc)
return rc;
if (ctask->sc->sc_data_direction != DMA_TO_DEVICE)
return 0;
if (tcp_ctask->xmstate & XMSTATE_IMM_DATA) {
rc = iscsi_send_data(ctask, &tcp_ctask->sendbuf, &tcp_ctask->sg,
&tcp_ctask->sent, &ctask->imm_count,
&tcp_ctask->immbuf, &tcp_ctask->immdigest);
if (rc)
return rc;
tcp_ctask->xmstate &= ~XMSTATE_IMM_DATA;
rc = iscsi_send_unsol_pdu(conn, ctask);
if (rc)
return rc;
rc = iscsi_send_sol_pdu(conn, ctask);
if (rc)
return rc;
return rc;
}
static struct iscsi_cls_conn *
iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
struct iscsi_conn *conn;
struct iscsi_cls_conn *cls_conn;
struct iscsi_tcp_conn *tcp_conn;
cls_conn = iscsi_conn_setup(cls_session, conn_idx);
if (!cls_conn)
return NULL;
conn = cls_conn->dd_data;
* due to strange issues with iser these are not set
* in iscsi_conn_setup
conn->max_recv_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN;
tcp_conn = kzalloc(sizeof(*tcp_conn), GFP_KERNEL);
if (!tcp_conn)
goto tcp_conn_alloc_fail;
conn->dd_data = tcp_conn;
tcp_conn->iscsi_conn = conn;
tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
/* initial operational parameters */
tcp_conn->hdr_size = sizeof(struct iscsi_hdr);
tcp_conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0,
CRYPTO_ALG_ASYNC);
tcp_conn->tx_hash.flags = 0;

Mike Christie
committed
if (IS_ERR(tcp_conn->tx_hash.tfm)) {
printk(KERN_ERR "Could not create connection due to crc32c "
"loading error %ld. Make sure the crc32c module is "
"built as a module or into the kernel\n",
PTR_ERR(tcp_conn->tx_hash.tfm));

Mike Christie
committed
}
tcp_conn->rx_hash.tfm = crypto_alloc_hash("crc32c", 0,
CRYPTO_ALG_ASYNC);
tcp_conn->rx_hash.flags = 0;

Mike Christie
committed
if (IS_ERR(tcp_conn->rx_hash.tfm)) {
printk(KERN_ERR "Could not create connection due to crc32c "
"loading error %ld. Make sure the crc32c module is "
"built as a module or into the kernel\n",
PTR_ERR(tcp_conn->rx_hash.tfm));

Mike Christie
committed
}
crypto_free_hash(tcp_conn->tx_hash.tfm);
free_tcp_conn:
kfree(tcp_conn);
tcp_conn_alloc_fail:
iscsi_conn_teardown(cls_conn);
return NULL;
static void
iscsi_tcp_release_conn(struct iscsi_conn *conn)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
if (!tcp_conn->sock)
return;
sock_hold(tcp_conn->sock->sk);
iscsi_conn_restore_callbacks(tcp_conn);
sock_put(tcp_conn->sock->sk);
sock_release(tcp_conn->sock);
tcp_conn->sock = NULL;
conn->recv_lock = NULL;
}
iscsi_tcp_conn_destroy(struct iscsi_cls_conn *cls_conn)
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
iscsi_tcp_release_conn(conn);
iscsi_conn_teardown(cls_conn);
if (tcp_conn->tx_hash.tfm)
crypto_free_hash(tcp_conn->tx_hash.tfm);
if (tcp_conn->rx_hash.tfm)
crypto_free_hash(tcp_conn->rx_hash.tfm);
static void
iscsi_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
iscsi_conn_stop(cls_conn, flag);
iscsi_tcp_release_conn(conn);
tcp_conn->hdr_size = sizeof(struct iscsi_hdr);
static int
iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
struct iscsi_cls_conn *cls_conn, uint64_t transport_eph,
int is_leading)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct sock *sk;
struct socket *sock;
int err;
/* lookup for existing socket */
sock = sockfd_lookup((int)transport_eph, &err);
if (!sock) {
printk(KERN_ERR "iscsi_tcp: sockfd_lookup failed %d\n", err);
return -EEXIST;
err = iscsi_conn_bind(cls_session, cls_conn, is_leading);
if (err)
return err;
/* bind iSCSI connection and socket */
tcp_conn->sock = sock;
/* setup Socket parameters */
sk = sock->sk;
sk->sk_reuse = 1;
sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */
sk->sk_allocation = GFP_ATOMIC;
/* FIXME: disable Nagle's algorithm */
/*
* Intercept TCP callbacks for sendfile like receive
* processing.
*/
conn->recv_lock = &sk->sk_callback_lock;
iscsi_conn_set_callbacks(conn);
tcp_conn->sendpage = tcp_conn->sock->ops->sendpage;
/*
* set receive state machine into initial state
*/
tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
return 0;
}
/* called with host lock */
iscsi_tcp_mgmt_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask,
char *data, uint32_t data_size)
struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data;
tcp_mtask->xmstate = XMSTATE_IMM_HDR_INIT;
}
static int
iscsi_r2tpool_alloc(struct iscsi_session *session)
{
int i;
int cmd_i;
/*
* initialize per-task: R2T pool and xmit queue
*/
for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
/*
* pre-allocated x4 as much r2ts to handle race when
* target acks DataOut faster than we data_xmit() queues
* could replenish r2tqueue.
*/
/* R2T pool */
if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4,
(void***)&tcp_ctask->r2ts,
sizeof(struct iscsi_r2t_info))) {
goto r2t_alloc_fail;
}
/* R2T xmit queue */
tcp_ctask->r2tqueue = kfifo_alloc(
session->max_r2t * 4 * sizeof(void*), GFP_KERNEL, NULL);
if (tcp_ctask->r2tqueue == ERR_PTR(-ENOMEM)) {
iscsi_pool_free(&tcp_ctask->r2tpool,
(void**)tcp_ctask->r2ts);
goto r2t_alloc_fail;
}
}
return 0;
r2t_alloc_fail:
for (i = 0; i < cmd_i; i++) {
struct iscsi_cmd_task *ctask = session->cmds[i];
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;