[PATCH RFC 3/3] SCSI Userspace Target: example target driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is the core iscsi tcp target coide. It demonstates how a target LLD
can reuse a existing transport class (in this case we use the iscsi transport
class's interface) and hook into scsi-tgt. The interesting parts are the
scsi_host_template and iscsi_tranpsort templates.

This patch alone does not compile. I am just sending it as a way to show
how it all works. There is tons of cleanup needed before this is ready
for a review and the goal is to kill most of this code and either merge
the target PDU handling with iscsi_tcp.c in mainline already or move
parts of iscsi_tcp.c to the transport class where it can be shared.
Most likely a lot of both will happen.


diff -Naurp linux-2.6.16-rc1/drivers/scsi/iscsi_tcp_tgt.c linux-2.6.16-rc1.tmp/drivers/scsi/iscsi_tcp_tgt.c
--- linux-2.6.16-rc1/drivers/scsi/iscsi_tcp_tgt.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.16-rc1.tmp/drivers/scsi/iscsi_tcp_tgt.c	2006-01-24 20:25:06.000000000 -0600
@@ -0,0 +1,1941 @@
+/*
+ * (C) 2004 - 2005 FUJITA Tomonori <tomof@xxxxxxx>
+ * Copyright (C) 2002 - 2003 Ardis Technolgies <roman@xxxxxxxxxxxxx>
+ * Copyright (C) 2005 - 2006 Mike Christie
+ *
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <linux/module.h>
+#include <linux/hash.h>
+#include <linux/mempool.h>
+#include <net/tcp.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_tgt.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_iscsi.h>
+#include <iscsi.h>
+
+static kmem_cache_t *istgt_cmd_cache;
+static char dummy_data[1024];
+
+static uint32_t cmnd_write_size(struct istgt_cmd *cmnd)
+{
+	struct iscsi_cmd *hdr = cmd_hdr(cmnd);
+
+	if (hdr->flags & ISCSI_FLAG_CMD_WRITE)
+		return be32_to_cpu(hdr->data_length);
+	return 0;
+}
+
+static uint32_t cmnd_read_size(struct istgt_cmd *cmnd)
+{
+	struct iscsi_cmd *hdr = cmd_hdr(cmnd);
+
+	if (hdr->flags & ISCSI_FLAG_CMD_READ) {
+		if (!(hdr->flags & ISCSI_FLAG_CMD_WRITE))
+			return be32_to_cpu(hdr->data_length);
+		if (hdr->flags & ISCSI_FLAG_CMD_READ) {
+			struct iscsi_rlength_ahdr *ahdr =
+				(struct iscsi_rlength_ahdr *)cmnd->pdu.ahs;
+			if (ahdr && ahdr->ahstype == ISCSI_AHSTYPE_RLENGTH)
+				return be32_to_cpu(ahdr->read_length);
+		}
+	}
+	return 0;
+}
+
+/*
+ * create a new command.
+ *
+ * iscsi_cmnd_create - 
+ * @conn: ptr to connection (for i/o)
+ *
+ * @return    ptr to command or NULL
+ */
+
+struct istgt_cmd *cmnd_alloc(struct iscsi_conn *conn, int req)
+{
+	struct istgt_cmd *cmnd;
+
+	/* TODO: async interface is necessary ? */
+	cmnd = kmem_cache_alloc(istgt_cmd_cache, GFP_KERNEL | __GFP_NOFAIL);
+
+	memset(cmnd, 0, sizeof(*cmnd));
+	INIT_LIST_HEAD(&cmnd->list);
+	INIT_LIST_HEAD(&cmnd->pdu_list);
+	INIT_LIST_HEAD(&cmnd->conn_list);
+	INIT_LIST_HEAD(&cmnd->hash_list);
+	cmnd->conn = conn;
+	spin_lock(&conn->list_lock);
+	atomic_inc(&conn->nr_cmnds);
+	init_completion(&cmnd->event);
+	if (req)
+		list_add_tail(&cmnd->conn_list, &conn->pdu_list);
+	spin_unlock(&conn->list_lock);
+	cmnd->sg = NULL;
+
+	if (req)
+		BUG_ON(!conn->session);
+
+	dprintk("%p:%p\n", conn, cmnd);
+
+	return cmnd;
+}
+
+/**
+ * create a new command used as response.
+ *
+ * iscsi_cmnd_create_rsp_cmnd - 
+ * @cmnd: ptr to request command
+ *
+ * @return    ptr to response command or NULL
+ */
+
+static struct istgt_cmd *iscsi_cmnd_create_rsp_cmnd(struct istgt_cmd *cmnd, int final)
+{
+	struct istgt_cmd *rsp = cmnd_alloc(cmnd->conn, 0);
+
+	if (final)
+		set_cmd_final(rsp);
+	list_add_tail(&rsp->pdu_list, &cmnd->pdu_list);
+	rsp->req = cmnd;
+	return rsp;
+}
+
+static struct istgt_cmd *get_rsp_cmnd(struct istgt_cmd *req)
+{
+	return list_entry(req->pdu_list.prev, struct istgt_cmd, pdu_list);
+}
+
+static void iscsi_cmnds_init_write(struct list_head *send)
+{
+	struct istgt_cmd *cmnd = list_entry(send->next, struct istgt_cmd, list);
+	struct iscsi_conn *conn = cmnd->conn;
+	struct list_head *pos, *next;
+
+	spin_lock(&conn->list_lock);
+
+	list_for_each_safe(pos, next, send) {
+		cmnd = list_entry(pos, struct istgt_cmd, list);
+
+		dprintk("%p:%x\n", cmnd, cmd_opcode(cmnd));
+
+		list_del_init(&cmnd->list);
+		BUG_ON(conn != cmnd->conn);
+		list_add_tail(&cmnd->list, &conn->write_list);
+	}
+
+	spin_unlock(&conn->list_lock);
+
+	nthread_wakeup(conn->session);
+}
+
+static void iscsi_cmnd_init_write(struct istgt_cmd *cmnd)
+{
+	LIST_HEAD(head);
+
+	if (!list_empty(&cmnd->list)) {
+		eprintk("%x %x %x %x %lx %u %u %u %u %u %u %u %d %d\n",
+			cmd_itt(cmnd), cmd_ttt(cmnd), cmd_opcode(cmnd),
+			cmd_scsicode(cmnd), cmnd->flags,
+			cmnd->r2t_sn, cmnd->r2t_length, cmnd->is_unsolicited_data,
+			cmnd->target_task_tag, cmnd->outstanding_r2t,
+			cmnd->hdigest, cmnd->ddigest,
+			list_empty(&cmnd->pdu_list), list_empty(&cmnd->hash_list));
+
+		BUG_ON(!list_empty(&cmnd->list));
+	}
+	list_add(&cmnd->list, &head);
+	iscsi_cmnds_init_write(&head);
+}
+
+static void do_send_data_rsp(struct istgt_cmd *cmnd)
+{
+	struct iscsi_conn *conn = cmnd->conn;
+	struct istgt_cmd *data_cmnd;
+	struct scatterlist *sg = cmnd->scmd->request_buffer;
+	struct iscsi_cmd *req = cmd_hdr(cmnd);
+	struct iscsi_data_rsp *rsp;
+	uint32_t pdusize, expsize, scsisize, size, offset, sn;
+	LIST_HEAD(send);
+
+	dprintk("%p\n", cmnd);
+	pdusize = conn->session->param.max_xmit_data_length;
+	expsize = cmnd_read_size(cmnd);
+	size = min(expsize, cmnd->scmd->request_bufflen);
+	dprintk("%u %u\n", expsize, cmnd->scmd->request_bufflen);
+	offset = 0;
+	sn = 0;
+
+	BUG_ON(!sg);
+
+	while (1) {
+		data_cmnd = iscsi_cmnd_create_rsp_cmnd(cmnd, size <= pdusize);
+		data_cmnd->sg = sg;
+		rsp = (struct iscsi_data_rsp *)&data_cmnd->pdu.bhs;
+
+		rsp->opcode = ISCSI_OP_SCSI_DATA_IN;
+		rsp->itt = req->itt;
+		rsp->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
+		rsp->offset = offset;
+		rsp->datasn = cpu_to_be32(sn);
+
+		if (size <= pdusize) {
+			data_cmnd->pdu.datasize = size;
+			rsp->flags = ISCSI_FLAG_CMD_FINAL |
+				     ISCSI_FLAG_DATA_STATUS;
+
+			scsisize = cmnd->scmd->request_bufflen;
+			if (scsisize < expsize) {
+				rsp->flags |= ISCSI_FLAG_CMD_UNDERFLOW;
+				size = expsize - scsisize;
+			} else if (scsisize > expsize) {
+				rsp->flags |= ISCSI_FLAG_CMD_OVERFLOW;
+				size = scsisize - expsize;
+			} else
+				size = 0;
+			rsp->residual_count = cpu_to_be32(size);
+			list_add_tail(&data_cmnd->list, &send);
+
+			break;
+		}
+
+		data_cmnd->pdu.datasize = pdusize;
+
+		size -= pdusize;
+		offset += pdusize;
+		sn++;
+
+		list_add_tail(&data_cmnd->list, &send);
+	}
+
+	iscsi_cmnds_init_write(&send);
+}
+
+static struct istgt_cmd *create_scsi_rsp(struct istgt_cmd *req)
+{
+	struct istgt_cmd *rsp;
+	struct iscsi_cmd *req_hdr = cmd_hdr(req);
+	struct iscsi_cmd_rsp *rsp_hdr;
+
+	rsp = iscsi_cmnd_create_rsp_cmnd(req, 1);
+
+	rsp_hdr = (struct iscsi_cmd_rsp *)&rsp->pdu.bhs;
+	rsp_hdr->opcode = ISCSI_OP_SCSI_CMD_RSP;
+	rsp_hdr->flags = ISCSI_FLAG_CMD_FINAL;
+	rsp_hdr->response = ISCSI_STATUS_CMD_COMPLETED;
+	rsp_hdr->cmd_status = SAM_STAT_GOOD;
+	rsp_hdr->itt = req_hdr->itt;
+
+	return rsp;
+}
+
+static void send_scsi_rsp(struct istgt_cmd *req)
+{
+	struct istgt_cmd *rsp;
+	struct iscsi_cmd_rsp *rsp_hdr;
+	uint32_t size;
+
+	rsp = create_scsi_rsp(req);
+	rsp_hdr = (struct iscsi_cmd_rsp *) &rsp->pdu.bhs;
+	if ((size = cmnd_read_size(req)) != 0) {
+		rsp_hdr->flags |= ISCSI_FLAG_CMD_UNDERFLOW;
+		rsp_hdr->residual_count = cpu_to_be32(size);
+	}
+
+	iscsi_cmnd_init_write(rsp);
+}
+
+static struct istgt_cmd *do_create_sense_rsp(struct istgt_cmd *req)
+{
+	struct istgt_cmd *rsp;
+	struct iscsi_cmd_rsp *rsp_hdr;
+	struct iscsi_sense_data *sense = &req->sense;
+	struct scatterlist *sg = &req->sense_sg;
+	struct scatterlist *sg_data = req->scmd->request_buffer;
+	struct page *page;
+
+	page = sg_data[0].page;
+	rsp = iscsi_cmnd_create_rsp_cmnd(req, 1);
+
+	rsp_hdr = (struct iscsi_cmd_rsp *)&rsp->pdu.bhs;
+	rsp_hdr->opcode = ISCSI_OP_SCSI_CMD_RSP;
+	rsp_hdr->flags = ISCSI_FLAG_CMD_FINAL;
+	rsp_hdr->response = ISCSI_STATUS_CMD_COMPLETED;
+	rsp_hdr->cmd_status = SAM_STAT_CHECK_CONDITION;
+	rsp_hdr->itt = cmd_hdr(req)->itt;
+
+	memcpy(sense->sense_buff, req->scmd->sense_buffer,
+		sizeof(sense->sense_buff));
+	/*
+	 * this looks broken for ppc
+	 */
+	sense->length = cpu_to_be16(req->scmd->request_bufflen);
+
+	sg->page = virt_to_page(sense);
+	sg->offset = offset_in_page(sense);
+	sg->length = req->scmd->request_bufflen + sizeof(uint16_t);
+	rsp->pdu.datasize = sg->length;
+	rsp->sg = sg;
+
+	return rsp;
+}
+
+static struct istgt_cmd *create_sense_rsp(struct istgt_cmd *req,
+					   uint8_t sense_key, uint8_t asc, uint8_t ascq)
+{
+	struct istgt_cmd *rsp;
+	struct iscsi_cmd_rsp *rsp_hdr;
+	struct scatterlist *sg = &req->sense_sg;
+	struct iscsi_sense_data *sense = &req->sense;
+	uint8_t *data = sense->sense_buff;
+
+	rsp = iscsi_cmnd_create_rsp_cmnd(req, 1);
+
+	rsp_hdr = (struct iscsi_cmd_rsp *)&rsp->pdu.bhs;
+	rsp_hdr->opcode = ISCSI_OP_SCSI_CMD_RSP;
+	rsp_hdr->flags = ISCSI_FLAG_CMD_FINAL;
+	rsp_hdr->response = ISCSI_STATUS_CMD_COMPLETED;
+	rsp_hdr->cmd_status = SAM_STAT_CHECK_CONDITION;
+	rsp_hdr->itt = cmd_hdr(req)->itt;
+
+	sg->page = virt_to_page(sense);
+	sg->offset = offset_in_page(sense);
+
+	sense->length = cpu_to_be16(14);
+	data[0] = 0xf0;
+	data[2] = sense_key;
+	data[7] = 6;	// Additional sense length
+	data[12] = asc;
+	data[13] = ascq;
+
+	rsp->pdu.datasize = sizeof(uint16_t) + 14;
+	rsp->sg = sg;
+
+	sg->length = (rsp->pdu.datasize + 3) & -4;
+
+	return rsp;
+}
+
+/**
+ * Free a command.
+ * Also frees the additional header.
+ *
+ * iscsi_cmnd_remove - 
+ * @cmnd: ptr to command
+ */
+
+void iscsi_cmnd_remove(struct istgt_cmd *cmnd)
+{
+	struct iscsi_conn *conn;
+
+	if (!cmnd)
+		return;
+	dprintk("%p\n", cmnd);
+	conn = cmnd->conn;
+	kfree(cmnd->pdu.ahs);
+
+	if (!list_empty(&cmnd->list)) {
+		struct iscsi_cmd *req = cmd_hdr(cmnd);
+
+		eprintk("cmnd %p still on some list?, %x %x %x %x %x %x %x %lx\n",
+			cmnd, req->opcode, req->cdb[0], req->flags, req->itt,
+			be32_to_cpu(req->data_length), req->cmdsn,
+			be32_to_cpu(cmnd->pdu.datasize), conn->state);
+
+		if (cmnd->req) {
+			struct iscsi_cmd *req = cmd_hdr(cmnd->req);
+			eprintk("%p %x %u\n", req, req->opcode, req->cdb[0]);
+		}
+		BUG();
+	}
+	list_del(&cmnd->list);
+	spin_lock(&conn->list_lock);
+	atomic_dec(&conn->nr_cmnds);
+	list_del(&cmnd->conn_list);
+	spin_unlock(&conn->list_lock);
+
+	if (cmnd->scmd)
+		cmnd->done(cmnd->scmd);
+	kmem_cache_free(istgt_cmd_cache, cmnd);
+}
+
+static void cmnd_skip_pdu(struct istgt_cmd *cmnd)
+{
+/* 	struct iscsi_conn *conn = cmnd->conn; */
+/* 	struct tio *tio = cmnd->tio; */
+/* 	char *addr; */
+/* 	u32 size; */
+/* 	int i; */
+
+	BUG_ON(1);
+
+/* 	eprintk("%x %x %x %u\n", cmd_itt(cmnd), cmd_opcode(cmnd), */
+/* 		cmd_hdr(cmnd)->cdb[0], cmnd->pdu.datasize); */
+
+/* 	if (!(size = cmnd->pdu.datasize)) */
+/* 		return; */
+
+/* 	if (tio) */
+/* 		assert(tio->pg_cnt > 0); */
+/* 	else */
+/* 		tio = cmnd->tio = tio_alloc(1); */
+
+/* 	addr = page_address(tio->pvec[0]); */
+/* 	assert(addr); */
+/* 	size = (size + 3) & -4; */
+/* 	conn->read_size = size; */
+/* 	for (i = 0; size > PAGE_CACHE_SIZE; i++, size -= PAGE_CACHE_SIZE) { */
+/* 		assert(i < ISCSI_CONN_IOV_MAX); */
+/* 		conn->read_iov[i].iov_base = addr; */
+/* 		conn->read_iov[i].iov_len = PAGE_CACHE_SIZE; */
+/* 	} */
+/* 	conn->read_iov[i].iov_base = addr; */
+/* 	conn->read_iov[i].iov_len = size; */
+/* 	conn->read_msg.msg_iov = conn->read_iov; */
+/* 	conn->read_msg.msg_iovlen = ++i; */
+}
+
+static void iscsi_cmnd_reject(struct istgt_cmd *req, int reason)
+{
+/* 	struct istgt_cmd *rsp; */
+/* 	struct iscsi_reject_hdr *rsp_hdr; */
+/* 	struct tio *tio; */
+/* 	char *addr; */
+
+	BUG_ON(1);
+
+/* 	rsp = iscsi_cmnd_create_rsp_cmnd(req, 1); */
+/* 	rsp_hdr = (struct iscsi_reject_hdr *)&rsp->pdu.bhs; */
+
+/* 	rsp_hdr->opcode = ISCSI_OP_REJECT; */
+/* 	rsp_hdr->ffffffff = ISCSI_RESERVED_TAG; */
+/* 	rsp_hdr->reason = reason; */
+
+/* 	rsp->tio = tio = tio_alloc(1); */
+/* 	addr = page_address(tio->pvec[0]); */
+/* 	clear_page(addr); */
+/* 	memcpy(addr, &req->pdu.bhs, sizeof(struct iscsi_hdr)); */
+/* 	tio->size = rsp->pdu.datasize = sizeof(struct iscsi_hdr); */
+/* 	cmnd_skip_pdu(req); */
+
+/* 	req->pdu.bhs.opcode = ISCSI_OP_PDU_REJECT; */
+}
+
+static void cmnd_set_sn(struct istgt_cmd *cmnd, int set_stat_sn)
+{
+	struct iscsi_conn *conn = cmnd->conn;
+	struct iscsi_session *sess = conn->session;
+
+	if (set_stat_sn)
+		cmnd->pdu.bhs.statsn = cpu_to_be32(conn->stat_sn++);
+	cmnd->pdu.bhs.exp_statsn = cpu_to_be32(sess->exp_cmd_sn);
+	cmnd->pdu.bhs.max_statsn = cpu_to_be32(sess->exp_cmd_sn +
+						sess->max_queued_cmnds);
+}
+
+static void update_stat_sn(struct istgt_cmd *cmnd)
+{
+	struct iscsi_conn *conn = cmnd->conn;
+	uint32_t exp_stat_sn;
+
+	cmnd->pdu.bhs.exp_statsn = exp_stat_sn = be32_to_cpu(cmnd->pdu.bhs.exp_statsn);
+	dprintk("%x,%x\n", cmd_opcode(cmnd), exp_stat_sn);
+	if ((int32_t) (exp_stat_sn - conn->exp_stat_sn) > 0 &&
+	    (int32_t) (exp_stat_sn - conn->stat_sn) <= 0) {
+		// free pdu resources
+		cmnd->conn->exp_stat_sn = exp_stat_sn;
+	}
+}
+
+static int check_cmd_sn(struct istgt_cmd *cmnd)
+{
+	struct iscsi_session *session = cmnd->conn->session;
+	uint32_t cmd_sn;
+
+	cmnd->pdu.bhs.statsn = cmd_sn = be32_to_cpu(cmnd->pdu.bhs.statsn);
+	dprintk("%d(%d)\n", cmd_sn, session->exp_cmd_sn);
+	if ((int32_t) (cmd_sn - session->exp_cmd_sn) >= 0)
+		return 0;
+	eprintk("sequence error (%x,%x)\n", cmd_sn, session->exp_cmd_sn);
+	return -ISCSI_REASON_PROTOCOL_ERROR;
+}
+
+static struct istgt_cmd *__cmnd_find_hash(struct iscsi_session *session,
+					   uint32_t itt, uint32_t ttt)
+{
+	struct list_head *head;
+	struct istgt_cmd *cmnd;
+
+	head = &session->cmnd_hash[cmnd_hashfn(itt)];
+
+	list_for_each_entry(cmnd, head, hash_list) {
+		if (cmnd->pdu.bhs.itt == itt) {
+			if ((ttt != ISCSI_RESERVED_TAG) && (ttt != cmnd->target_task_tag))
+				continue;
+			return cmnd;
+		}
+	}
+
+	return NULL;
+}
+
+static struct istgt_cmd *cmnd_find_hash(struct iscsi_session *session,
+					 uint32_t itt, uint32_t ttt)
+{
+	struct istgt_cmd *cmnd;
+
+	spin_lock(&session->cmnd_hash_lock);
+
+	cmnd = __cmnd_find_hash(session, itt, ttt);
+
+	spin_unlock(&session->cmnd_hash_lock);
+
+	return cmnd;
+}
+
+static int cmnd_insert_hash(struct istgt_cmd *cmnd)
+{
+	struct iscsi_session *session = cmnd->conn->session;
+	struct istgt_cmd *tmp;
+	struct list_head *head;
+	int err = 0;
+	uint32_t itt = cmnd->pdu.bhs.itt;
+
+	dprintk("%p:%x\n", cmnd, itt);
+	if (itt == ISCSI_RESERVED_TAG) {
+		err = -ISCSI_REASON_PROTOCOL_ERROR;
+		goto out;
+	}
+
+	head = &session->cmnd_hash[cmnd_hashfn(cmnd->pdu.bhs.itt)];
+
+	spin_lock(&session->cmnd_hash_lock);
+
+	tmp = __cmnd_find_hash(session, itt, ISCSI_RESERVED_TAG);
+	if (!tmp) {
+		list_add_tail(&cmnd->hash_list, head);
+		set_cmd_hashed(cmnd);
+	} else
+		err = -ISCSI_REASON_TASK_IN_PROGRESS;
+
+	spin_unlock(&session->cmnd_hash_lock);
+
+	if (!err) {
+		update_stat_sn(cmnd);
+		err = check_cmd_sn(cmnd);
+	}
+
+out:
+	return err;
+}
+
+static void __cmnd_remove_hash(struct istgt_cmd *cmnd)
+{
+	list_del(&cmnd->hash_list);
+}
+
+static void cmnd_remove_hash(struct istgt_cmd *cmnd)
+{
+	struct iscsi_session *session = cmnd->conn->session;
+	struct istgt_cmd *tmp;
+
+	spin_lock(&session->cmnd_hash_lock);
+
+	tmp = __cmnd_find_hash(session, cmnd->pdu.bhs.itt, ISCSI_RESERVED_TAG);
+
+	if (tmp && tmp == cmnd)
+		__cmnd_remove_hash(tmp);
+	else
+		eprintk("%p:%x not found\n", cmnd, cmd_itt(cmnd));
+
+	spin_unlock(&session->cmnd_hash_lock);
+}
+
+static void cmnd_skip_data(struct istgt_cmd *req)
+{
+	struct istgt_cmd *rsp;
+	struct iscsi_cmd_rsp *rsp_hdr;
+	uint32_t size;
+
+	rsp = get_rsp_cmnd(req);
+	rsp_hdr = (struct iscsi_cmd_rsp *)&rsp->pdu.bhs;
+	if (cmd_opcode(rsp) != ISCSI_OP_SCSI_CMD_RSP) {
+		eprintk("unexpected response command %u\n", cmd_opcode(rsp));
+		return;
+	}
+
+	size = cmnd_write_size(req);
+	if (size) {
+		rsp_hdr->flags |= ISCSI_FLAG_CMD_UNDERFLOW;
+		rsp_hdr->residual_count = cpu_to_be32(size);
+	}
+	size = cmnd_read_size(req);
+	if (size) {
+		if (cmd_hdr(req)->flags & ISCSI_FLAG_CMD_WRITE) {
+			rsp_hdr->flags |= ISCSI_FLAG_CMD_BIDI_UNDERFLOW;
+			rsp_hdr->bi_residual_count = cpu_to_be32(size);
+		} else {
+			rsp_hdr->flags |= ISCSI_FLAG_CMD_BIDI_OVERFLOW;
+			rsp_hdr->residual_count = cpu_to_be32(size);
+		}
+	}
+	req->pdu.bhs.opcode =
+		(req->pdu.bhs.opcode & ~ISCSI_OPCODE_MASK) | ISCSI_OP_SCSI_REJECT;
+
+	cmnd_skip_pdu(req);
+}
+
+static int cmnd_recv_pdu(struct iscsi_conn *conn, struct scsi_cmnd *scmd,
+			 uint32_t offset, uint32_t size)
+{
+	int idx, i;
+	char *addr;
+	struct scatterlist *sg;
+
+	dprintk("%u,%u\n", offset, size);
+
+	BUG_ON(!scmd);
+	BUG_ON(!scmd->request_buffer);
+	sg = scmd->request_buffer;
+	offset += sg->offset;
+
+	if (!(offset < sg->offset + scmd->request_bufflen) ||
+	    !(offset + size <= sg->offset + scmd->request_bufflen)) {
+		eprintk("%u %u %u %u", offset, size, sg->offset,
+			scmd->request_bufflen);
+		return -EIO;
+	}
+	BUG_ON(!(offset < sg->offset + scmd->request_bufflen));
+	BUG_ON(!(offset + size <= sg->offset + scmd->request_bufflen));
+
+	idx = offset >> PAGE_CACHE_SHIFT;
+	offset &= ~PAGE_CACHE_MASK;
+
+	conn->read_msg.msg_iov = conn->read_iov;
+	conn->read_size = (size + 3) & -4;
+	conn->read_overflow = 0;
+
+	i = 0;
+	while (1) {
+		sg = scmd->request_buffer + idx;
+		BUG_ON(!sg);
+		BUG_ON(!sg->page);
+		addr = page_address(sg->page);
+		BUG_ON(!addr);
+
+		conn->read_iov[i].iov_base =  addr + offset;
+		if (offset + size <= PAGE_CACHE_SIZE) {
+			conn->read_iov[i].iov_len = size;
+			conn->read_msg.msg_iovlen = ++i;
+			break;
+		}
+		conn->read_iov[i].iov_len = PAGE_CACHE_SIZE - offset;
+		size -= conn->read_iov[i].iov_len;
+		offset = 0;
+		if (++i >= ISCSI_CONN_IOV_MAX) {
+			conn->read_msg.msg_iovlen = i;
+			conn->read_overflow = size;
+			conn->read_size -= size;
+			break;
+		}
+
+		idx++;
+	}
+
+	return 0;
+}
+
+static void send_r2t(struct istgt_cmd *req)
+{
+	struct istgt_cmd *rsp;
+	struct iscsi_r2t_rsp *rsp_hdr;
+	uint32_t length, offset, burst;
+	LIST_HEAD(send);
+
+	length = req->r2t_length;
+	burst = req->conn->session->param.max_burst_length;
+	offset = be32_to_cpu(cmd_hdr(req)->data_length) - length;
+
+	do {
+		rsp = iscsi_cmnd_create_rsp_cmnd(req, 0);
+		rsp->pdu.bhs.ttt = req->target_task_tag;
+
+		rsp_hdr = (struct iscsi_r2t_rsp *)&rsp->pdu.bhs;
+		rsp_hdr->opcode = ISCSI_OP_R2T;
+		rsp_hdr->flags = ISCSI_FLAG_CMD_FINAL;
+		memcpy(rsp_hdr->lun, cmd_hdr(req)->lun, 8);
+		rsp_hdr->itt = cmd_hdr(req)->itt;
+		rsp_hdr->r2tsn = cpu_to_be32(req->r2t_sn++);
+		rsp_hdr->data_offset = cpu_to_be32(offset);
+		if (length > burst) {
+			rsp_hdr->data_length = cpu_to_be32(burst);
+			length -= burst;
+			offset += burst;
+		} else {
+			rsp_hdr->data_length = cpu_to_be32(length);
+			length = 0;
+		}
+
+		dprintk("%x %u %u %u %u\n", cmd_itt(req),
+			be32_to_cpu(rsp_hdr->data_length),
+			be32_to_cpu(rsp_hdr->data_offset),
+			be32_to_cpu(rsp_hdr->r2tsn), req->outstanding_r2t);
+
+		list_add_tail(&rsp->list, &send);
+
+		if (++req->outstanding_r2t >= req->conn->session->param.max_outstanding_r2t)
+			break;
+
+	} while (length);
+
+	iscsi_cmnds_init_write(&send);
+}
+
+static void __scsi_cmnd_done(void *data)
+{
+	struct scsi_cmnd *scmd = data;
+	struct istgt_cmd *cmnd = (struct istgt_cmd *) scmd->SCp.ptr;
+	struct iscsi_cmd *req = cmd_hdr(cmnd);
+
+	if (scmd->result) {
+		struct istgt_cmd *rsp;
+
+		rsp = do_create_sense_rsp(cmnd);
+		iscsi_cmnd_init_write(rsp);
+		return;
+	}
+
+	switch (req->cdb[0]) {
+	case INQUIRY:
+	case REPORT_LUNS:
+	case READ_CAPACITY:
+	case MODE_SENSE:
+	case REQUEST_SENSE:
+	case SERVICE_ACTION_IN:
+	case READ_6:
+	case READ_10:
+	case READ_16:
+		do_send_data_rsp(cmnd);
+		break;
+	case WRITE_6:
+	case WRITE_10:
+	case WRITE_16:
+	case WRITE_VERIFY:
+	case START_STOP:
+	case TEST_UNIT_READY:
+	case SYNCHRONIZE_CACHE:
+	case VERIFY:
+	case VERIFY_16:
+	case RESERVE:
+	case RELEASE:
+	case RESERVE_10:
+	case RELEASE_10:
+		send_scsi_rsp(cmnd);
+		break;
+	default:
+		BUG_ON(1);
+		break;
+	}
+}
+
+/* TODO : merge this with nthread. */
+static int scsi_cmnd_done(struct scsi_cmnd *scmd,
+			  void (*done)(struct scsi_cmnd *))
+{
+	struct istgt_cmd *cmnd = (struct istgt_cmd *) scmd->SCp.ptr;
+	int err;
+
+	cmnd->done = done;
+	INIT_WORK(&cmnd->work, __scsi_cmnd_done, scmd);
+	err = schedule_work(&cmnd->work);
+	BUG_ON(!err);
+
+	return TGT_CMD_XMIT_OK;
+}
+
+static void tgt_scsi_cmd_create(struct istgt_cmd *req)
+{
+	struct iscsi_cmd *req_hdr = cmd_hdr(req);
+	struct scsi_cmnd *scmd;
+	enum dma_data_direction data_dir;
+
+	/*
+	 * handle bidi later
+	 */
+	if (req_hdr->flags & ISCSI_FLAG_CMD_WRITE)
+		data_dir = DMA_TO_DEVICE;
+	else if (req_hdr->flags & ISCSI_FLAG_CMD_READ)
+		data_dir = DMA_FROM_DEVICE;
+	else
+		data_dir = DMA_NONE;
+
+
+	scmd = scsi_host_get_command(req->conn->session->shost, data_dir,
+				     GFP_KERNEL);
+	BUG_ON(!scmd);
+	req->scmd = scmd;
+
+	memcpy(scmd->data_cmnd, req_hdr->cdb, MAX_COMMAND_SIZE);
+	scmd->request_bufflen = be32_to_cpu(req_hdr->data_length);
+	scmd->SCp.ptr = (char *) req;
+
+	switch (req->pdu.bhs.flags & ISCSI_FLAG_CMD_ATTR_MASK) {
+	case ISCSI_ATTR_UNTAGGED:
+	case ISCSI_ATTR_SIMPLE:
+		scmd->tag = MSG_SIMPLE_TAG;
+		break;
+	case ISCSI_ATTR_ORDERED:
+		scmd->tag = MSG_ORDERED_TAG;
+		break;
+	case ISCSI_ATTR_HEAD_OF_QUEUE:
+		scmd->tag = MSG_HEAD_TAG;
+		break;
+	case ISCSI_ATTR_ACA:
+		scmd->tag = MSG_SIMPLE_TAG;
+		break;
+	default:
+		scmd->tag = MSG_SIMPLE_TAG;
+	}
+
+	if (scmd->sc_data_direction == DMA_TO_DEVICE &&
+	    be32_to_cpu(req_hdr->data_length)) {
+		switch (req_hdr->cdb[0]) {
+		case WRITE_6:
+		case WRITE_10:
+		case WRITE_16:
+		case WRITE_VERIFY:
+			break;
+		default:
+			eprintk("%x\n", req_hdr->cdb[0]);
+			break;
+		}
+	}
+
+	scsi_tgt_queue_command(scmd, (struct scsi_lun *)req_hdr->lun, 0);
+}
+
+static void scsi_cmnd_exec(struct istgt_cmd *cmnd)
+{
+	struct scsi_cmnd *scmd = cmnd->scmd;
+
+	if (cmnd->r2t_length) {
+		if (!cmnd->is_unsolicited_data)
+			send_r2t(cmnd);
+	} else {
+		set_cmd_waitio(cmnd);
+		if (scmd) {
+			if (!cmnd->done)
+				BUG();
+			else
+				cmnd->done(scmd);
+		} else
+			tgt_scsi_cmd_create(cmnd);
+	}
+}
+
+static int noop_out_start(struct iscsi_conn *conn, struct istgt_cmd *cmnd)
+{
+	uint32_t size, tmp;
+	int i = 0, err = 0;
+
+	if (cmd_ttt(cmnd) != cpu_to_be32(ISCSI_RESERVED_TAG)) {
+		/*
+		 * We don't request a NOP-Out by sending a NOP-In.
+		 * See 10.18.2 in the draft 20.
+		 */
+		eprintk("initiator bug %x\n", cmd_itt(cmnd));
+		err = -ISCSI_REASON_PROTOCOL_ERROR;
+		goto out;
+	}
+
+	if (cmd_itt(cmnd) == cpu_to_be32(ISCSI_RESERVED_TAG)) {
+		if (!(cmnd->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE))
+			eprintk("%s\n","initiator bug!");
+		update_stat_sn(cmnd);
+		err = check_cmd_sn(cmnd);
+		goto out;
+	} else if ((err = cmnd_insert_hash(cmnd)) < 0) {
+		eprintk("ignore this request %x\n", cmd_itt(cmnd));
+		goto out;
+	}
+
+	if ((size = cmnd->pdu.datasize)) {
+		size = (size + 3) & -4;
+		conn->read_msg.msg_iov = conn->read_iov;
+		if (cmnd->pdu.bhs.itt != cpu_to_be32(ISCSI_RESERVED_TAG)) {
+/* 			struct tio *tio; */
+			int pg_cnt = get_pgcnt(size, 0);
+
+			BUG_ON(pg_cnt >= ISCSI_CONN_IOV_MAX);
+			BUG_ON(1);
+/* 			cmnd->tio = tio = tio_alloc(pg_cnt); */
+/* 			tio_set(tio, size, 0); */
+
+/* 			for (i = 0; i < pg_cnt; i++) { */
+/* 				conn->read_iov[i].iov_base */
+/* 					= page_address(tio->pvec[i]); */
+/* 				tmp = min_t(u32, size, PAGE_CACHE_SIZE); */
+/* 				conn->read_iov[i].iov_len = tmp; */
+/* 				conn->read_size += tmp; */
+/* 				size -= tmp; */
+/* 			} */
+		} else {
+			for (i = 0; i < ISCSI_CONN_IOV_MAX; i++) {
+				conn->read_iov[i].iov_base = dummy_data;
+				tmp = min_t(uint32_t, size, sizeof(dummy_data));
+				conn->read_iov[i].iov_len = tmp;
+				conn->read_size += tmp;
+				size -= tmp;
+			}
+		}
+		BUG_ON(size);
+		conn->read_overflow = size;
+		conn->read_msg.msg_iovlen = i;
+	}
+
+out:
+	return err;
+}
+
+static uint32_t get_next_ttt(struct iscsi_session *session)
+{
+	uint32_t ttt;
+
+	if (session->next_ttt == ISCSI_RESERVED_TAG)
+		session->next_ttt++;
+	ttt = session->next_ttt++;
+
+	return cpu_to_be32(ttt);
+}
+
+static void scsi_cmnd_start(struct iscsi_conn *conn, struct istgt_cmd *req)
+{
+	struct iscsi_cmd *req_hdr = cmd_hdr(req);
+
+	dprintk("scsi command: %02x\n", req_hdr->cdb[0]);
+
+	switch (req_hdr->cdb[0]) {
+	case SERVICE_ACTION_IN:
+		if ((req_hdr->cdb[1] & 0x1f) != 0x10)
+			goto error;
+
+	case INQUIRY:
+	case REPORT_LUNS:
+	case TEST_UNIT_READY:
+	case SYNCHRONIZE_CACHE:
+	case VERIFY:
+	case VERIFY_16:
+	case START_STOP:
+	case READ_CAPACITY:
+	case MODE_SENSE:
+	case REQUEST_SENSE:
+	case RESERVE:
+	case RELEASE:
+	case RESERVE_10:
+	case RELEASE_10:
+	case READ_6:
+	case READ_10:
+	case READ_16:
+	{
+		if (!(req_hdr->flags & ISCSI_FLAG_CMD_FINAL) ||
+		      req->pdu.datasize) {
+			/* unexpected unsolicited data */
+			eprintk("%x %x\n", cmd_itt(req), req_hdr->cdb[0]);
+			create_sense_rsp(req, ABORTED_COMMAND, 0xc, 0xc);
+			cmnd_skip_data(req);
+		}
+		break;
+	}
+	case WRITE_6:
+	case WRITE_10:
+	case WRITE_16:
+	case WRITE_VERIFY:
+	{
+		struct iscsi_sess_param *param = &conn->session->param;
+
+		/*
+		 * We don't know this command arrives in order,
+		 * however we need to allocate buffer for immediate
+		 * and unsolicited data. tgt will not start to perform
+		 * this command until we call cmd->done so we don't
+		 * need to worry about the order of the command.
+		 */
+		tgt_scsi_cmd_create(req);
+		wait_for_completion(&req->event);
+
+		req->r2t_length = be32_to_cpu(req_hdr->data_length) - req->pdu.datasize;
+		req->is_unsolicited_data = !(req_hdr->flags &
+						ISCSI_FLAG_CMD_FINAL);
+		req->target_task_tag = get_next_ttt(conn->session);
+
+		if (!param->immediate_data && req->pdu.datasize)
+			eprintk("%x %x\n", cmd_itt(req), req_hdr->cdb[0]);
+
+		if (param->initial_r2t &&
+		    !(req_hdr->flags & ISCSI_FLAG_CMD_FINAL))
+			eprintk("%x %x\n", cmd_itt(req), req_hdr->cdb[0]);
+
+		if (req_hdr->cdb[0] == WRITE_VERIFY && req_hdr->cdb[1] & 0x02)
+			eprintk("Verification is ignored %x\n", cmd_itt(req));
+
+		if (req->pdu.datasize) {
+			if (cmnd_recv_pdu(conn, req->scmd, 0,
+					  req->pdu.datasize) < 0)
+				BUG_ON(1);
+		}
+		break;
+	}
+	error:
+	default:
+		eprintk("Unsupported %x\n", req_hdr->cdb[0]);
+		create_sense_rsp(req, ILLEGAL_REQUEST, 0x20, 0x0);
+		cmnd_skip_data(req);
+		break;
+	}
+
+	return;
+}
+
+static void data_out_start(struct iscsi_conn *conn, struct istgt_cmd *cmnd)
+{
+	struct iscsi_data *req = (struct iscsi_data *)&cmnd->pdu.bhs;
+	struct istgt_cmd *scsi_cmnd = NULL;
+	uint32_t offset = be32_to_cpu(req->offset);
+
+	update_stat_sn(cmnd);
+
+	cmnd->req = scsi_cmnd = cmnd_find_hash(conn->session, req->itt, req->ttt);
+	if (!scsi_cmnd) {
+		eprintk("unable to find scsi task %x %x\n",
+			cmd_itt(cmnd), cmd_ttt(cmnd));
+		goto skip_data;
+	}
+
+	if (scsi_cmnd->r2t_length < cmnd->pdu.datasize) {
+		eprintk("invalid data len %x %u %u\n",
+			cmd_itt(scsi_cmnd), cmnd->pdu.datasize, scsi_cmnd->r2t_length);
+		goto skip_data;
+	}
+
+	if (scsi_cmnd->r2t_length + offset != cmnd_write_size(scsi_cmnd)) {
+		eprintk("%x %u %u %u\n", cmd_itt(scsi_cmnd), scsi_cmnd->r2t_length,
+			offset,	cmnd_write_size(scsi_cmnd));
+		goto skip_data;
+	}
+
+	scsi_cmnd->r2t_length -= cmnd->pdu.datasize;
+
+	if (req->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) {
+		/* unsolicited burst data */
+		if (scsi_cmnd->pdu.bhs.flags & ISCSI_FLAG_CMD_FINAL) {
+			eprintk("unexpected data from %x %x\n",
+				cmd_itt(cmnd), cmd_ttt(cmnd));
+			goto skip_data;
+		}
+	}
+
+	dprintk("%u %p %p %u %u\n", req->ttt, cmnd, scsi_cmnd,
+		offset, cmnd->pdu.datasize);
+
+	if (cmnd_recv_pdu(conn, scsi_cmnd->scmd, offset, cmnd->pdu.datasize) < 0)
+		goto skip_data;
+	return;
+
+skip_data:
+	cmnd->pdu.bhs.opcode = ISCSI_OP_DATA_REJECT;
+	cmnd_skip_pdu(cmnd);
+	return;
+}
+
+static void data_out_end(struct iscsi_conn *conn, struct istgt_cmd *cmnd)
+{
+	struct iscsi_data *req = (struct iscsi_data *) &cmnd->pdu.bhs;
+	struct istgt_cmd *scsi_cmnd;
+	uint32_t offset;
+
+	BUG_ON(!cmnd);
+	scsi_cmnd = cmnd->req;
+	BUG_ON(!scsi_cmnd);
+
+	if (conn->read_overflow) {
+		eprintk("%x %u\n", cmd_itt(cmnd), conn->read_overflow);
+		offset = be32_to_cpu(req->offset);
+		offset += cmnd->pdu.datasize - conn->read_overflow;
+		if (cmnd_recv_pdu(conn, scsi_cmnd->scmd, offset,
+				  conn->read_overflow) < 0)
+			BUG_ON(1);
+		return;
+	}
+
+	if (req->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) {
+		if (req->flags & ISCSI_FLAG_CMD_FINAL) {
+			scsi_cmnd->is_unsolicited_data = 0;
+			if (!cmd_pending(scsi_cmnd))
+				scsi_cmnd_exec(scsi_cmnd);
+		}
+	} else {
+		/* TODO : proper error handling */
+		if (!(req->flags & ISCSI_FLAG_CMD_FINAL) &&
+		    scsi_cmnd->r2t_length == 0)
+			eprintk("initiator error %x\n", cmd_itt(scsi_cmnd));
+
+		if (!(req->flags & ISCSI_FLAG_CMD_FINAL))
+			goto out;
+
+		scsi_cmnd->outstanding_r2t--;
+
+		if (scsi_cmnd->r2t_length == 0)
+			BUG_ON(!list_empty(&scsi_cmnd->pdu_list));
+
+		scsi_cmnd_exec(scsi_cmnd);
+	}
+
+out:
+	iscsi_cmnd_remove(cmnd);
+	return;
+}
+
+/* static int __cmnd_abort(struct istgt_cmd *cmnd) */
+/* { */
+/* 	if (!cmnd_waitio(cmnd)) { */
+/* 		cmnd_release(cmnd, 1); */
+/* 		return 0; */
+/* 	} else */
+/* 		return -ISCSI_RESPONSE_UNKNOWN_TASK; */
+/* } */
+
+/* static int cmnd_abort(struct iscsi_session *session, u32 itt) */
+/* { */
+/* 	struct istgt_cmd *cmnd; */
+/* 	int err =  -ISCSI_RESPONSE_UNKNOWN_TASK; */
+
+/* 	if ((cmnd = cmnd_find_hash(session, itt, ISCSI_RESERVED_TAG))) { */
+/* 		eprintk("%x %x %x %u %u %u %u\n", cmd_itt(cmnd), cmd_opcode(cmnd), */
+/* 			cmnd->r2t_length, cmnd_scsicode(cmnd), */
+/* 			cmnd_write_size(cmnd), cmnd->is_unsolicited_data, */
+/* 			cmnd->outstanding_r2t); */
+/* 		err = __cmnd_abort(cmnd); */
+/* 	} */
+
+/* 	return err; */
+/* } */
+
+/* static int target_reset(struct istgt_cmd *req, u32 lun, int all) */
+/* { */
+/* 	struct iscsi_target *target = req->conn->session->target; */
+/* 	struct iscsi_session *session; */
+/* 	struct iscsi_conn *conn; */
+/* 	struct istgt_cmd *cmnd, *tmp; */
+
+/* 	list_for_each_entry(session, &target->session_list, list) { */
+/* 		list_for_each_entry(conn, &session->conn_list, list) { */
+/* 			list_for_each_entry_safe(cmnd, tmp, &conn->pdu_list, conn_list) { */
+/* 				if (cmnd == req) */
+/* 					continue; */
+
+/* 				if (all) */
+/* 					__cmnd_abort(cmnd); */
+/* 				else if (translate_lun(cmd_hdr(cmnd)->lun) == lun) */
+/* 					__cmnd_abort(cmnd); */
+/* 			} */
+/* 		} */
+/* 	} */
+
+/* 	return 0; */
+/* } */
+
+/* static void task_set_abort(struct istgt_cmd *req) */
+/* { */
+/* 	struct iscsi_session *session = req->conn->session; */
+/* 	struct iscsi_conn *conn; */
+/* 	struct istgt_cmd *cmnd, *tmp; */
+
+/* 	list_for_each_entry(conn, &session->conn_list, list) { */
+/* 		list_for_each_entry_safe(cmnd, tmp, &conn->pdu_list, conn_list) { */
+/* 			if (cmnd != req) */
+/* 				__cmnd_abort(cmnd); */
+/* 		} */
+/* 	} */
+/* } */
+
+static void execute_task_management(struct istgt_cmd *req)
+{
+/* 	struct iscsi_conn *conn = req->conn; */
+/* 	struct iscsi_target *target = conn->session->target; */
+	struct istgt_cmd *rsp;
+	struct iscsi_tm *req_hdr = (struct iscsi_tm *)&req->pdu.bhs;
+	struct iscsi_tm_rsp *rsp_hdr;
+	int function = req_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK;
+
+	rsp = iscsi_cmnd_create_rsp_cmnd(req, 1);
+	rsp_hdr = (struct iscsi_tm_rsp *)&rsp->pdu.bhs;
+
+	rsp_hdr->opcode = ISCSI_OP_SCSI_TMFUNC_RSP;
+	rsp_hdr->flags = ISCSI_FLAG_CMD_FINAL;
+	rsp_hdr->itt = req_hdr->itt;
+/* 	rsp_hdr->response = ISCSI_TMF_RSP_COMPLETE; */
+	rsp_hdr->response = ISCSI_TMF_RSP_REJECTED;
+
+	eprintk("%x %d %x\n", cmd_itt(req), function, req_hdr->rtt);
+
+/* 	switch (function) { */
+/* 	case ISCSI_FUNCTION_ABORT_TASK: */
+/* 	case ISCSI_FUNCTION_ABORT_TASK_SET: */
+/* 	case ISCSI_FUNCTION_CLEAR_ACA: */
+/* 	case ISCSI_FUNCTION_CLEAR_TASK_SET: */
+/* 	case ISCSI_FUNCTION_LOGICAL_UNIT_RESET: */
+/* 		lun = translate_lun(req_hdr->lun); */
+/* 		if (!volume_lookup(target, lun)) { */
+/* 			rsp_hdr->response = ISCSI_RESPONSE_UNKNOWN_LUN; */
+/* 			goto out; */
+/* 		} */
+/* 	} */
+
+/* 	switch (function) { */
+/* 	case ISCSI_FUNCTION_ABORT_TASK: */
+/* 		if ((err = cmnd_abort(conn->session, req_hdr->rtt)) < 0) */
+/* 			rsp_hdr->response = -err; */
+/* 		break; */
+/* 	case ISCSI_FUNCTION_ABORT_TASK_SET: */
+/* 		task_set_abort(req); */
+/* 		break; */
+/* 	case ISCSI_FUNCTION_CLEAR_ACA: */
+/* 		rsp_hdr->response = ISCSI_RESPONSE_FUNCTION_UNSUPPORTED; */
+/* 		break; */
+/* 	case ISCSI_FUNCTION_CLEAR_TASK_SET: */
+/* 		rsp_hdr->response = ISCSI_RESPONSE_FUNCTION_UNSUPPORTED; */
+/* 		break; */
+/* 	case ISCSI_FUNCTION_LOGICAL_UNIT_RESET: */
+/* 		target_reset(req, translate_lun(req_hdr->lun), 0); */
+/* 		break; */
+/* 	case ISCSI_FUNCTION_TARGET_WARM_RESET: */
+/* 	case ISCSI_FUNCTION_TARGET_COLD_RESET: */
+/* 		target_reset(req, 0, 1); */
+/* 		if (function == ISCSI_FUNCTION_TARGET_COLD_RESET) */
+/* 			set_cmnd_close(rsp); */
+/* 		break; */
+/* 	case ISCSI_FUNCTION_TASK_REASSIGN: */
+/* 		rsp_hdr->response = ISCSI_RESPONSE_FUNCTION_UNSUPPORTED; */
+/* 		break; */
+/* 	default: */
+/* 		rsp_hdr->response = ISCSI_RESPONSE_FUNCTION_REJECTED; */
+/* 		break; */
+/* 	} */
+/* out: */
+	iscsi_cmnd_init_write(rsp);
+}
+
+static void noop_out_exec(struct istgt_cmd *req)
+{
+	struct istgt_cmd *rsp;
+	struct iscsi_nopin *rsp_hdr;
+
+	if (cmd_itt(req) != cpu_to_be32(ISCSI_RESERVED_TAG)) {
+		rsp = iscsi_cmnd_create_rsp_cmnd(req, 1);
+
+		rsp_hdr = (struct iscsi_nopin *)&rsp->pdu.bhs;
+		rsp_hdr->opcode = ISCSI_OP_NOOP_IN;
+		rsp_hdr->flags = ISCSI_FLAG_CMD_FINAL;
+		rsp_hdr->itt = req->pdu.bhs.itt;
+		rsp_hdr->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
+
+/* 		if (req->pdu.datasize) */
+/* 			assert(req->tio); */
+/* 		else */
+/* 			assert(!req->tio); */
+
+/* 		if (req->tio) { */
+/* 			tio_get(req->tio); */
+/* 			rsp->tio = req->tio; */
+/* 		} */
+
+		BUG_ON(get_pgcnt(req->pdu.datasize, 0) >= ISCSI_CONN_IOV_MAX);
+		rsp->pdu.datasize = req->pdu.datasize;
+		iscsi_cmnd_init_write(rsp);
+	} else
+		iscsi_cmnd_remove(req);
+}
+
+static void logout_exec(struct istgt_cmd *req)
+{
+	struct iscsi_logout *req_hdr;
+	struct istgt_cmd *rsp;
+	struct iscsi_logout_rsp *rsp_hdr;
+
+	req_hdr = (struct iscsi_logout *)&req->pdu.bhs;
+	rsp = iscsi_cmnd_create_rsp_cmnd(req, 1);
+	rsp_hdr = (struct iscsi_logout_rsp *)&rsp->pdu.bhs;
+	rsp_hdr->opcode = ISCSI_OP_LOGOUT_RSP;
+	rsp_hdr->flags = ISCSI_FLAG_CMD_FINAL;
+	rsp_hdr->itt = req_hdr->itt;
+	set_cmd_close(rsp);
+	iscsi_cmnd_init_write(rsp);
+}
+
+static void iscsi_cmnd_exec(struct istgt_cmd *cmnd)
+{
+	dprintk("%p,%x,%u\n", cmnd, cmd_opcode(cmnd),
+		cmnd->pdu.bhs.statsn);
+
+	switch (cmd_opcode(cmnd)) {
+	case ISCSI_OP_NOOP_OUT:
+		noop_out_exec(cmnd);
+		break;
+	case ISCSI_OP_SCSI_CMD:
+		scsi_cmnd_exec(cmnd);
+		break;
+	case ISCSI_OP_SCSI_TMFUNC:
+		execute_task_management(cmnd);
+		break;
+	case ISCSI_OP_LOGOUT:
+		logout_exec(cmnd);
+		break;
+	case ISCSI_OP_SCSI_REJECT:
+		iscsi_cmnd_init_write(get_rsp_cmnd(cmnd));
+		break;
+	case ISCSI_OP_TEXT:
+	case ISCSI_OP_SNACK:
+		break;
+	default:
+		eprintk("unexpected cmnd op %x\n", cmd_opcode(cmnd));
+		break;
+	}
+}
+
+static void __cmnd_send_pdu(struct iscsi_conn *conn, struct scatterlist *sg,
+			    uint32_t offset, uint32_t size)
+{
+/* 	dprintk(D_GENERIC, "%p %u,%u\n", tio, offset, size); */
+	offset += sg->offset;
+
+/* 	assert(offset <= sg->offset + tio->size); */
+/* 	assert(offset + size <= tio->offset + tio->size); */
+
+	conn->write_sg = sg;
+	conn->write_offset = offset;
+	conn->write_size += size;
+}
+
+static void cmnd_send_pdu(struct iscsi_conn *conn, struct istgt_cmd *cmnd)
+{
+	uint32_t size;
+
+	if (!cmnd->pdu.datasize)
+		return;
+
+	size = (cmnd->pdu.datasize + 3) & -4;
+	BUG_ON(!cmnd->sg);
+	__cmnd_send_pdu(conn, cmnd->sg, 0, size);
+}
+
+static void set_cork(struct socket *sock, int on)
+{
+	int opt = on;
+	mm_segment_t oldfs;
+
+	oldfs = get_fs();
+	set_fs(get_ds());
+	sock->ops->setsockopt(sock, SOL_TCP, TCP_CORK, (void *)&opt, sizeof(opt));
+	set_fs(oldfs);
+}
+
+void cmnd_release(struct istgt_cmd *cmnd, int force)
+{
+	struct istgt_cmd *req, *rsp;
+	int is_last = 0;
+
+	if (!cmnd)
+		return;
+
+	req = cmnd->req;
+	is_last = cmd_final(cmnd);
+
+	if (force) {
+		while (!list_empty(&cmnd->pdu_list)) {
+			rsp = list_entry(cmnd->pdu_list.next, struct istgt_cmd, pdu_list);
+			list_del_init(&rsp->list);
+			list_del(&rsp->pdu_list);
+			iscsi_cmnd_remove(rsp);
+		}
+		list_del_init(&cmnd->list);
+	}
+
+	if (cmd_hashed(cmnd))
+		cmnd_remove_hash(cmnd);
+
+	list_del_init(&cmnd->pdu_list);
+	iscsi_cmnd_remove(cmnd);
+
+	if (is_last) {
+		BUG_ON(force);
+		BUG_ON(!req);
+		cmnd_release(req, 0);
+	}
+
+	return;
+}
+
+void cmnd_tx_start(struct istgt_cmd *cmnd)
+{
+	struct iscsi_conn *conn = cmnd->conn;
+	struct iovec *iop;
+
+	dprintk("%p:%x\n", cmnd, cmd_opcode(cmnd));
+	BUG_ON(!cmnd);
+	iscsi_cmnd_set_length(&cmnd->pdu);
+
+	set_cork(conn->sock, 1);
+
+	conn->write_iop = iop = conn->write_iov;
+	iop->iov_base = &cmnd->pdu.bhs;
+	iop->iov_len = sizeof(cmnd->pdu.bhs);
+	iop++;
+	conn->write_size = sizeof(cmnd->pdu.bhs);
+
+	switch (cmd_opcode(cmnd)) {
+	case ISCSI_OP_NOOP_IN:
+		cmnd_set_sn(cmnd, 1);
+		cmnd_send_pdu(conn, cmnd);
+		break;
+	case ISCSI_OP_SCSI_CMD_RSP:
+		cmnd_set_sn(cmnd, 1);
+		cmnd_send_pdu(conn, cmnd);
+		break;
+	case ISCSI_OP_SCSI_TMFUNC_RSP:
+		cmnd_set_sn(cmnd, 1);
+		break;
+	case ISCSI_OP_TEXT_RSP:
+		cmnd_set_sn(cmnd, 1);
+		break;
+	case ISCSI_OP_SCSI_DATA_IN:
+	{
+		struct iscsi_data_rsp *rsp = (struct iscsi_data_rsp *)&cmnd->pdu.bhs;
+		uint32_t offset;
+
+		cmnd_set_sn(cmnd, (rsp->flags & ISCSI_FLAG_CMD_FINAL) ? 1 : 0);
+		offset = rsp->offset;
+		rsp->offset = cpu_to_be32(offset);
+		BUG_ON(!cmnd->sg);
+		__cmnd_send_pdu(conn, cmnd->sg, offset, cmnd->pdu.datasize);
+		break;
+	}
+	case ISCSI_OP_LOGOUT_RSP:
+		cmnd_set_sn(cmnd, 1);
+		break;
+	case ISCSI_OP_R2T:
+		cmnd_set_sn(cmnd, 0);
+		cmnd->pdu.bhs.statsn = cpu_to_be32(conn->stat_sn);
+		break;
+	case ISCSI_OP_ASYNC_EVENT:
+		cmnd_set_sn(cmnd, 1);
+		break;
+	case ISCSI_OP_REJECT:
+		cmnd_set_sn(cmnd, 1);
+		cmnd_send_pdu(conn, cmnd);
+		break;
+	default:
+		eprintk("unexpected cmnd op %x\n", cmd_opcode(cmnd));
+		break;
+	}
+
+	iop->iov_len = 0;
+	// move this?
+	conn->write_size = (conn->write_size + 3) & -4;
+}
+
+void cmnd_tx_end(struct istgt_cmd *cmnd)
+{
+	struct iscsi_conn *conn = cmnd->conn;
+
+	dprintk("%p:%x\n", cmnd, cmd_opcode(cmnd));
+	switch (cmd_opcode(cmnd)) {
+	case ISCSI_OP_NOOP_IN:
+	case ISCSI_OP_SCSI_CMD_RSP:
+	case ISCSI_OP_SCSI_TMFUNC_RSP:
+	case ISCSI_OP_TEXT_RSP:
+	case ISCSI_OP_R2T:
+	case ISCSI_OP_ASYNC_EVENT:
+	case ISCSI_OP_REJECT:
+	case ISCSI_OP_SCSI_DATA_IN:
+	case ISCSI_OP_LOGOUT_RSP:
+		break;
+	default:
+		eprintk("unexpected cmnd op %x\n", cmd_opcode(cmnd));
+		BUG_ON(1);
+		break;
+	}
+
+	if (cmd_close(cmnd))
+		conn_close(conn);
+
+	list_del_init(&cmnd->list);
+	set_cork(cmnd->conn->sock, 0);
+}
+
+/**
+ * Push the command for execution.
+ * This functions reorders the commands.
+ * Called from the read thread.
+ *
+ * iscsi_session_push_cmnd - 
+ * @cmnd: ptr to command
+ */
+
+static void iscsi_session_push_cmnd(struct istgt_cmd *cmnd)
+{
+	struct iscsi_session *session = cmnd->conn->session;
+	struct list_head *entry;
+	uint32_t cmd_sn;
+
+	dprintk("%p:%x %u,%u\n",
+		cmnd, cmd_opcode(cmnd), cmnd->pdu.bhs.statsn,
+		session->exp_cmd_sn);
+
+	if (cmnd->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE) {
+		iscsi_cmnd_exec(cmnd);
+		return;
+	}
+
+	cmd_sn = cmnd->pdu.bhs.statsn;
+	if (cmd_sn == session->exp_cmd_sn) {
+		while (1) {
+			session->exp_cmd_sn = ++cmd_sn;
+			iscsi_cmnd_exec(cmnd);
+
+			if (list_empty(&session->pending_list))
+				break;
+			cmnd = list_entry(session->pending_list.next, struct istgt_cmd, list);
+			if (cmnd->pdu.bhs.statsn != cmd_sn)
+				break;
+/* 			eprintk("find out-of-order %x %u %u\n", */
+/* 				cmd_itt(cmnd), cmd_sn, cmnd->pdu.bhs.statsn); */
+			list_del_init(&cmnd->list);
+			clear_cmd_pending(cmnd);
+		}
+	} else {
+/* 		eprintk("out-of-order %x %u %u\n", */
+/* 			cmd_itt(cmnd), cmd_sn, session->exp_cmd_sn); */
+
+		set_cmd_pending(cmnd);
+		if (before(cmd_sn, session->exp_cmd_sn)) /* close the conn */
+			eprintk("unexpected cmd_sn (%u,%u)\n", cmd_sn, session->exp_cmd_sn);
+
+		if (after(cmd_sn, session->exp_cmd_sn + session->max_queued_cmnds))
+			eprintk("too large cmd_sn (%u,%u)\n", cmd_sn, session->exp_cmd_sn);
+
+		list_for_each(entry, &session->pending_list) {
+			struct istgt_cmd *tmp = list_entry(entry, struct istgt_cmd, list);
+			if (before(cmd_sn, tmp->pdu.bhs.statsn))
+				break;
+		}
+
+		BUG_ON(!list_empty(&cmnd->list));
+
+		list_add_tail(&cmnd->list, entry);
+	}
+}
+
+static int check_segment_length(struct istgt_cmd *cmnd)
+{
+	struct iscsi_conn *conn = cmnd->conn;
+	struct iscsi_sess_param *param = &conn->session->param;
+
+	if (cmnd->pdu.datasize > param->max_recv_data_length) {
+		eprintk("too lond data %x %u %u\n", cmd_itt(cmnd),
+			cmnd->pdu.datasize, param->max_recv_data_length);
+
+		if (get_pgcnt(cmnd->pdu.datasize, 0) > ISCSI_CONN_IOV_MAX) {
+			conn_close(conn);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+void cmnd_rx_start(struct istgt_cmd *cmnd)
+{
+	struct iscsi_conn *conn = cmnd->conn;
+	int err = 0;
+
+	if (check_segment_length(cmnd) < 0)
+		return;
+
+	switch (cmd_opcode(cmnd)) {
+	case ISCSI_OP_NOOP_OUT:
+		err = noop_out_start(conn, cmnd);
+		break;
+	case ISCSI_OP_SCSI_CMD:
+		if (!(err = cmnd_insert_hash(cmnd)))
+			scsi_cmnd_start(conn, cmnd);
+		break;
+	case ISCSI_OP_SCSI_TMFUNC:
+		err = cmnd_insert_hash(cmnd);
+		break;
+	case ISCSI_OP_SCSI_DATA_OUT:
+		data_out_start(conn, cmnd);
+		break;
+	case ISCSI_OP_LOGOUT:
+		err = cmnd_insert_hash(cmnd);
+		break;
+	case ISCSI_OP_TEXT:
+	case ISCSI_OP_SNACK:
+		err = -ISCSI_REASON_CMD_NOT_SUPPORTED;
+		break;
+	default:
+		err = -ISCSI_REASON_CMD_NOT_SUPPORTED;
+		break;
+	}
+
+	if (err < 0) {
+		eprintk("%x %x %d\n", cmd_opcode(cmnd), cmd_itt(cmnd), err);
+		iscsi_cmnd_reject(cmnd, -err);
+	}
+}
+
+void cmnd_rx_end(struct istgt_cmd *cmnd)
+{
+	struct iscsi_conn *conn = cmnd->conn;
+
+	dprintk("%p:%x\n", cmnd, cmd_opcode(cmnd));
+	switch (cmd_opcode(cmnd)) {
+	case ISCSI_OP_SCSI_REJECT:
+	case ISCSI_OP_NOOP_OUT:
+	case ISCSI_OP_SCSI_CMD:
+	case ISCSI_OP_SCSI_TMFUNC:
+	case ISCSI_OP_TEXT:
+	case ISCSI_OP_LOGOUT:
+		iscsi_session_push_cmnd(cmnd);
+		break;
+	case ISCSI_OP_SCSI_DATA_OUT:
+		data_out_end(conn, cmnd);
+		break;
+	case ISCSI_OP_SNACK:
+		break;
+	case ISCSI_OP_PDU_REJECT:
+		iscsi_cmnd_init_write(get_rsp_cmnd(cmnd));
+		break;
+	case ISCSI_OP_DATA_REJECT:
+		cmnd_release(cmnd, 0);
+		break;
+	default:
+		eprintk("unexpected cmnd op %x\n", cmd_opcode(cmnd));
+		BUG();
+		break;
+	}
+}
+
+static int buffer_ready(struct scsi_cmnd *scmd,
+			void (*done)(struct scsi_cmnd *))
+{
+	struct istgt_cmd *cmnd = (struct istgt_cmd *) scmd->SCp.ptr;
+
+	cmnd->done = done;
+	complete(&cmnd->event);
+	return 0;
+}
+
+
+static struct iscsi_sess_param default_session_param = {
+	.initial_r2t = 1,
+	.immediate_data = 1,
+	.max_connections = 1,
+	.max_recv_data_length = 8192,
+	.max_xmit_data_length = 8192,
+	.max_burst_length = 262144,
+	.first_burst_length = 65536,
+	.default_wait_time = 2,
+	.default_retain_time = 20,
+	.max_outstanding_r2t = 1,
+	.data_pdu_inorder = 1,
+	.data_sequence_inorder = 1,
+	.error_recovery_level = 0,
+	.header_digest = DIGEST_NONE,
+	.data_digest = DIGEST_NONE,
+	.ofmarker = 0,
+	.ifmarker = 0,
+	.ofmarkint = 2048,
+	.ifmarkint = 2048,
+};
+
+static struct iscsi_trgt_param default_target_param = {
+	.queued_cmnds = DEFAULT_NR_QUEUED_CMNDS,
+};
+
+static struct iscsi_transport istgt_transport;
+
+static struct iscsi_cls_session *
+istgt_session_create(struct scsi_transport_template *scsit,
+		     uint32_t initial_cmdsn, uint32_t *sid)
+{
+	struct Scsi_Host *shost;
+	struct iscsi_session *session;
+	int err, i;
+
+	shost = iscsi_transport_create_session(scsit, &istgt_transport);
+	if (!shost)
+		return NULL;
+
+	if (scsi_tgt_alloc_queue(shost))
+		goto destroy_session;
+
+	session = iscsi_hostdata(shost->hostdata);
+	memset(session, 0, sizeof(*session));
+
+	dprintk("%p %u %" PRIx64 "\n", session, session->shost->host_no);
+
+	session->shost = shost;
+	*sid = session->sid = shost->host_no;
+	memcpy(&session->param, &default_session_param,
+	       sizeof(default_session_param));
+	memcpy(&session->trgt_param, &default_target_param,
+	       sizeof(default_target_param));
+	init_MUTEX(&session->target_sem);
+	INIT_LIST_HEAD(&session->session_list);
+
+	session->max_queued_cmnds = session->trgt_param.queued_cmnds;
+	session->exp_cmd_sn = initial_cmdsn + 1;
+	session->max_cmd_sn = initial_cmdsn + 1;
+
+	INIT_LIST_HEAD(&session->conn_list);
+	INIT_LIST_HEAD(&session->pending_list);
+
+	spin_lock_init(&session->cmnd_hash_lock);
+	for (i = 0; i < ARRAY_SIZE(session->cmnd_hash); i++)
+		INIT_LIST_HEAD(&session->cmnd_hash[i]);
+
+	session->next_ttt = 1;
+
+	nthread_init(session);
+	err = nthread_start(session);
+	if (err)
+		goto destroy_session;
+
+	return hostdata_session(shost->hostdata);
+
+destroy_session:
+	iscsi_transport_destroy_session(shost);
+	return NULL;
+}
+
+static void istgt_session_destroy(struct iscsi_cls_session *cls_session)
+{
+	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+	struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+	int i;
+
+	dprintk("%" PRIx64 "\n", session->sid);
+
+	if (!list_empty(&session->conn_list)) {
+		eprintk("%" PRIx64 " still have connections\n", session->sid);
+		BUG();
+	}
+
+	BUG_ON(!list_empty(&session->conn_list));
+
+	for (i = 0; i < ARRAY_SIZE(session->cmnd_hash); i++)
+		BUG_ON(!list_empty(&session->cmnd_hash[i]));
+
+	down(&session->target_sem);
+	up(&session->target_sem);
+
+	nthread_stop(session);
+	iscsi_transport_destroy_session(shost);
+}
+
+static int
+istgt_conn_get_param(struct iscsi_cls_conn *cls_conn,
+		     enum iscsi_param key, uint32_t *value)
+{
+	struct iscsi_conn *conn = cls_conn->dd_data;
+	struct iscsi_sess_param *param = &conn->session->param;
+
+	switch(key) {
+	case ISCSI_PARAM_MAX_RECV_DLENGTH:
+		*value = param->max_recv_data_length;
+		break;
+	case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+		*value = param->max_xmit_data_length;
+		break;
+	case ISCSI_PARAM_HDRDGST_EN:
+		*value = param->header_digest;
+		break;
+	case ISCSI_PARAM_DATADGST_EN:
+		*value = param->data_digest;
+		break;
+	default:
+		return ISCSI_ERR_PARAM_NOT_FOUND;
+	}
+
+	return 0;
+}
+
+static int
+istgt_session_get_param(struct iscsi_cls_session *cls_session,
+			enum iscsi_param key, uint32_t *value)
+{
+	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+	struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+	struct iscsi_sess_param *param = &session->param;
+
+	switch(key) {
+	case ISCSI_PARAM_INITIAL_R2T_EN:
+		*value = param->initial_r2t;
+		break;
+	case ISCSI_PARAM_MAX_R2T:
+		*value = param->max_outstanding_r2t;
+		break;
+	case ISCSI_PARAM_IMM_DATA_EN:
+		*value = param->immediate_data;
+		break;
+	case ISCSI_PARAM_FIRST_BURST:
+		*value = param->first_burst_length;
+		break;
+	case ISCSI_PARAM_MAX_BURST:
+		*value = param->max_burst_length;
+		break;
+	case ISCSI_PARAM_PDU_INORDER_EN:
+		*value = param->data_pdu_inorder;
+		break;
+	case ISCSI_PARAM_DATASEQ_INORDER_EN:
+		*value = param->data_sequence_inorder;
+		break;
+	case ISCSI_PARAM_ERL:
+		*value = param->error_recovery_level;
+		break;
+	case ISCSI_PARAM_IFMARKER_EN:
+		*value = param->ifmarker;
+		break;
+	case ISCSI_PARAM_OFMARKER_EN:
+		*value = param->ofmarker;
+		break;
+	default:
+		return ISCSI_ERR_PARAM_NOT_FOUND;
+	}
+
+	return 0;
+}
+
+static int
+istgt_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param key,
+		uint32_t value)
+{
+	struct iscsi_conn *conn = cls_conn->dd_data;
+	struct iscsi_session *session = conn->session;
+	struct iscsi_sess_param *param = &session->param;
+
+	switch(key) {
+	case ISCSI_PARAM_MAX_RECV_DLENGTH:
+		param->max_recv_data_length = value;
+		break;
+	case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+		param->max_xmit_data_length = value;
+		break;
+	case ISCSI_PARAM_HDRDGST_EN:
+		param->header_digest = value;
+		break;
+	case ISCSI_PARAM_DATADGST_EN:
+		param->data_digest = value;
+		break;
+	case ISCSI_PARAM_INITIAL_R2T_EN:
+		param->initial_r2t = value;
+		break;
+	case ISCSI_PARAM_MAX_R2T:
+		param->max_outstanding_r2t = value;
+		break;
+	case ISCSI_PARAM_IMM_DATA_EN:
+		param->immediate_data = value;
+		break;
+	case ISCSI_PARAM_FIRST_BURST:
+		param->first_burst_length = value;
+		break;
+	case ISCSI_PARAM_MAX_BURST:
+		param->max_burst_length = value;
+		break;
+	case ISCSI_PARAM_PDU_INORDER_EN:
+		param->data_pdu_inorder = value;
+		break;
+	case ISCSI_PARAM_DATASEQ_INORDER_EN:
+		param->data_sequence_inorder = value;
+		break;
+	case ISCSI_PARAM_ERL:
+		param->error_recovery_level = value;
+		break;
+	case ISCSI_PARAM_IFMARKER_EN:
+		param->ifmarker = value;
+		break;
+	case ISCSI_PARAM_OFMARKER_EN:
+		param->ofmarker = value;
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static struct scsi_host_template istgt_sht = {
+	.name			= THIS_NAME,
+	.module			= THIS_MODULE,
+	.can_queue		= DEFAULT_NR_QUEUED_CMNDS,
+	.sg_tablesize		= SG_ALL,
+	.use_clustering		= DISABLE_CLUSTERING,
+	.max_sectors		= 65536,
+	.transfer_response	= scsi_cmnd_done,
+	.transfer_data		= buffer_ready,
+};
+
+static struct iscsi_transport istgt_transport = {
+	.owner			= THIS_MODULE,
+	.name			= "tcp_tgt",
+	.host_template		= &istgt_sht,
+	.hostdata_size		= sizeof(struct iscsi_session),
+	.max_conn		= 1,
+	.max_cmd_len		= 16,
+	.create_session		= istgt_session_create,
+	.destroy_session	= istgt_session_destroy, 
+	.create_conn		= istgt_conn_create,
+	.destroy_conn		= istgt_conn_destroy,
+	.bind_conn		= istgt_conn_bind,
+	.start_conn		= istgt_conn_start,
+	.set_param		= istgt_set_param,
+	.get_session_param	= istgt_session_get_param,
+	.get_conn_param		= istgt_conn_get_param,
+
+};
+
+static void istgt_exit(void)
+{
+	kmem_cache_destroy(istgt_cmd_cache);
+	iscsi_unregister_transport(&istgt_transport);
+}
+
+static int istgt_init(void)
+{
+	printk("iSCSI Target Software for Linux Target Framework %s\n",
+	       VERSION_STRING);
+
+	istgt_cmd_cache = kmem_cache_create("istgt_cmd",
+					    sizeof(struct istgt_cmd),
+					    0, 0, NULL, NULL);
+	if (!istgt_cmd_cache)
+		return -ENOMEM;
+
+	if (!iscsi_register_transport(&istgt_transport))
+		goto free_cmd_cache;
+
+	return 0;
+
+free_cmd_cache:
+	kmem_cache_destroy(istgt_cmd_cache);
+	return -ENOMEM;
+
+}
+
+module_init(istgt_init);
+module_exit(istgt_exit);
+
+MODULE_LICENSE("GPL");


-
: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux