* [PATCH RFC 3/3] SCSI Userspace Target: example target driver
@ 2006-01-25 2:58 Mike Christie
0 siblings, 0 replies; only message in thread
From: Mike Christie @ 2006-01-25 2:58 UTC (permalink / raw)
To: linux-scsi
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@acm.org>
+ * Copyright (C) 2002 - 2003 Ardis Technolgies <roman@ardistech.com>
+ * 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");
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2006-01-25 2:58 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-01-25 2:58 [PATCH RFC 3/3] SCSI Userspace Target: example target driver Mike Christie
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).