#include <linux/delay.h>
#include <linux/kfifo.h>
#include <linux/scatterlist.h>
+#include <linux/mutex.h>
#include <net/tcp.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
"Alex Aizman <itn780@yahoo.com>");
MODULE_DESCRIPTION("iSCSI/TCP data-path");
MODULE_LICENSE("GPL");
-
+MODULE_VERSION("0:4.445");
/* #define DEBUG_TCP */
/* #define DEBUG_SCSI */
#define DEBUG_ASSERT
crypto_digest_digest(conn->rx_tfm, &sg, 1, (u8 *)&cdgst);
rdgst = *(uint32_t*)((char*)hdr + sizeof(struct iscsi_hdr) +
conn->in.ahslen);
+ if (cdgst != rdgst) {
+ printk(KERN_ERR "iscsi_tcp: itt %x: hdrdgst error "
+ "recv 0x%x calc 0x%x\n", conn->in.itt, rdgst,
+ cdgst);
+ return ISCSI_ERR_HDR_DGST;
+ }
}
/* save opcode for later */
- conn->in.opcode = hdr->opcode;
+ conn->in.opcode = hdr->opcode & ISCSI_OPCODE_MASK;
/* verify itt (itt encoding: age+cid+itt) */
if (hdr->itt != cpu_to_be32(ISCSI_RESERVED_TAG)) {
conn->in.ahslen, conn->in.datalen);
if (conn->in.itt < session->cmds_max) {
- if (conn->hdrdgst_en && cdgst != rdgst) {
- printk(KERN_ERR "iscsi_tcp: itt %x: hdrdgst error "
- "recv 0x%x calc 0x%x\n", conn->in.itt, rdgst,
- cdgst);
- return ISCSI_ERR_HDR_DGST;
- }
-
ctask = (struct iscsi_cmd_task *)session->cmds[conn->in.itt];
if (!ctask->sc) {
switch(conn->in.opcode) {
case ISCSI_OP_SCSI_CMD_RSP:
BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
- if (ctask->hdr.flags & ISCSI_FLAG_CMD_WRITE)
- rc = iscsi_cmd_rsp(conn, ctask);
- else if (!conn->in.datalen)
+ if (!conn->in.datalen)
rc = iscsi_cmd_rsp(conn, ctask);
else
/*
break;
case ISCSI_OP_R2T:
BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
- if (ctask->hdr.flags & ISCSI_FLAG_CMD_WRITE &&
- ctask->sc->sc_data_direction == DMA_TO_DEVICE)
+ if (ctask->sc->sc_data_direction == DMA_TO_DEVICE)
rc = iscsi_r2t_rsp(conn, ctask);
else
rc = ISCSI_ERR_PROTO;
break;
- case ISCSI_OP_NOOP_IN:
- case ISCSI_OP_TEXT_RSP:
- case ISCSI_OP_LOGOUT_RSP:
- case ISCSI_OP_ASYNC_EVENT:
- case ISCSI_OP_REJECT:
- rc = iscsi_check_assign_cmdsn(session,
- (struct iscsi_nopin*)hdr);
- if (rc)
- break;
-
- /* update ExpStatSN */
- conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
- if (!conn->in.datalen) {
- struct iscsi_mgmt_task *mtask;
-
- rc = iscsi_recv_pdu(iscsi_handle(conn), hdr,
- NULL, 0);
- mtask = (struct iscsi_mgmt_task *)
- session->mgmt_cmds[conn->in.itt -
- ISCSI_MGMT_ITT_OFFSET];
- if (conn->login_mtask != mtask) {
- spin_lock(&session->lock);
- __kfifo_put(session->mgmtpool.queue,
- (void*)&mtask, sizeof(void*));
- spin_unlock(&session->lock);
- }
- }
- break;
default:
rc = ISCSI_ERR_BAD_OPCODE;
break;
switch(conn->in.opcode) {
case ISCSI_OP_LOGIN_RSP:
case ISCSI_OP_TEXT_RSP:
+ case ISCSI_OP_LOGOUT_RSP:
rc = iscsi_check_assign_cmdsn(session,
(struct iscsi_nopin*)hdr);
if (rc)
}
spin_unlock(&session->lock);
break;
+ case ISCSI_OP_NOOP_IN:
+ if (hdr->ttt != ISCSI_RESERVED_TAG) {
+ rc = ISCSI_ERR_PROTO;
+ break;
+ }
+ rc = iscsi_check_assign_cmdsn(session,
+ (struct iscsi_nopin*)hdr);
+ if (rc)
+ break;
+ conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
+
+ if (!conn->in.datalen) {
+ struct iscsi_mgmt_task *mtask;
+
+ rc = iscsi_recv_pdu(iscsi_handle(conn), hdr,
+ NULL, 0);
+ mtask = (struct iscsi_mgmt_task *)
+ session->mgmt_cmds[conn->in.itt -
+ ISCSI_MGMT_ITT_OFFSET];
+ if (conn->login_mtask != mtask) {
+ spin_lock(&session->lock);
+ __kfifo_put(session->mgmtpool.queue,
+ (void*)&mtask, sizeof(void*));
+ spin_unlock(&session->lock);
+ }
+ }
+ break;
default:
rc = ISCSI_ERR_BAD_OPCODE;
break;
}
} else if (conn->in.itt == ISCSI_RESERVED_TAG) {
- if (conn->in.opcode == ISCSI_OP_NOOP_IN && !conn->in.datalen) {
- rc = iscsi_check_assign_cmdsn(session,
+ switch(conn->in.opcode) {
+ case ISCSI_OP_NOOP_IN:
+ if (!conn->in.datalen) {
+ rc = iscsi_check_assign_cmdsn(session,
(struct iscsi_nopin*)hdr);
- if (!rc)
- rc = iscsi_recv_pdu(iscsi_handle(conn),
- hdr, NULL, 0);
- }
- else
+ if (!rc && hdr->ttt != ISCSI_RESERVED_TAG)
+ rc = iscsi_recv_pdu(iscsi_handle(conn),
+ hdr, NULL, 0);
+ } else
+ rc = ISCSI_ERR_PROTO;
+ break;
+ case ISCSI_OP_REJECT:
+ /* we need sth like iscsi_reject_rsp()*/
+ case ISCSI_OP_ASYNC_EVENT:
+ /* we need sth like iscsi_async_event_rsp() */
+ rc = ISCSI_ERR_BAD_OPCODE;
+ break;
+ default:
rc = ISCSI_ERR_BAD_OPCODE;
+ break;
+ }
} else
rc = ISCSI_ERR_BAD_ITT;
crypto_digest_update(conn->data_rx_tfm, &temp, 1);
}
+static void
+iscsi_recv_digest_update(struct iscsi_conn *conn, char* buf, int len)
+{
+ struct scatterlist tmp;
+
+ sg_init_one(&tmp, buf, len);
+ crypto_digest_update(conn->data_rx_tfm, &tmp, 1);
+}
+
static int iscsi_scsi_data_in(struct iscsi_conn *conn)
{
struct iscsi_cmd_task *ctask = conn->in.ctask;
struct scsi_cmnd *sc = ctask->sc;
- struct scatterlist tmp, *sg;
+ struct scatterlist *sg;
int i, offset, rc = 0;
BUG_ON((void*)ctask != sc->SCp.ptr);
sc->request_bufflen, ctask->data_offset);
if (rc == -EAGAIN)
return rc;
- if (conn->datadgst_en) {
- sg_init_one(&tmp, sc->request_buffer, i);
- crypto_digest_update(conn->data_rx_tfm, &tmp, 1);
- }
+ if (conn->datadgst_en)
+ iscsi_recv_digest_update(conn, sc->request_buffer, i);
rc = 0;
goto done;
}
conn->in.hdr = &conn->hdr;
conn->senselen = (conn->data[0] << 8) | conn->data[1];
rc = iscsi_cmd_rsp(conn, conn->in.ctask);
+ if (!rc && conn->datadgst_en)
+ iscsi_recv_digest_update(conn, conn->data,
+ conn->in.datalen);
}
break;
case ISCSI_OP_TEXT_RSP:
rc = iscsi_recv_pdu(iscsi_handle(conn), conn->in.hdr,
conn->data, conn->in.datalen);
+ if (!rc && conn->datadgst_en &&
+ conn->in.opcode != ISCSI_OP_LOGIN_RSP)
+ iscsi_recv_digest_update(conn, conn->data,
+ conn->in.datalen);
+
if (mtask && conn->login_mtask != mtask) {
spin_lock(&session->lock);
__kfifo_put(session->mgmtpool.queue, (void*)&mtask,
}
}
break;
+ case ISCSI_OP_ASYNC_EVENT:
+ case ISCSI_OP_REJECT:
default:
BUG_ON(1);
}
*/
rc = iscsi_hdr_recv(conn);
if (!rc && conn->in.datalen) {
- if (conn->datadgst_en &&
- conn->in.opcode == ISCSI_OP_SCSI_DATA_IN) {
+ if (conn->datadgst_en) {
BUG_ON(!conn->data_rx_tfm);
crypto_digest_init(conn->data_rx_tfm);
}
}
if (conn->in_progress == IN_PROGRESS_DDIGEST_RECV) {
+ uint32_t recv_digest;
debug_tcp("extra data_recv offset %d copy %d\n",
conn->in.offset, conn->in.copy);
- if (conn->in.opcode == ISCSI_OP_SCSI_DATA_IN) {
- uint32_t recv_digest;
- skb_copy_bits(conn->in.skb, conn->in.offset,
- &recv_digest, 4);
- conn->in.offset += 4;
- conn->in.copy -= 4;
- if (recv_digest != conn->in.datadgst) {
- debug_tcp("iscsi_tcp: data digest error!"
- "0x%x != 0x%x\n", recv_digest,
- conn->in.datadgst);
- iscsi_conn_failure(conn, ISCSI_ERR_DATA_DGST);
- return 0;
- } else {
- debug_tcp("iscsi_tcp: data digest match!"
- "0x%x == 0x%x\n", recv_digest,
- conn->in.datadgst);
- conn->in_progress = IN_PROGRESS_WAIT_HEADER;
- }
+ skb_copy_bits(conn->in.skb, conn->in.offset,
+ &recv_digest, 4);
+ conn->in.offset += 4;
+ conn->in.copy -= 4;
+ if (recv_digest != conn->in.datadgst) {
+ debug_tcp("iscsi_tcp: data digest error!"
+ "0x%x != 0x%x\n", recv_digest,
+ conn->in.datadgst);
+ iscsi_conn_failure(conn, ISCSI_ERR_DATA_DGST);
+ return 0;
+ } else {
+ debug_tcp("iscsi_tcp: data digest match!"
+ "0x%x == 0x%x\n", recv_digest,
+ conn->in.datadgst);
+ conn->in_progress = IN_PROGRESS_WAIT_HEADER;
}
}
}
conn->in.copy -= conn->in.padding;
conn->in.offset += conn->in.padding;
- if (conn->datadgst_en &&
- conn->in.opcode == ISCSI_OP_SCSI_DATA_IN) {
+ if (conn->datadgst_en) {
if (conn->in.padding) {
debug_tcp("padding -> %d\n", conn->in.padding);
memset(pad, 0, conn->in.padding);
conn = (struct iscsi_conn*)sk->sk_user_data;
session = conn->session;
- if (sk->sk_state == TCP_CLOSE_WAIT ||
- sk->sk_state == TCP_CLOSE) {
+ 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);
}
/*
* serialize Xmit worker on a per-connection basis.
*/
- down(&conn->xmitsema);
+ mutex_lock(&conn->xmitmutex);
if (iscsi_data_xmit(conn))
schedule_work(&conn->xmitwork);
- up(&conn->xmitsema);
+ mutex_unlock(&conn->xmitmutex);
}
#define FAILURE_BAD_HOST 1
session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
spin_unlock(&session->lock);
- if (!in_interrupt() && !down_trylock(&conn->xmitsema)) {
+ if (!in_interrupt() && mutex_trylock(&conn->xmitmutex)) {
spin_unlock_irq(host->host_lock);
if (iscsi_data_xmit(conn))
schedule_work(&conn->xmitwork);
- up(&conn->xmitsema);
+ mutex_unlock(&conn->xmitmutex);
spin_lock_irq(host->host_lock);
} else
schedule_work(&conn->xmitwork);
return 0;
}
+static int
+iscsi_change_queue_depth(struct scsi_device *sdev, int depth)
+{
+ if (depth > ISCSI_MAX_CMD_PER_LUN)
+ depth = ISCSI_MAX_CMD_PER_LUN;
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+ return sdev->queue_depth;
+}
+
static int
iscsi_pool_init(struct iscsi_queue *q, int max, void ***items, int item_size)
{
goto max_recv_dlenght_alloc_fail;
init_timer(&conn->tmabort_timer);
- init_MUTEX(&conn->xmitsema);
+ mutex_init(&conn->xmitmutex);
init_waitqueue_head(&conn->ehwait);
return iscsi_handle(conn);
struct iscsi_conn *conn = iscsi_ptr(connh);
struct iscsi_session *session = conn->session;
- down(&conn->xmitsema);
+ mutex_lock(&conn->xmitmutex);
set_bit(SUSPEND_BIT, &conn->suspend_tx);
if (conn->c_stage == ISCSI_CONN_INITIAL_STAGE && conn->sock) {
struct sock *sk = conn->sock->sk;
}
spin_unlock_bh(&session->lock);
- up(&conn->xmitsema);
+ mutex_unlock(&conn->xmitmutex);
/*
* Block until all in-progress commands for this connection
set_bit(SUSPEND_BIT, &conn->suspend_rx);
write_unlock_bh(&sk->sk_callback_lock);
- down(&conn->xmitsema);
+ mutex_lock(&conn->xmitmutex);
spin_lock_irqsave(session->host->host_lock, flags);
spin_lock(&session->lock);
* in hdr_extract() and will be re-negotiated at
* set_param() time.
*/
- if (flag == STOP_CONN_RECOVER)
+ if (flag == STOP_CONN_RECOVER) {
conn->hdr_size = sizeof(struct iscsi_hdr);
+ conn->hdrdgst_en = 0;
+ conn->datadgst_en = 0;
+ }
}
- up(&conn->xmitsema);
+ mutex_unlock(&conn->xmitmutex);
}
static int
* 1) connection-level failure;
* 2) recovery due protocol error;
*/
- down(&conn->xmitsema);
+ mutex_lock(&conn->xmitmutex);
spin_lock_bh(&session->lock);
if (session->state != ISCSI_STATE_LOGGED_IN) {
if (session->state == ISCSI_STATE_TERMINATE) {
spin_unlock_bh(&session->lock);
- up(&conn->xmitsema);
+ mutex_unlock(&conn->xmitmutex);
goto failed;
}
spin_unlock_bh(&session->lock);
* 2) session was re-open during time out of ctask.
*/
spin_unlock_bh(&session->lock);
- up(&conn->xmitsema);
+ mutex_unlock(&conn->xmitmutex);
goto success;
}
conn->tmabort_state = TMABORT_INITIAL;
conn->tmabort_state == TMABORT_SUCCESS) {
conn->tmabort_state = TMABORT_INITIAL;
spin_unlock_bh(&session->lock);
- up(&conn->xmitsema);
+ mutex_unlock(&conn->xmitmutex);
goto success;
}
conn->tmabort_state = TMABORT_INITIAL;
spin_unlock_bh(&session->lock);
}
}
- up(&conn->xmitsema);
+ mutex_unlock(&conn->xmitmutex);
/*
exit:
del_timer_sync(&conn->tmabort_timer);
- down(&conn->xmitsema);
+ mutex_lock(&conn->xmitmutex);
if (conn->sock) {
struct sock *sk = conn->sock->sk;
iscsi_ctask_cleanup(conn, ctask);
write_unlock_bh(&sk->sk_callback_lock);
}
- up(&conn->xmitsema);
+ mutex_unlock(&conn->xmitmutex);
return rc;
}
static struct scsi_host_template iscsi_sht = {
.name = "iSCSI Initiator over TCP/IP, v."
ISCSI_VERSION_STR,
- .queuecommand = iscsi_queuecommand,
+ .queuecommand = iscsi_queuecommand,
+ .change_queue_depth = iscsi_change_queue_depth,
.can_queue = ISCSI_XMIT_CMDS_MAX - 1,
.sg_tablesize = ISCSI_SG_TABLESIZE,
- .cmd_per_lun = ISCSI_CMD_PER_LUN,
- .eh_abort_handler = iscsi_eh_abort,
- .eh_host_reset_handler = iscsi_eh_host_reset,
- .use_clustering = DISABLE_CLUSTERING,
+ .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN,
+ .eh_abort_handler = iscsi_eh_abort,
+ .eh_host_reset_handler = iscsi_eh_host_reset,
+ .use_clustering = DISABLE_CLUSTERING,
.proc_name = "iscsi_tcp",
.this_id = -1,
};
switch(param) {
case ISCSI_PARAM_MAX_RECV_DLENGTH: {
char *saveptr = conn->data;
- int flags = GFP_KERNEL;
+ gfp_t flags = GFP_KERNEL;
if (conn->data_size >= value) {
conn->max_recv_dlength = value;
struct iscsi_conn *conn = iscsi_ptr(connh);
int rc;
- down(&conn->xmitsema);
+ mutex_lock(&conn->xmitmutex);
rc = iscsi_conn_send_generic(conn, hdr, data, data_size);
- up(&conn->xmitsema);
+ mutex_unlock(&conn->xmitmutex);
return rc;
}