SHA256
1
0
forked from pool/open-iscsi
open-iscsi/open-iscsi-779.diff

10547 lines
299 KiB
Diff

Index: kernel/2.6.16-18_compat.patch
===================================================================
--- kernel/2.6.16-18_compat.patch (revision 0)
+++ kernel/2.6.16-18_compat.patch (revision 779)
@@ -0,0 +1,136 @@
+diff -Nurp a/iscsi_2.6.20_compat.h b/iscsi_2.6.20_compat.h
+--- a/iscsi_2.6.20_compat.h 1970-01-01 00:00:00.000000000 +0200
++++ b/iscsi_2.6.20_compat.h 2007-02-22 18:21:22.000000000 +0200
+@@ -0,0 +1,47 @@
++
++#define CRYPTO_ALG_ASYNC 0x00000080
++struct hash_desc
++{
++ struct crypto_tfm *tfm;
++ u32 flags;
++};
++
++static inline int crypto_hash_init(struct hash_desc *desc)
++{
++ crypto_digest_init(desc->tfm);
++ return 0;
++}
++
++static inline int crypto_hash_digest(struct hash_desc *desc,
++ struct scatterlist *sg,
++ unsigned int nbytes, u8 *out)
++{
++ crypto_digest_digest(desc->tfm, sg, (nbytes+(PAGE_SIZE-1)) / PAGE_SIZE, out);
++ return nbytes;
++}
++
++static inline int crypto_hash_update(struct hash_desc *desc,
++ struct scatterlist *sg,
++ unsigned int nbytes)
++{
++ crypto_digest_update(desc->tfm, sg, (nbytes+(PAGE_SIZE-1)) / PAGE_SIZE);
++ return nbytes;
++}
++
++static inline int crypto_hash_final(struct hash_desc *desc, u8 *out)
++{
++ crypto_digest_final(desc->tfm, out);
++ return 0;
++}
++
++static inline struct crypto_tfm *crypto_alloc_hash(const char *alg_name,
++ u32 type, u32 mask)
++{
++ struct crypto_tfm *ret = crypto_alloc_tfm(alg_name ,type);
++ return ret ? ret : ERR_PTR(-ENOMEM);
++}
++
++static inline void crypto_free_hash(struct crypto_tfm *tfm)
++{
++ crypto_free_tfm(tfm);
++}
+diff -Nurp a/iscsi_tcp.h b/iscsi_tcp.h
+--- a/iscsi_tcp.h 2007-01-10 12:38:25.000000000 +0200
++++ b/iscsi_tcp.h 2007-01-10 12:38:25.000000000 +0200
+@@ -49,6 +49,7 @@
+ #define ISCSI_SG_TABLESIZE SG_ALL
+ #define ISCSI_TCP_MAX_CMD_LEN 16
+
++#include "iscsi_2.6.20_compat.h"
+ struct crypto_hash;
+ struct socket;
+
+diff -Nurp a/libiscsi.c b/libiscsi.c
+--- a/libiscsi.c 2007-02-21 11:57:49.000000000 +0200
++++ b/libiscsi.c 2007-02-21 11:57:49.000000000 +0200
+@@ -716,10 +716,9 @@ again:
+ return rc;
+ }
+
+-static void iscsi_xmitworker(struct work_struct *work)
++static void iscsi_xmitworker(void *data)
+ {
+- struct iscsi_conn *conn =
+- container_of(work, struct iscsi_conn, xmitwork);
++ struct iscsi_conn *conn = data;
+ int rc;
+ /*
+ * serialize Xmit worker on a per-connection basis.
+@@ -1509,7 +1508,7 @@ iscsi_conn_setup(struct iscsi_cls_sessio
+ if (conn->mgmtqueue == ERR_PTR(-ENOMEM))
+ goto mgmtqueue_alloc_fail;
+
+- INIT_WORK(&conn->xmitwork, iscsi_xmitworker);
++ INIT_WORK(&conn->xmitwork, iscsi_xmitworker, conn);
+
+ /* allocate login_mtask used for the login/text sequences */
+ spin_lock_bh(&session->lock);
+diff -Nurp a/libiscsi.h b/libiscsi.h
+--- a/libiscsi.h 2007-01-10 12:38:25.000000000 +0200
++++ b/libiscsi.h 2007-01-10 12:38:25.000000000 +0200
+@@ -25,8 +25,6 @@
+
+ #include <linux/types.h>
+ #include <linux/mutex.h>
+-#include <linux/timer.h>
+-#include <linux/workqueue.h>
+ #include "iscsi_proto.h"
+ #include "iscsi_if.h"
+
+diff -Nurp a/scsi_transport_iscsi.c b/scsi_transport_iscsi.c
+--- a/scsi_transport_iscsi.c 2007-01-10 12:38:25.000000000 +0200
++++ b/scsi_transport_iscsi.c 2007-01-10 12:38:25.000000000 +0200
+@@ -234,11 +234,9 @@ static int iscsi_user_scan(struct Scsi_H
+ return 0;
+ }
+
+-static void session_recovery_timedout(struct work_struct *work)
++static void session_recovery_timedout(void *data)
+ {
+- struct iscsi_cls_session *session =
+- container_of(work, struct iscsi_cls_session,
+- recovery_work.work);
++ struct iscsi_cls_session *session = data;
+
+ dev_printk(KERN_INFO, &session->dev, "iscsi: session recovery timed "
+ "out after %d secs\n", session->recovery_tmo);
+@@ -278,7 +276,7 @@ iscsi_alloc_session(struct Scsi_Host *sh
+
+ session->transport = transport;
+ session->recovery_tmo = 120;
+- INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);
++ INIT_WORK(&session->recovery_work, session_recovery_timedout, session);
+ INIT_LIST_HEAD(&session->host_list);
+ INIT_LIST_HEAD(&session->sess_list);
+
+diff -Nurp a/scsi_transport_iscsi.h b/scsi_transport_iscsi.h
+--- a/scsi_transport_iscsi.h 2007-01-10 12:38:26.000000000 +0200
++++ b/scsi_transport_iscsi.h 2007-01-10 12:38:25.000000000 +0200
+@@ -176,7 +176,7 @@ struct iscsi_cls_session {
+
+ /* recovery fields */
+ int recovery_tmo;
+- struct delayed_work recovery_work;
++ struct work_struct recovery_work;
+
+ int target_id;
Index: kernel/iscsi_tcp.c
===================================================================
--- kernel/iscsi_tcp.c (revision 0)
+++ kernel/iscsi_tcp.c (revision 779)
@@ -0,0 +1,2235 @@
+/*
+ * iSCSI Initiator over TCP/IP Data-Path
+ *
+ * Copyright (C) 2004 Dmitry Yusupov
+ * Copyright (C) 2004 Alex Aizman
+ * Copyright (C) 2005 - 2006 Mike Christie
+ * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
+ * maintained by open-iscsi@googlegroups.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * See the file COPYING included with this distribution for more details.
+ *
+ * Credits:
+ * Christoph Hellwig
+ * FUJITA Tomonori
+ * Arne Redlich
+ * Zhenyu Wang
+ */
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/inet.h>
+#include <linux/blkdev.h>
+#include <linux/crypto.h>
+#include <linux/delay.h>
+#include <linux/kfifo.h>
+#include <linux/scatterlist.h>
+#include <linux/mutex.h>
+#include <net/tcp.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi.h>
+#include "scsi_transport_iscsi.h"
+
+#include "iscsi_tcp.h"
+
+MODULE_AUTHOR("Dmitry Yusupov <dmitry_yus@yahoo.com>, "
+ "Alex Aizman <itn780@yahoo.com>");
+MODULE_DESCRIPTION("iSCSI/TCP data-path");
+MODULE_LICENSE("GPL");
+/* #define DEBUG_TCP */
+#define DEBUG_ASSERT
+
+#ifdef DEBUG_TCP
+#define debug_tcp(fmt...) printk(KERN_INFO "tcp: " fmt)
+#else
+#define debug_tcp(fmt...)
+#endif
+
+#ifndef DEBUG_ASSERT
+#ifdef BUG_ON
+#undef BUG_ON
+#endif
+#define BUG_ON(expr)
+#endif
+
+static unsigned int iscsi_max_lun = 512;
+module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
+
+static inline void
+iscsi_buf_init_iov(struct iscsi_buf *ibuf, char *vbuf, int size)
+{
+ ibuf->sg.page = virt_to_page(vbuf);
+ ibuf->sg.offset = offset_in_page(vbuf);
+ ibuf->sg.length = size;
+ ibuf->sent = 0;
+ ibuf->use_sendmsg = 1;
+}
+
+static inline void
+iscsi_buf_init_sg(struct iscsi_buf *ibuf, struct scatterlist *sg)
+{
+ ibuf->sg.page = sg->page;
+ ibuf->sg.offset = sg->offset;
+ ibuf->sg.length = sg->length;
+ /*
+ * Fastpath: sg element fits into single page
+ */
+ if (sg->length + sg->offset <= PAGE_SIZE && !PageSlab(sg->page))
+ ibuf->use_sendmsg = 0;
+ else
+ ibuf->use_sendmsg = 1;
+ ibuf->sent = 0;
+}
+
+static inline int
+iscsi_buf_left(struct iscsi_buf *ibuf)
+{
+ int rc;
+
+ rc = ibuf->sg.length - ibuf->sent;
+ BUG_ON(rc < 0);
+ return rc;
+}
+
+static inline void
+iscsi_hdr_digest(struct iscsi_conn *conn, struct iscsi_buf *buf,
+ u8* crc)
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+
+ crypto_hash_digest(&tcp_conn->tx_hash, &buf->sg, buf->sg.length, crc);
+ buf->sg.length = tcp_conn->hdr_size;
+}
+
+static inline int
+iscsi_hdr_extract(struct iscsi_tcp_conn *tcp_conn)
+{
+ struct sk_buff *skb = tcp_conn->in.skb;
+
+ tcp_conn->in.zero_copy_hdr = 0;
+
+ if (tcp_conn->in.copy >= tcp_conn->hdr_size &&
+ tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER) {
+ /*
+ * Zero-copy PDU Header: using connection context
+ * to store header pointer.
+ */
+ if (skb_shinfo(skb)->frag_list == NULL &&
+ !skb_shinfo(skb)->nr_frags) {
+ tcp_conn->in.hdr = (struct iscsi_hdr *)
+ ((char*)skb->data + tcp_conn->in.offset);
+ tcp_conn->in.zero_copy_hdr = 1;
+ } else {
+ /* ignoring return code since we checked
+ * in.copy before */
+ skb_copy_bits(skb, tcp_conn->in.offset,
+ &tcp_conn->hdr, tcp_conn->hdr_size);
+ tcp_conn->in.hdr = &tcp_conn->hdr;
+ }
+ tcp_conn->in.offset += tcp_conn->hdr_size;
+ tcp_conn->in.copy -= tcp_conn->hdr_size;
+ } else {
+ int hdr_remains;
+ int copylen;
+
+ /*
+ * PDU header scattered across SKB's,
+ * copying it... This'll happen quite rarely.
+ */
+
+ if (tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER)
+ tcp_conn->in.hdr_offset = 0;
+
+ hdr_remains = tcp_conn->hdr_size - tcp_conn->in.hdr_offset;
+ BUG_ON(hdr_remains <= 0);
+
+ copylen = min(tcp_conn->in.copy, hdr_remains);
+ skb_copy_bits(skb, tcp_conn->in.offset,
+ (char*)&tcp_conn->hdr + tcp_conn->in.hdr_offset,
+ copylen);
+
+ debug_tcp("PDU gather offset %d bytes %d in.offset %d "
+ "in.copy %d\n", tcp_conn->in.hdr_offset, copylen,
+ tcp_conn->in.offset, tcp_conn->in.copy);
+
+ tcp_conn->in.offset += copylen;
+ tcp_conn->in.copy -= copylen;
+ if (copylen < hdr_remains) {
+ tcp_conn->in_progress = IN_PROGRESS_HEADER_GATHER;
+ tcp_conn->in.hdr_offset += copylen;
+ return -EAGAIN;
+ }
+ tcp_conn->in.hdr = &tcp_conn->hdr;
+ tcp_conn->discontiguous_hdr_cnt++;
+ tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
+ }
+
+ return 0;
+}
+
+/*
+ * must be called with session lock
+ */
+static void
+iscsi_tcp_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ struct iscsi_r2t_info *r2t;
+ struct scsi_cmnd *sc;
+
+ /* flush ctask's r2t queues */
+ while (__kfifo_get(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*))) {
+ __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+ sizeof(void*));
+ debug_scsi("iscsi_tcp_cleanup_ctask pending r2t dropped\n");
+ }
+
+ sc = ctask->sc;
+ if (unlikely(!sc))
+ return;
+
+ tcp_ctask->xmstate = XMSTATE_IDLE;
+ tcp_ctask->r2t = NULL;
+}
+
+/**
+ * iscsi_data_rsp - SCSI Data-In Response processing
+ * @conn: iscsi connection
+ * @ctask: scsi command task
+ **/
+static int
+iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+{
+ int rc;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr;
+ struct iscsi_session *session = conn->session;
+ int datasn = be32_to_cpu(rhdr->datasn);
+
+ rc = iscsi_check_assign_cmdsn(session, (struct iscsi_nopin*)rhdr);
+ if (rc)
+ return rc;
+ /*
+ * setup Data-In byte counter (gets decremented..)
+ */
+ ctask->data_count = tcp_conn->in.datalen;
+
+ if (tcp_conn->in.datalen == 0)
+ return 0;
+
+ if (ctask->datasn != datasn)
+ return ISCSI_ERR_DATASN;
+
+ ctask->datasn++;
+
+ tcp_ctask->data_offset = be32_to_cpu(rhdr->offset);
+ if (tcp_ctask->data_offset + tcp_conn->in.datalen > ctask->total_length)
+ return ISCSI_ERR_DATA_OFFSET;
+
+ if (rhdr->flags & ISCSI_FLAG_DATA_STATUS) {
+ struct scsi_cmnd *sc = ctask->sc;
+
+ conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1;
+ if (rhdr->flags & ISCSI_FLAG_DATA_UNDERFLOW) {
+ int res_count = be32_to_cpu(rhdr->residual_count);
+
+ if (res_count > 0 &&
+ res_count <= sc->request_bufflen) {
+ sc->resid = res_count;
+ sc->result = (DID_OK << 16) | rhdr->cmd_status;
+ } else
+ sc->result = (DID_BAD_TARGET << 16) |
+ rhdr->cmd_status;
+ } else if (rhdr->flags & ISCSI_FLAG_DATA_OVERFLOW) {
+ sc->resid = be32_to_cpu(rhdr->residual_count);
+ sc->result = (DID_OK << 16) | rhdr->cmd_status;
+ } else
+ sc->result = (DID_OK << 16) | rhdr->cmd_status;
+ }
+
+ conn->datain_pdus_cnt++;
+ return 0;
+}
+
+/**
+ * iscsi_solicit_data_init - initialize first Data-Out
+ * @conn: iscsi connection
+ * @ctask: scsi command task
+ * @r2t: R2T info
+ *
+ * Notes:
+ * Initialize first Data-Out within this R2T sequence and finds
+ * proper data_offset within this SCSI command.
+ *
+ * This function is called with connection lock taken.
+ **/
+static void
+iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+ struct iscsi_r2t_info *r2t)
+{
+ struct iscsi_data *hdr;
+ struct scsi_cmnd *sc = ctask->sc;
+
+ hdr = &r2t->dtask.hdr;
+ memset(hdr, 0, sizeof(struct iscsi_data));
+ hdr->ttt = r2t->ttt;
+ hdr->datasn = cpu_to_be32(r2t->solicit_datasn);
+ r2t->solicit_datasn++;
+ hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
+ memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun));
+ hdr->itt = ctask->hdr->itt;
+ hdr->exp_statsn = r2t->exp_statsn;
+ hdr->offset = cpu_to_be32(r2t->data_offset);
+ if (r2t->data_length > conn->max_xmit_dlength) {
+ hton24(hdr->dlength, conn->max_xmit_dlength);
+ r2t->data_count = conn->max_xmit_dlength;
+ hdr->flags = 0;
+ } else {
+ hton24(hdr->dlength, r2t->data_length);
+ r2t->data_count = r2t->data_length;
+ hdr->flags = ISCSI_FLAG_CMD_FINAL;
+ }
+ conn->dataout_pdus_cnt++;
+
+ r2t->sent = 0;
+
+ iscsi_buf_init_iov(&r2t->headbuf, (char*)hdr,
+ sizeof(struct iscsi_hdr));
+
+ if (sc->use_sg) {
+ int i, sg_count = 0;
+ struct scatterlist *sg = sc->request_buffer;
+
+ r2t->sg = NULL;
+ for (i = 0; i < sc->use_sg; i++, sg += 1) {
+ /* FIXME: prefetch ? */
+ if (sg_count + sg->length > r2t->data_offset) {
+ int page_offset;
+
+ /* sg page found! */
+
+ /* offset within this page */
+ page_offset = r2t->data_offset - sg_count;
+
+ /* fill in this buffer */
+ iscsi_buf_init_sg(&r2t->sendbuf, sg);
+ r2t->sendbuf.sg.offset += page_offset;
+ r2t->sendbuf.sg.length -= page_offset;
+
+ /* xmit logic will continue with next one */
+ r2t->sg = sg + 1;
+ break;
+ }
+ sg_count += sg->length;
+ }
+ BUG_ON(r2t->sg == NULL);
+ } else {
+ iscsi_buf_init_iov(&r2t->sendbuf,
+ (char*)sc->request_buffer + r2t->data_offset,
+ r2t->data_count);
+ r2t->sg = NULL;
+ }
+}
+
+/**
+ * iscsi_r2t_rsp - iSCSI R2T Response processing
+ * @conn: iscsi connection
+ * @ctask: scsi command task
+ **/
+static int
+iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_r2t_info *r2t;
+ struct iscsi_session *session = conn->session;
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_r2t_rsp *rhdr = (struct iscsi_r2t_rsp *)tcp_conn->in.hdr;
+ int r2tsn = be32_to_cpu(rhdr->r2tsn);
+ int rc;
+
+ if (tcp_conn->in.datalen) {
+ printk(KERN_ERR "iscsi_tcp: invalid R2t with datalen %d\n",
+ tcp_conn->in.datalen);
+ return ISCSI_ERR_DATALEN;
+ }
+
+ if (tcp_ctask->exp_r2tsn && tcp_ctask->exp_r2tsn != r2tsn)
+ return ISCSI_ERR_R2TSN;
+
+ rc = iscsi_check_assign_cmdsn(session, (struct iscsi_nopin*)rhdr);
+ if (rc)
+ return rc;
+
+ /* FIXME: use R2TSN to detect missing R2T */
+
+ /* fill-in new R2T associated with the task */
+ spin_lock(&session->lock);
+ if (!ctask->sc || ctask->mtask ||
+ session->state != ISCSI_STATE_LOGGED_IN) {
+ printk(KERN_INFO "iscsi_tcp: dropping R2T itt %d in "
+ "recovery...\n", ctask->itt);
+ spin_unlock(&session->lock);
+ return 0;
+ }
+
+ rc = __kfifo_get(tcp_ctask->r2tpool.queue, (void*)&r2t, sizeof(void*));
+ BUG_ON(!rc);
+
+ r2t->exp_statsn = rhdr->statsn;
+ r2t->data_length = be32_to_cpu(rhdr->data_length);
+ if (r2t->data_length == 0) {
+ printk(KERN_ERR "iscsi_tcp: invalid R2T with zero data len\n");
+ spin_unlock(&session->lock);
+ return ISCSI_ERR_DATALEN;
+ }
+
+ if (r2t->data_length > session->max_burst)
+ debug_scsi("invalid R2T with data len %u and max burst %u."
+ "Attempting to execute request.\n",
+ r2t->data_length, session->max_burst);
+
+ r2t->data_offset = be32_to_cpu(rhdr->data_offset);
+ if (r2t->data_offset + r2t->data_length > ctask->total_length) {
+ spin_unlock(&session->lock);
+ printk(KERN_ERR "iscsi_tcp: invalid R2T with data len %u at "
+ "offset %u and total length %d\n", r2t->data_length,
+ r2t->data_offset, ctask->total_length);
+ return ISCSI_ERR_DATALEN;
+ }
+
+ r2t->ttt = rhdr->ttt; /* no flip */
+ r2t->solicit_datasn = 0;
+
+ iscsi_solicit_data_init(conn, ctask, r2t);
+
+ tcp_ctask->exp_r2tsn = r2tsn + 1;
+ __kfifo_put(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*));
+ tcp_ctask->xmstate |= XMSTATE_SOL_HDR;
+ list_move_tail(&ctask->running, &conn->xmitqueue);
+
+ scsi_queue_work(session->host, &conn->xmitwork);
+ conn->r2t_pdus_cnt++;
+ spin_unlock(&session->lock);
+
+ return 0;
+}
+
+static int
+iscsi_tcp_hdr_recv(struct iscsi_conn *conn)
+{
+ int rc = 0, opcode, ahslen;
+ struct iscsi_hdr *hdr;
+ struct iscsi_session *session = conn->session;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ uint32_t cdgst, rdgst = 0, itt;
+
+ hdr = tcp_conn->in.hdr;
+
+ /* verify PDU length */
+ tcp_conn->in.datalen = ntoh24(hdr->dlength);
+ if (tcp_conn->in.datalen > conn->max_recv_dlength) {
+ printk(KERN_ERR "iscsi_tcp: datalen %d > %d\n",
+ tcp_conn->in.datalen, conn->max_recv_dlength);
+ return ISCSI_ERR_DATALEN;
+ }
+ tcp_conn->data_copied = 0;
+
+ /* read AHS */
+ ahslen = hdr->hlength << 2;
+ tcp_conn->in.offset += ahslen;
+ tcp_conn->in.copy -= ahslen;
+ if (tcp_conn->in.copy < 0) {
+ printk(KERN_ERR "iscsi_tcp: can't handle AHS with length "
+ "%d bytes\n", ahslen);
+ return ISCSI_ERR_AHSLEN;
+ }
+
+ /* calculate read padding */
+ tcp_conn->in.padding = tcp_conn->in.datalen & (ISCSI_PAD_LEN-1);
+ if (tcp_conn->in.padding) {
+ tcp_conn->in.padding = ISCSI_PAD_LEN - tcp_conn->in.padding;
+ debug_scsi("read padding %d bytes\n", tcp_conn->in.padding);
+ }
+
+ if (conn->hdrdgst_en) {
+ struct scatterlist sg;
+
+ sg_init_one(&sg, (u8 *)hdr,
+ sizeof(struct iscsi_hdr) + ahslen);
+ crypto_hash_digest(&tcp_conn->rx_hash, &sg, sg.length,
+ (u8 *)&cdgst);
+ rdgst = *(uint32_t*)((char*)hdr + sizeof(struct iscsi_hdr) +
+ ahslen);
+ if (cdgst != rdgst) {
+ printk(KERN_ERR "iscsi_tcp: hdrdgst error "
+ "recv 0x%x calc 0x%x\n", rdgst, cdgst);
+ return ISCSI_ERR_HDR_DGST;
+ }
+ }
+
+ opcode = hdr->opcode & ISCSI_OPCODE_MASK;
+ /* verify itt (itt encoding: age+cid+itt) */
+ rc = iscsi_verify_itt(conn, hdr, &itt);
+ if (rc == ISCSI_ERR_NO_SCSI_CMD) {
+ tcp_conn->in.datalen = 0; /* force drop */
+ return 0;
+ } else if (rc)
+ return rc;
+
+ debug_tcp("opcode 0x%x offset %d copy %d ahslen %d datalen %d\n",
+ opcode, tcp_conn->in.offset, tcp_conn->in.copy,
+ ahslen, tcp_conn->in.datalen);
+
+ switch(opcode) {
+ case ISCSI_OP_SCSI_DATA_IN:
+ tcp_conn->in.ctask = session->cmds[itt];
+ rc = iscsi_data_rsp(conn, tcp_conn->in.ctask);
+ if (rc)
+ return rc;
+ /* fall through */
+ case ISCSI_OP_SCSI_CMD_RSP:
+ tcp_conn->in.ctask = session->cmds[itt];
+ if (tcp_conn->in.datalen)
+ goto copy_hdr;
+
+ spin_lock(&session->lock);
+ rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
+ spin_unlock(&session->lock);
+ break;
+ case ISCSI_OP_R2T:
+ tcp_conn->in.ctask = session->cmds[itt];
+ if (ahslen)
+ rc = ISCSI_ERR_AHSLEN;
+ else if (tcp_conn->in.ctask->sc->sc_data_direction ==
+ DMA_TO_DEVICE)
+ rc = iscsi_r2t_rsp(conn, tcp_conn->in.ctask);
+ else
+ rc = ISCSI_ERR_PROTO;
+ break;
+ case ISCSI_OP_LOGIN_RSP:
+ case ISCSI_OP_TEXT_RSP:
+ case ISCSI_OP_REJECT:
+ case ISCSI_OP_ASYNC_EVENT:
+ /*
+ * It is possible that we could get a PDU with a buffer larger
+ * than 8K, but there are no targets that currently do this.
+ * For now we fail until we find a vendor that needs it
+ */
+ if (ISCSI_DEF_MAX_RECV_SEG_LEN <
+ tcp_conn->in.datalen) {
+ printk(KERN_ERR "iscsi_tcp: received buffer of len %u "
+ "but conn buffer is only %u (opcode %0x)\n",
+ tcp_conn->in.datalen,
+ ISCSI_DEF_MAX_RECV_SEG_LEN, opcode);
+ rc = ISCSI_ERR_PROTO;
+ break;
+ }
+
+ if (tcp_conn->in.datalen)
+ goto copy_hdr;
+ /* fall through */
+ case ISCSI_OP_LOGOUT_RSP:
+ case ISCSI_OP_NOOP_IN:
+ case ISCSI_OP_SCSI_TMFUNC_RSP:
+ rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
+ break;
+ default:
+ rc = ISCSI_ERR_BAD_OPCODE;
+ break;
+ }
+
+ return rc;
+
+copy_hdr:
+ /*
+ * if we did zero copy for the header but we will need multiple
+ * skbs to complete the command then we have to copy the header
+ * for later use
+ */
+ if (tcp_conn->in.zero_copy_hdr && tcp_conn->in.copy <=
+ (tcp_conn->in.datalen + tcp_conn->in.padding +
+ (conn->datadgst_en ? 4 : 0))) {
+ debug_tcp("Copying header for later use. in.copy %d in.datalen"
+ " %d\n", tcp_conn->in.copy, tcp_conn->in.datalen);
+ memcpy(&tcp_conn->hdr, tcp_conn->in.hdr,
+ sizeof(struct iscsi_hdr));
+ tcp_conn->in.hdr = &tcp_conn->hdr;
+ tcp_conn->in.zero_copy_hdr = 0;
+ }
+ return 0;
+}
+
+/**
+ * iscsi_ctask_copy - copy skb bits to the destanation cmd task
+ * @conn: iscsi tcp connection
+ * @ctask: scsi command task
+ * @buf: buffer to copy to
+ * @buf_size: size of buffer
+ * @offset: offset within the buffer
+ *
+ * Notes:
+ * The function calls skb_copy_bits() and updates per-connection and
+ * per-cmd byte counters.
+ *
+ * Read counters (in bytes):
+ *
+ * conn->in.offset offset within in progress SKB
+ * conn->in.copy left to copy from in progress SKB
+ * including padding
+ * conn->in.copied copied already from in progress SKB
+ * conn->data_copied copied already from in progress buffer
+ * ctask->sent total bytes sent up to the MidLayer
+ * ctask->data_count left to copy from in progress Data-In
+ * buf_left left to copy from in progress buffer
+ **/
+static inline int
+iscsi_ctask_copy(struct iscsi_tcp_conn *tcp_conn, struct iscsi_cmd_task *ctask,
+ void *buf, int buf_size, int offset)
+{
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ int buf_left = buf_size - (tcp_conn->data_copied + offset);
+ int size = min(tcp_conn->in.copy, buf_left);
+ int rc;
+
+ size = min(size, ctask->data_count);
+
+ debug_tcp("ctask_copy %d bytes at offset %d copied %d\n",
+ size, tcp_conn->in.offset, tcp_conn->in.copied);
+
+ BUG_ON(size <= 0);
+ BUG_ON(tcp_ctask->sent + size > ctask->total_length);
+
+ rc = skb_copy_bits(tcp_conn->in.skb, tcp_conn->in.offset,
+ (char*)buf + (offset + tcp_conn->data_copied), size);
+ /* must fit into skb->len */
+ BUG_ON(rc);
+
+ tcp_conn->in.offset += size;
+ tcp_conn->in.copy -= size;
+ tcp_conn->in.copied += size;
+ tcp_conn->data_copied += size;
+ tcp_ctask->sent += size;
+ ctask->data_count -= size;
+
+ BUG_ON(tcp_conn->in.copy < 0);
+ BUG_ON(ctask->data_count < 0);
+
+ if (buf_size != (tcp_conn->data_copied + offset)) {
+ if (!ctask->data_count) {
+ BUG_ON(buf_size - tcp_conn->data_copied < 0);
+ /* done with this PDU */
+ return buf_size - tcp_conn->data_copied;
+ }
+ return -EAGAIN;
+ }
+
+ /* done with this buffer or with both - PDU and buffer */
+ tcp_conn->data_copied = 0;
+ return 0;
+}
+
+/**
+ * iscsi_tcp_copy - copy skb bits to the destanation buffer
+ * @conn: iscsi tcp connection
+ *
+ * Notes:
+ * The function calls skb_copy_bits() and updates per-connection
+ * byte counters.
+ **/
+static inline int
+iscsi_tcp_copy(struct iscsi_conn *conn, int buf_size)
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ int buf_left = buf_size - tcp_conn->data_copied;
+ int size = min(tcp_conn->in.copy, buf_left);
+ int rc;
+
+ debug_tcp("tcp_copy %d bytes at offset %d copied %d\n",
+ size, tcp_conn->in.offset, tcp_conn->data_copied);
+ BUG_ON(size <= 0);
+
+ rc = skb_copy_bits(tcp_conn->in.skb, tcp_conn->in.offset,
+ (char*)conn->data + tcp_conn->data_copied, size);
+ BUG_ON(rc);
+
+ tcp_conn->in.offset += size;
+ tcp_conn->in.copy -= size;
+ tcp_conn->in.copied += size;
+ tcp_conn->data_copied += size;
+
+ if (buf_size != tcp_conn->data_copied)
+ return -EAGAIN;
+
+ return 0;
+}
+
+static inline void
+partial_sg_digest_update(struct hash_desc *desc, struct scatterlist *sg,
+ int offset, int length)
+{
+ struct scatterlist temp;
+
+ memcpy(&temp, sg, sizeof(struct scatterlist));
+ temp.offset = offset;
+ temp.length = length;
+ crypto_hash_update(desc, &temp, length);
+}
+
+static void
+iscsi_recv_digest_update(struct iscsi_tcp_conn *tcp_conn, char* buf, int len)
+{
+ struct scatterlist tmp;
+
+ sg_init_one(&tmp, buf, len);
+ crypto_hash_update(&tcp_conn->rx_hash, &tmp, len);
+}
+
+static int iscsi_scsi_data_in(struct iscsi_conn *conn)
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_cmd_task *ctask = tcp_conn->in.ctask;
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ struct scsi_cmnd *sc = ctask->sc;
+ struct scatterlist *sg;
+ int i, offset, rc = 0;
+
+ BUG_ON((void*)ctask != sc->SCp.ptr);
+
+ /*
+ * copying Data-In into the Scsi_Cmnd
+ */
+ if (!sc->use_sg) {
+ i = ctask->data_count;
+ rc = iscsi_ctask_copy(tcp_conn, ctask, sc->request_buffer,
+ sc->request_bufflen,
+ tcp_ctask->data_offset);
+ if (rc == -EAGAIN)
+ return rc;
+ if (conn->datadgst_en)
+ iscsi_recv_digest_update(tcp_conn, sc->request_buffer,
+ i);
+ rc = 0;
+ goto done;
+ }
+
+ offset = tcp_ctask->data_offset;
+ sg = sc->request_buffer;
+
+ if (tcp_ctask->data_offset)
+ for (i = 0; i < tcp_ctask->sg_count; i++)
+ offset -= sg[i].length;
+ /* we've passed through partial sg*/
+ if (offset < 0)
+ offset = 0;
+
+ for (i = tcp_ctask->sg_count; i < sc->use_sg; i++) {
+ char *dest;
+
+ dest = kmap_atomic(sg[i].page, KM_SOFTIRQ0);
+ rc = iscsi_ctask_copy(tcp_conn, ctask, dest + sg[i].offset,
+ sg[i].length, offset);
+ kunmap_atomic(dest, KM_SOFTIRQ0);
+ if (rc == -EAGAIN)
+ /* continue with the next SKB/PDU */
+ return rc;
+ if (!rc) {
+ if (conn->datadgst_en) {
+ if (!offset)
+ crypto_hash_update(
+ &tcp_conn->rx_hash,
+ &sg[i], sg[i].length);
+ else
+ partial_sg_digest_update(
+ &tcp_conn->rx_hash,
+ &sg[i],
+ sg[i].offset + offset,
+ sg[i].length - offset);
+ }
+ offset = 0;
+ tcp_ctask->sg_count++;
+ }
+
+ if (!ctask->data_count) {
+ if (rc && conn->datadgst_en)
+ /*
+ * data-in is complete, but buffer not...
+ */
+ partial_sg_digest_update(&tcp_conn->rx_hash,
+ &sg[i],
+ sg[i].offset,
+ sg[i].length-rc);
+ rc = 0;
+ break;
+ }
+
+ if (!tcp_conn->in.copy)
+ return -EAGAIN;
+ }
+ BUG_ON(ctask->data_count);
+
+done:
+ /* check for non-exceptional status */
+ if (tcp_conn->in.hdr->flags & ISCSI_FLAG_DATA_STATUS) {
+ debug_scsi("done [sc %lx res %d itt 0x%x flags 0x%x]\n",
+ (long)sc, sc->result, ctask->itt,
+ tcp_conn->in.hdr->flags);
+ spin_lock(&conn->session->lock);
+ __iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0);
+ spin_unlock(&conn->session->lock);
+ }
+
+ return rc;
+}
+
+static int
+iscsi_data_recv(struct iscsi_conn *conn)
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ int rc = 0, opcode;
+
+ opcode = tcp_conn->in.hdr->opcode & ISCSI_OPCODE_MASK;
+ switch (opcode) {
+ case ISCSI_OP_SCSI_DATA_IN:
+ rc = iscsi_scsi_data_in(conn);
+ break;
+ case ISCSI_OP_SCSI_CMD_RSP:
+ case ISCSI_OP_TEXT_RSP:
+ case ISCSI_OP_LOGIN_RSP:
+ case ISCSI_OP_ASYNC_EVENT:
+ case ISCSI_OP_REJECT:
+ /*
+ * Collect data segment to the connection's data
+ * placeholder
+ */
+ if (iscsi_tcp_copy(conn, tcp_conn->in.datalen)) {
+ rc = -EAGAIN;
+ goto exit;
+ }
+
+ rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, conn->data,
+ tcp_conn->in.datalen);
+ if (!rc && conn->datadgst_en && opcode != ISCSI_OP_LOGIN_RSP)
+ iscsi_recv_digest_update(tcp_conn, conn->data,
+ tcp_conn->in.datalen);
+ break;
+ default:
+ BUG_ON(1);
+ }
+exit:
+ return rc;
+}
+
+/**
+ * iscsi_tcp_data_recv - TCP receive in sendfile fashion
+ * @rd_desc: read descriptor
+ * @skb: socket buffer
+ * @offset: offset in skb
+ * @len: skb->len - offset
+ **/
+static int
+iscsi_tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
+ unsigned int offset, size_t len)
+{
+ int rc;
+ struct iscsi_conn *conn = rd_desc->arg.data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ int processed;
+ char pad[ISCSI_PAD_LEN];
+ struct scatterlist sg;
+
+ /*
+ * Save current SKB and its offset in the corresponding
+ * connection context.
+ */
+ tcp_conn->in.copy = skb->len - offset;
+ tcp_conn->in.offset = offset;
+ tcp_conn->in.skb = skb;
+ tcp_conn->in.len = tcp_conn->in.copy;
+ BUG_ON(tcp_conn->in.copy <= 0);
+ debug_tcp("in %d bytes\n", tcp_conn->in.copy);
+
+more:
+ tcp_conn->in.copied = 0;
+ rc = 0;
+
+ if (unlikely(conn->suspend_rx)) {
+ debug_tcp("conn %d Rx suspended!\n", conn->id);
+ return 0;
+ }
+
+ if (tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER ||
+ tcp_conn->in_progress == IN_PROGRESS_HEADER_GATHER) {
+ rc = iscsi_hdr_extract(tcp_conn);
+ if (rc) {
+ if (rc == -EAGAIN)
+ goto nomore;
+ else {
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ return 0;
+ }
+ }
+
+ /*
+ * Verify and process incoming PDU header.
+ */
+ rc = iscsi_tcp_hdr_recv(conn);
+ if (!rc && tcp_conn->in.datalen) {
+ if (conn->datadgst_en)
+ crypto_hash_init(&tcp_conn->rx_hash);
+ tcp_conn->in_progress = IN_PROGRESS_DATA_RECV;
+ } else if (rc) {
+ iscsi_conn_failure(conn, rc);
+ return 0;
+ }
+ }
+
+ if (tcp_conn->in_progress == IN_PROGRESS_DDIGEST_RECV) {
+ uint32_t recv_digest;
+
+ debug_tcp("extra data_recv offset %d copy %d\n",
+ tcp_conn->in.offset, tcp_conn->in.copy);
+ rc = iscsi_tcp_copy(conn, sizeof(uint32_t));
+ if (rc) {
+ if (rc == -EAGAIN)
+ goto again;
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ return 0;
+ }
+
+ memcpy(&recv_digest, conn->data, sizeof(uint32_t));
+ if (recv_digest != tcp_conn->in.datadgst) {
+ debug_tcp("iscsi_tcp: data digest error!"
+ "0x%x != 0x%x\n", recv_digest,
+ tcp_conn->in.datadgst);
+ iscsi_conn_failure(conn, ISCSI_ERR_DATA_DGST);
+ return 0;
+ } else {
+ debug_tcp("iscsi_tcp: data digest match!"
+ "0x%x == 0x%x\n", recv_digest,
+ tcp_conn->in.datadgst);
+ tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
+ }
+ }
+
+ if (tcp_conn->in_progress == IN_PROGRESS_DATA_RECV &&
+ tcp_conn->in.copy) {
+
+ debug_tcp("data_recv offset %d copy %d\n",
+ tcp_conn->in.offset, tcp_conn->in.copy);
+
+ rc = iscsi_data_recv(conn);
+ if (rc) {
+ if (rc == -EAGAIN)
+ goto again;
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ return 0;
+ }
+ tcp_conn->in.copy -= tcp_conn->in.padding;
+ tcp_conn->in.offset += tcp_conn->in.padding;
+ if (conn->datadgst_en) {
+ if (tcp_conn->in.padding) {
+ debug_tcp("padding -> %d\n",
+ tcp_conn->in.padding);
+ memset(pad, 0, tcp_conn->in.padding);
+ sg_init_one(&sg, pad, tcp_conn->in.padding);
+ crypto_hash_update(&tcp_conn->rx_hash,
+ &sg, sg.length);
+ }
+ crypto_hash_final(&tcp_conn->rx_hash,
+ (u8 *) &tcp_conn->in.datadgst);
+ debug_tcp("rx digest 0x%x\n", tcp_conn->in.datadgst);
+ tcp_conn->in_progress = IN_PROGRESS_DDIGEST_RECV;
+ tcp_conn->data_copied = 0;
+ } else
+ tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
+ }
+
+ debug_tcp("f, processed %d from out of %d padding %d\n",
+ tcp_conn->in.offset - offset, (int)len, tcp_conn->in.padding);
+ BUG_ON(tcp_conn->in.offset - offset > len);
+
+ if (tcp_conn->in.offset - offset != len) {
+ debug_tcp("continue to process %d bytes\n",
+ (int)len - (tcp_conn->in.offset - offset));
+ goto more;
+ }
+
+nomore:
+ processed = tcp_conn->in.offset - offset;
+ BUG_ON(processed == 0);
+ return processed;
+
+again:
+ processed = tcp_conn->in.offset - offset;
+ debug_tcp("c, processed %d from out of %d rd_desc_cnt %d\n",
+ processed, (int)len, (int)rd_desc->count);
+ BUG_ON(processed == 0);
+ BUG_ON(processed > len);
+
+ conn->rxdata_octets += processed;
+ return processed;
+}
+
+static void
+iscsi_tcp_data_ready(struct sock *sk, int flag)
+{
+ struct iscsi_conn *conn = sk->sk_user_data;
+ read_descriptor_t rd_desc;
+
+ read_lock(&sk->sk_callback_lock);
+
+ /*
+ * Use rd_desc to pass 'conn' to iscsi_tcp_data_recv.
+ * We set count to 1 because we want the network layer to
+ * hand us all the skbs that are available. iscsi_tcp_data_recv
+ * handled pdus that cross buffers or pdus that still need data.
+ */
+ rd_desc.arg.data = conn;
+ rd_desc.count = 1;
+ tcp_read_sock(sk, &rd_desc, iscsi_tcp_data_recv);
+
+ read_unlock(&sk->sk_callback_lock);
+}
+
+static void
+iscsi_tcp_state_change(struct sock *sk)
+{
+ struct iscsi_tcp_conn *tcp_conn;
+ struct iscsi_conn *conn;
+ struct iscsi_session *session;
+ void (*old_state_change)(struct sock *);
+
+ read_lock(&sk->sk_callback_lock);
+
+ conn = (struct iscsi_conn*)sk->sk_user_data;
+ session = conn->session;
+
+ if ((sk->sk_state == TCP_CLOSE_WAIT ||
+ sk->sk_state == TCP_CLOSE) &&
+ !atomic_read(&sk->sk_rmem_alloc)) {
+ debug_tcp("iscsi_tcp_state_change: TCP_CLOSE|TCP_CLOSE_WAIT\n");
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ }
+
+ tcp_conn = conn->dd_data;
+ old_state_change = tcp_conn->old_state_change;
+
+ read_unlock(&sk->sk_callback_lock);
+
+ old_state_change(sk);
+}
+
+/**
+ * iscsi_write_space - Called when more output buffer space is available
+ * @sk: socket space is available for
+ **/
+static void
+iscsi_write_space(struct sock *sk)
+{
+ struct iscsi_conn *conn = (struct iscsi_conn*)sk->sk_user_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+
+ tcp_conn->old_write_space(sk);
+ debug_tcp("iscsi_write_space: cid %d\n", conn->id);
+ scsi_queue_work(conn->session->host, &conn->xmitwork);
+}
+
+static void
+iscsi_conn_set_callbacks(struct iscsi_conn *conn)
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct sock *sk = tcp_conn->sock->sk;
+
+ /* assign new callbacks */
+ write_lock_bh(&sk->sk_callback_lock);
+ sk->sk_user_data = conn;
+ tcp_conn->old_data_ready = sk->sk_data_ready;
+ tcp_conn->old_state_change = sk->sk_state_change;
+ tcp_conn->old_write_space = sk->sk_write_space;
+ sk->sk_data_ready = iscsi_tcp_data_ready;
+ sk->sk_state_change = iscsi_tcp_state_change;
+ sk->sk_write_space = iscsi_write_space;
+ write_unlock_bh(&sk->sk_callback_lock);
+}
+
+static void
+iscsi_conn_restore_callbacks(struct iscsi_tcp_conn *tcp_conn)
+{
+ struct sock *sk = tcp_conn->sock->sk;
+
+ /* restore socket callbacks, see also: iscsi_conn_set_callbacks() */
+ write_lock_bh(&sk->sk_callback_lock);
+ sk->sk_user_data = NULL;
+ sk->sk_data_ready = tcp_conn->old_data_ready;
+ sk->sk_state_change = tcp_conn->old_state_change;
+ sk->sk_write_space = tcp_conn->old_write_space;
+ sk->sk_no_check = 0;
+ write_unlock_bh(&sk->sk_callback_lock);
+}
+
+/**
+ * iscsi_send - generic send routine
+ * @sk: kernel's socket
+ * @buf: buffer to write from
+ * @size: actual size to write
+ * @flags: socket's flags
+ */
+static inline int
+iscsi_send(struct iscsi_conn *conn, struct iscsi_buf *buf, int size, int flags)
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct socket *sk = tcp_conn->sock;
+ int offset = buf->sg.offset + buf->sent, res;
+
+ /*
+ * if we got use_sg=0 or are sending something we kmallocd
+ * then we did not have to do kmap (kmap returns page_address)
+ *
+ * if we got use_sg > 0, but had to drop down, we do not
+ * set clustering so this should only happen for that
+ * slab case.
+ */
+ if (buf->use_sendmsg)
+ res = sock_no_sendpage(sk, buf->sg.page, offset, size, flags);
+ else
+ res = tcp_conn->sendpage(sk, buf->sg.page, offset, size, flags);
+
+ if (res >= 0) {
+ conn->txdata_octets += res;
+ buf->sent += res;
+ return res;
+ }
+
+ tcp_conn->sendpage_failures_cnt++;
+ if (res == -EAGAIN)
+ res = -ENOBUFS;
+ else
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ return res;
+}
+
+/**
+ * iscsi_sendhdr - send PDU Header via tcp_sendpage()
+ * @conn: iscsi connection
+ * @buf: buffer to write from
+ * @datalen: lenght of data to be sent after the header
+ *
+ * Notes:
+ * (Tx, Fast Path)
+ **/
+static inline int
+iscsi_sendhdr(struct iscsi_conn *conn, struct iscsi_buf *buf, int datalen)
+{
+ int flags = 0; /* MSG_DONTWAIT; */
+ int res, size;
+
+ size = buf->sg.length - buf->sent;
+ BUG_ON(buf->sent + size > buf->sg.length);
+ if (buf->sent + size != buf->sg.length || datalen)
+ flags |= MSG_MORE;
+
+ res = iscsi_send(conn, buf, size, flags);
+ debug_tcp("sendhdr %d bytes, sent %d res %d\n", size, buf->sent, res);
+ if (res >= 0) {
+ if (size != res)
+ return -EAGAIN;
+ return 0;
+ }
+
+ return res;
+}
+
+/**
+ * iscsi_sendpage - send one page of iSCSI Data-Out.
+ * @conn: iscsi connection
+ * @buf: buffer to write from
+ * @count: remaining data
+ * @sent: number of bytes sent
+ *
+ * Notes:
+ * (Tx, Fast Path)
+ **/
+static inline int
+iscsi_sendpage(struct iscsi_conn *conn, struct iscsi_buf *buf,
+ int *count, int *sent)
+{
+ int flags = 0; /* MSG_DONTWAIT; */
+ int res, size;
+
+ size = buf->sg.length - buf->sent;
+ BUG_ON(buf->sent + size > buf->sg.length);
+ if (size > *count)
+ size = *count;
+ if (buf->sent + size != buf->sg.length || *count != size)
+ flags |= MSG_MORE;
+
+ res = iscsi_send(conn, buf, size, flags);
+ debug_tcp("sendpage: %d bytes, sent %d left %d sent %d res %d\n",
+ size, buf->sent, *count, *sent, res);
+ if (res >= 0) {
+ *count -= res;
+ *sent += res;
+ if (size != res)
+ return -EAGAIN;
+ return 0;
+ }
+
+ return res;
+}
+
+static inline void
+iscsi_data_digest_init(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_tcp_cmd_task *tcp_ctask)
+{
+ crypto_hash_init(&tcp_conn->tx_hash);
+ tcp_ctask->digest_count = 4;
+}
+
+/**
+ * iscsi_solicit_data_cont - initialize next Data-Out
+ * @conn: iscsi connection
+ * @ctask: scsi command task
+ * @r2t: R2T info
+ * @left: bytes left to transfer
+ *
+ * Notes:
+ * Initialize next Data-Out within this R2T sequence and continue
+ * to process next Scatter-Gather element(if any) of this SCSI command.
+ *
+ * Called under connection lock.
+ **/
+static void
+iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+ struct iscsi_r2t_info *r2t, int left)
+{
+ struct iscsi_data *hdr;
+ struct scsi_cmnd *sc = ctask->sc;
+ int new_offset;
+
+ hdr = &r2t->dtask.hdr;
+ memset(hdr, 0, sizeof(struct iscsi_data));
+ hdr->ttt = r2t->ttt;
+ hdr->datasn = cpu_to_be32(r2t->solicit_datasn);
+ r2t->solicit_datasn++;
+ hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
+ memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun));
+ hdr->itt = ctask->hdr->itt;
+ hdr->exp_statsn = r2t->exp_statsn;
+ new_offset = r2t->data_offset + r2t->sent;
+ hdr->offset = cpu_to_be32(new_offset);
+ if (left > conn->max_xmit_dlength) {
+ hton24(hdr->dlength, conn->max_xmit_dlength);
+ r2t->data_count = conn->max_xmit_dlength;
+ } else {
+ hton24(hdr->dlength, left);
+ r2t->data_count = left;
+ hdr->flags = ISCSI_FLAG_CMD_FINAL;
+ }
+ conn->dataout_pdus_cnt++;
+
+ iscsi_buf_init_iov(&r2t->headbuf, (char*)hdr,
+ sizeof(struct iscsi_hdr));
+
+ if (iscsi_buf_left(&r2t->sendbuf))
+ return;
+
+ if (sc->use_sg) {
+ iscsi_buf_init_sg(&r2t->sendbuf, r2t->sg);
+ r2t->sg += 1;
+ } else {
+ iscsi_buf_init_iov(&r2t->sendbuf,
+ (char*)sc->request_buffer + new_offset,
+ r2t->data_count);
+ r2t->sg = NULL;
+ }
+}
+
+static void iscsi_set_padding(struct iscsi_tcp_cmd_task *tcp_ctask,
+ unsigned long len)
+{
+ tcp_ctask->pad_count = len & (ISCSI_PAD_LEN - 1);
+ if (!tcp_ctask->pad_count)
+ return;
+
+ tcp_ctask->pad_count = ISCSI_PAD_LEN - tcp_ctask->pad_count;
+ debug_scsi("write padding %d bytes\n", tcp_ctask->pad_count);
+ tcp_ctask->xmstate |= XMSTATE_W_PAD;
+}
+
+/**
+ * iscsi_tcp_cmd_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
+ * @conn: iscsi connection
+ * @ctask: scsi command task
+ * @sc: scsi command
+ **/
+static void
+iscsi_tcp_cmd_init(struct iscsi_cmd_task *ctask)
+{
+ struct scsi_cmnd *sc = ctask->sc;
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+
+ BUG_ON(__kfifo_len(tcp_ctask->r2tqueue));
+
+ tcp_ctask->sent = 0;
+ tcp_ctask->sg_count = 0;
+
+ if (sc->sc_data_direction == DMA_TO_DEVICE) {
+ tcp_ctask->xmstate = XMSTATE_W_HDR;
+ tcp_ctask->exp_r2tsn = 0;
+ BUG_ON(ctask->total_length == 0);
+
+ if (sc->use_sg) {
+ struct scatterlist *sg = sc->request_buffer;
+
+ iscsi_buf_init_sg(&tcp_ctask->sendbuf, sg);
+ tcp_ctask->sg = sg + 1;
+ tcp_ctask->bad_sg = sg + sc->use_sg;
+ } else {
+ iscsi_buf_init_iov(&tcp_ctask->sendbuf,
+ sc->request_buffer,
+ sc->request_bufflen);
+ tcp_ctask->sg = NULL;
+ tcp_ctask->bad_sg = NULL;
+ }
+ debug_scsi("cmd [itt 0x%x total %d imm_data %d "
+ "unsol count %d, unsol offset %d]\n",
+ ctask->itt, ctask->total_length, ctask->imm_count,
+ ctask->unsol_count, ctask->unsol_offset);
+ } else
+ tcp_ctask->xmstate = XMSTATE_R_HDR;
+
+ iscsi_buf_init_iov(&tcp_ctask->headbuf, (char*)ctask->hdr,
+ sizeof(struct iscsi_hdr));
+}
+
+/**
+ * iscsi_tcp_mtask_xmit - xmit management(immediate) task
+ * @conn: iscsi connection
+ * @mtask: task management task
+ *
+ * Notes:
+ * The function can return -EAGAIN in which case caller must
+ * call it again later, or recover. '0' return code means successful
+ * xmit.
+ *
+ * Management xmit state machine consists of two states:
+ * IN_PROGRESS_IMM_HEAD - PDU Header xmit in progress
+ * IN_PROGRESS_IMM_DATA - PDU Data xmit in progress
+ **/
+static int
+iscsi_tcp_mtask_xmit(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
+{
+ struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data;
+ int rc;
+
+ debug_scsi("mtask deq [cid %d state %x itt 0x%x]\n",
+ conn->id, tcp_mtask->xmstate, mtask->itt);
+
+ if (tcp_mtask->xmstate & XMSTATE_IMM_HDR) {
+ tcp_mtask->xmstate &= ~XMSTATE_IMM_HDR;
+ if (mtask->data_count)
+ tcp_mtask->xmstate |= XMSTATE_IMM_DATA;
+ if (conn->c_stage != ISCSI_CONN_INITIAL_STAGE &&
+ conn->stop_stage != STOP_CONN_RECOVER &&
+ conn->hdrdgst_en)
+ iscsi_hdr_digest(conn, &tcp_mtask->headbuf,
+ (u8*)tcp_mtask->hdrext);
+ rc = iscsi_sendhdr(conn, &tcp_mtask->headbuf,
+ mtask->data_count);
+ if (rc) {
+ tcp_mtask->xmstate |= XMSTATE_IMM_HDR;
+ if (mtask->data_count)
+ tcp_mtask->xmstate &= ~XMSTATE_IMM_DATA;
+ return rc;
+ }
+ }
+
+ if (tcp_mtask->xmstate & XMSTATE_IMM_DATA) {
+ BUG_ON(!mtask->data_count);
+ tcp_mtask->xmstate &= ~XMSTATE_IMM_DATA;
+ /* FIXME: implement.
+ * Virtual buffer could be spreaded across multiple pages...
+ */
+ do {
+ int rc;
+
+ rc = iscsi_sendpage(conn, &tcp_mtask->sendbuf,
+ &mtask->data_count, &tcp_mtask->sent);
+ if (rc) {
+ tcp_mtask->xmstate |= XMSTATE_IMM_DATA;
+ return rc;
+ }
+ } while (mtask->data_count);
+ }
+
+ BUG_ON(tcp_mtask->xmstate != XMSTATE_IDLE);
+ if (mtask->hdr->itt == RESERVED_ITT) {
+ struct iscsi_session *session = conn->session;
+
+ spin_lock_bh(&session->lock);
+ list_del(&conn->mtask->running);
+ __kfifo_put(session->mgmtpool.queue, (void*)&conn->mtask,
+ sizeof(void*));
+ spin_unlock_bh(&session->lock);
+ }
+ return 0;
+}
+
+static inline int
+iscsi_send_read_hdr(struct iscsi_conn *conn,
+ struct iscsi_tcp_cmd_task *tcp_ctask)
+{
+ int rc;
+
+ tcp_ctask->xmstate &= ~XMSTATE_R_HDR;
+ if (conn->hdrdgst_en)
+ iscsi_hdr_digest(conn, &tcp_ctask->headbuf,
+ (u8*)tcp_ctask->hdrext);
+ rc = iscsi_sendhdr(conn, &tcp_ctask->headbuf, 0);
+ if (!rc) {
+ BUG_ON(tcp_ctask->xmstate != XMSTATE_IDLE);
+ return 0; /* wait for Data-In */
+ }
+ tcp_ctask->xmstate |= XMSTATE_R_HDR;
+ return rc;
+}
+
+static inline int
+iscsi_send_write_hdr(struct iscsi_conn *conn,
+ struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ int rc;
+
+ tcp_ctask->xmstate &= ~XMSTATE_W_HDR;
+ if (conn->hdrdgst_en)
+ iscsi_hdr_digest(conn, &tcp_ctask->headbuf,
+ (u8*)tcp_ctask->hdrext);
+ rc = iscsi_sendhdr(conn, &tcp_ctask->headbuf, ctask->imm_count);
+ if (rc) {
+ tcp_ctask->xmstate |= XMSTATE_W_HDR;
+ return rc;
+ }
+
+ if (ctask->imm_count) {
+ tcp_ctask->xmstate |= XMSTATE_IMM_DATA;
+ iscsi_set_padding(tcp_ctask, ctask->imm_count);
+
+ if (ctask->conn->datadgst_en) {
+ iscsi_data_digest_init(ctask->conn->dd_data, tcp_ctask);
+ tcp_ctask->immdigest = 0;
+ }
+ }
+
+ if (ctask->unsol_count)
+ tcp_ctask->xmstate |= XMSTATE_UNS_HDR | XMSTATE_UNS_INIT;
+ return 0;
+}
+
+static int
+iscsi_send_padding(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ int sent = 0, rc;
+
+ if (tcp_ctask->xmstate & XMSTATE_W_PAD) {
+ iscsi_buf_init_iov(&tcp_ctask->sendbuf, (char*)&tcp_ctask->pad,
+ tcp_ctask->pad_count);
+ if (conn->datadgst_en)
+ crypto_hash_update(&tcp_conn->tx_hash,
+ &tcp_ctask->sendbuf.sg,
+ tcp_ctask->sendbuf.sg.length);
+ } else if (!(tcp_ctask->xmstate & XMSTATE_W_RESEND_PAD))
+ return 0;
+
+ tcp_ctask->xmstate &= ~XMSTATE_W_PAD;
+ tcp_ctask->xmstate &= ~XMSTATE_W_RESEND_PAD;
+ debug_scsi("sending %d pad bytes for itt 0x%x\n",
+ tcp_ctask->pad_count, ctask->itt);
+ rc = iscsi_sendpage(conn, &tcp_ctask->sendbuf, &tcp_ctask->pad_count,
+ &sent);
+ if (rc) {
+ debug_scsi("padding send failed %d\n", rc);
+ tcp_ctask->xmstate |= XMSTATE_W_RESEND_PAD;
+ }
+ return rc;
+}
+
+static int
+iscsi_send_digest(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+ struct iscsi_buf *buf, uint32_t *digest)
+{
+ struct iscsi_tcp_cmd_task *tcp_ctask;
+ struct iscsi_tcp_conn *tcp_conn;
+ int rc, sent = 0;
+
+ if (!conn->datadgst_en)
+ return 0;
+
+ tcp_ctask = ctask->dd_data;
+ tcp_conn = conn->dd_data;
+
+ if (!(tcp_ctask->xmstate & XMSTATE_W_RESEND_DATA_DIGEST)) {
+ crypto_hash_final(&tcp_conn->tx_hash, (u8*)digest);
+ iscsi_buf_init_iov(buf, (char*)digest, 4);
+ }
+ tcp_ctask->xmstate &= ~XMSTATE_W_RESEND_DATA_DIGEST;
+
+ rc = iscsi_sendpage(conn, buf, &tcp_ctask->digest_count, &sent);
+ if (!rc)
+ debug_scsi("sent digest 0x%x for itt 0x%x\n", *digest,
+ ctask->itt);
+ else {
+ debug_scsi("sending digest 0x%x failed for itt 0x%x!\n",
+ *digest, ctask->itt);
+ tcp_ctask->xmstate |= XMSTATE_W_RESEND_DATA_DIGEST;
+ }
+ return rc;
+}
+
+static int
+iscsi_send_data(struct iscsi_cmd_task *ctask, struct iscsi_buf *sendbuf,
+ struct scatterlist **sg, int *sent, int *count,
+ struct iscsi_buf *digestbuf, uint32_t *digest)
+{
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ struct iscsi_conn *conn = ctask->conn;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ int rc, buf_sent, offset;
+
+ while (*count) {
+ buf_sent = 0;
+ offset = sendbuf->sent;
+
+ rc = iscsi_sendpage(conn, sendbuf, count, &buf_sent);
+ *sent = *sent + buf_sent;
+ if (buf_sent && conn->datadgst_en)
+ partial_sg_digest_update(&tcp_conn->tx_hash,
+ &sendbuf->sg, sendbuf->sg.offset + offset,
+ buf_sent);
+ if (!iscsi_buf_left(sendbuf) && *sg != tcp_ctask->bad_sg) {
+ iscsi_buf_init_sg(sendbuf, *sg);
+ *sg = *sg + 1;
+ }
+
+ if (rc)
+ return rc;
+ }
+
+ rc = iscsi_send_padding(conn, ctask);
+ if (rc)
+ return rc;
+
+ return iscsi_send_digest(conn, ctask, digestbuf, digest);
+}
+
+static int
+iscsi_send_unsol_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ struct iscsi_data_task *dtask;
+ int rc;
+
+ tcp_ctask->xmstate |= XMSTATE_UNS_DATA;
+ if (tcp_ctask->xmstate & XMSTATE_UNS_INIT) {
+ dtask = &tcp_ctask->unsol_dtask;
+
+ iscsi_prep_unsolicit_data_pdu(ctask, &dtask->hdr);
+ iscsi_buf_init_iov(&tcp_ctask->headbuf, (char*)&dtask->hdr,
+ sizeof(struct iscsi_hdr));
+ if (conn->hdrdgst_en)
+ iscsi_hdr_digest(conn, &tcp_ctask->headbuf,
+ (u8*)dtask->hdrext);
+
+ tcp_ctask->xmstate &= ~XMSTATE_UNS_INIT;
+ iscsi_set_padding(tcp_ctask, ctask->data_count);
+ }
+
+ rc = iscsi_sendhdr(conn, &tcp_ctask->headbuf, ctask->data_count);
+ if (rc) {
+ tcp_ctask->xmstate &= ~XMSTATE_UNS_DATA;
+ tcp_ctask->xmstate |= XMSTATE_UNS_HDR;
+ return rc;
+ }
+
+ if (conn->datadgst_en) {
+ dtask = &tcp_ctask->unsol_dtask;
+ iscsi_data_digest_init(ctask->conn->dd_data, tcp_ctask);
+ dtask->digest = 0;
+ }
+
+ debug_scsi("uns dout [itt 0x%x dlen %d sent %d]\n",
+ ctask->itt, ctask->unsol_count, tcp_ctask->sent);
+ return 0;
+}
+
+static int
+iscsi_send_unsol_pdu(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ int rc;
+
+ if (tcp_ctask->xmstate & XMSTATE_UNS_HDR) {
+ BUG_ON(!ctask->unsol_count);
+ tcp_ctask->xmstate &= ~XMSTATE_UNS_HDR;
+send_hdr:
+ rc = iscsi_send_unsol_hdr(conn, ctask);
+ if (rc)
+ return rc;
+ }
+
+ if (tcp_ctask->xmstate & XMSTATE_UNS_DATA) {
+ struct iscsi_data_task *dtask = &tcp_ctask->unsol_dtask;
+ int start = tcp_ctask->sent;
+
+ rc = iscsi_send_data(ctask, &tcp_ctask->sendbuf, &tcp_ctask->sg,
+ &tcp_ctask->sent, &ctask->data_count,
+ &dtask->digestbuf, &dtask->digest);
+ ctask->unsol_count -= tcp_ctask->sent - start;
+ if (rc)
+ return rc;
+ tcp_ctask->xmstate &= ~XMSTATE_UNS_DATA;
+ /*
+ * Done with the Data-Out. Next, check if we need
+ * to send another unsolicited Data-Out.
+ */
+ if (ctask->unsol_count) {
+ debug_scsi("sending more uns\n");
+ tcp_ctask->xmstate |= XMSTATE_UNS_INIT;
+ goto send_hdr;
+ }
+ }
+ return 0;
+}
+
+static int iscsi_send_sol_pdu(struct iscsi_conn *conn,
+ struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ struct iscsi_session *session = conn->session;
+ struct iscsi_r2t_info *r2t;
+ struct iscsi_data_task *dtask;
+ int left, rc;
+
+ if (tcp_ctask->xmstate & XMSTATE_SOL_HDR) {
+ tcp_ctask->xmstate &= ~XMSTATE_SOL_HDR;
+ tcp_ctask->xmstate |= XMSTATE_SOL_DATA;
+ if (!tcp_ctask->r2t) {
+ spin_lock_bh(&session->lock);
+ __kfifo_get(tcp_ctask->r2tqueue, (void*)&tcp_ctask->r2t,
+ sizeof(void*));
+ spin_unlock_bh(&session->lock);
+ }
+send_hdr:
+ r2t = tcp_ctask->r2t;
+ dtask = &r2t->dtask;
+
+ if (conn->hdrdgst_en)
+ iscsi_hdr_digest(conn, &r2t->headbuf,
+ (u8*)dtask->hdrext);
+ rc = iscsi_sendhdr(conn, &r2t->headbuf, r2t->data_count);
+ if (rc) {
+ tcp_ctask->xmstate &= ~XMSTATE_SOL_DATA;
+ tcp_ctask->xmstate |= XMSTATE_SOL_HDR;
+ return rc;
+ }
+
+ if (conn->datadgst_en) {
+ iscsi_data_digest_init(conn->dd_data, tcp_ctask);
+ dtask->digest = 0;
+ }
+
+ iscsi_set_padding(tcp_ctask, r2t->data_count);
+ debug_scsi("sol dout [dsn %d itt 0x%x dlen %d sent %d]\n",
+ r2t->solicit_datasn - 1, ctask->itt, r2t->data_count,
+ r2t->sent);
+ }
+
+ if (tcp_ctask->xmstate & XMSTATE_SOL_DATA) {
+ r2t = tcp_ctask->r2t;
+ dtask = &r2t->dtask;
+
+ rc = iscsi_send_data(ctask, &r2t->sendbuf, &r2t->sg,
+ &r2t->sent, &r2t->data_count,
+ &dtask->digestbuf, &dtask->digest);
+ if (rc)
+ return rc;
+ tcp_ctask->xmstate &= ~XMSTATE_SOL_DATA;
+
+ /*
+ * Done with this Data-Out. Next, check if we have
+ * to send another Data-Out for this R2T.
+ */
+ BUG_ON(r2t->data_length - r2t->sent < 0);
+ left = r2t->data_length - r2t->sent;
+ if (left) {
+ iscsi_solicit_data_cont(conn, ctask, r2t, left);
+ tcp_ctask->xmstate |= XMSTATE_SOL_DATA;
+ tcp_ctask->xmstate &= ~XMSTATE_SOL_HDR;
+ goto send_hdr;
+ }
+
+ /*
+ * Done with this R2T. Check if there are more
+ * outstanding R2Ts ready to be processed.
+ */
+ spin_lock_bh(&session->lock);
+ tcp_ctask->r2t = NULL;
+ __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+ sizeof(void*));
+ if (__kfifo_get(tcp_ctask->r2tqueue, (void*)&r2t,
+ sizeof(void*))) {
+ tcp_ctask->r2t = r2t;
+ tcp_ctask->xmstate |= XMSTATE_SOL_DATA;
+ tcp_ctask->xmstate &= ~XMSTATE_SOL_HDR;
+ spin_unlock_bh(&session->lock);
+ goto send_hdr;
+ }
+ spin_unlock_bh(&session->lock);
+ }
+ return 0;
+}
+
+static int
+iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ int rc = 0;
+
+ debug_scsi("ctask deq [cid %d xmstate %x itt 0x%x]\n",
+ conn->id, tcp_ctask->xmstate, ctask->itt);
+
+ /*
+ * serialize with TMF AbortTask
+ */
+ if (ctask->mtask)
+ return rc;
+
+ if (tcp_ctask->xmstate & XMSTATE_R_HDR)
+ return iscsi_send_read_hdr(conn, tcp_ctask);
+
+ if (tcp_ctask->xmstate & XMSTATE_W_HDR) {
+ rc = iscsi_send_write_hdr(conn, ctask);
+ if (rc)
+ return rc;
+ }
+
+ if (tcp_ctask->xmstate & XMSTATE_IMM_DATA) {
+ rc = iscsi_send_data(ctask, &tcp_ctask->sendbuf, &tcp_ctask->sg,
+ &tcp_ctask->sent, &ctask->imm_count,
+ &tcp_ctask->immbuf, &tcp_ctask->immdigest);
+ if (rc)
+ return rc;
+ tcp_ctask->xmstate &= ~XMSTATE_IMM_DATA;
+ }
+
+ rc = iscsi_send_unsol_pdu(conn, ctask);
+ if (rc)
+ return rc;
+
+ rc = iscsi_send_sol_pdu(conn, ctask);
+ if (rc)
+ return rc;
+
+ return rc;
+}
+
+static struct iscsi_cls_conn *
+iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
+{
+ struct iscsi_conn *conn;
+ struct iscsi_cls_conn *cls_conn;
+ struct iscsi_tcp_conn *tcp_conn;
+
+ cls_conn = iscsi_conn_setup(cls_session, conn_idx);
+ if (!cls_conn)
+ return NULL;
+ conn = cls_conn->dd_data;
+ /*
+ * due to strange issues with iser these are not set
+ * in iscsi_conn_setup
+ */
+ conn->max_recv_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN;
+
+ tcp_conn = kzalloc(sizeof(*tcp_conn), GFP_KERNEL);
+ if (!tcp_conn)
+ goto tcp_conn_alloc_fail;
+
+ conn->dd_data = tcp_conn;
+ tcp_conn->iscsi_conn = conn;
+ tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
+ /* initial operational parameters */
+ tcp_conn->hdr_size = sizeof(struct iscsi_hdr);
+
+ tcp_conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0,
+ CRYPTO_ALG_ASYNC);
+ tcp_conn->tx_hash.flags = 0;
+ if (IS_ERR(tcp_conn->tx_hash.tfm)) {
+ printk(KERN_ERR "Could not create connection due to crc32c "
+ "loading error %ld. Make sure the crc32c module is "
+ "built as a module or into the kernel\n",
+ PTR_ERR(tcp_conn->tx_hash.tfm));
+ goto free_tcp_conn;
+ }
+
+ tcp_conn->rx_hash.tfm = crypto_alloc_hash("crc32c", 0,
+ CRYPTO_ALG_ASYNC);
+ tcp_conn->rx_hash.flags = 0;
+ if (IS_ERR(tcp_conn->rx_hash.tfm)) {
+ printk(KERN_ERR "Could not create connection due to crc32c "
+ "loading error %ld. Make sure the crc32c module is "
+ "built as a module or into the kernel\n",
+ PTR_ERR(tcp_conn->rx_hash.tfm));
+ goto free_tx_tfm;
+ }
+
+ return cls_conn;
+
+free_tx_tfm:
+ crypto_free_hash(tcp_conn->tx_hash.tfm);
+free_tcp_conn:
+ kfree(tcp_conn);
+tcp_conn_alloc_fail:
+ iscsi_conn_teardown(cls_conn);
+ return NULL;
+}
+
+static void
+iscsi_tcp_release_conn(struct iscsi_conn *conn)
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+
+ if (!tcp_conn->sock)
+ return;
+
+ sock_hold(tcp_conn->sock->sk);
+ iscsi_conn_restore_callbacks(tcp_conn);
+ sock_put(tcp_conn->sock->sk);
+
+ sock_release(tcp_conn->sock);
+ tcp_conn->sock = NULL;
+ conn->recv_lock = NULL;
+}
+
+static void
+iscsi_tcp_conn_destroy(struct iscsi_cls_conn *cls_conn)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+
+ iscsi_tcp_release_conn(conn);
+ iscsi_conn_teardown(cls_conn);
+
+ if (tcp_conn->tx_hash.tfm)
+ crypto_free_hash(tcp_conn->tx_hash.tfm);
+ if (tcp_conn->rx_hash.tfm)
+ crypto_free_hash(tcp_conn->rx_hash.tfm);
+
+ kfree(tcp_conn);
+}
+
+static void
+iscsi_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+
+ iscsi_conn_stop(cls_conn, flag);
+ iscsi_tcp_release_conn(conn);
+ tcp_conn->hdr_size = sizeof(struct iscsi_hdr);
+}
+
+static int
+iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
+ struct iscsi_cls_conn *cls_conn, uint64_t transport_eph,
+ int is_leading)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct sock *sk;
+ struct socket *sock;
+ int err;
+
+ /* lookup for existing socket */
+ sock = sockfd_lookup((int)transport_eph, &err);
+ if (!sock) {
+ printk(KERN_ERR "iscsi_tcp: sockfd_lookup failed %d\n", err);
+ return -EEXIST;
+ }
+
+ err = iscsi_conn_bind(cls_session, cls_conn, is_leading);
+ if (err)
+ return err;
+
+ /* bind iSCSI connection and socket */
+ tcp_conn->sock = sock;
+
+ /* setup Socket parameters */
+ sk = sock->sk;
+ sk->sk_reuse = 1;
+ sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */
+ sk->sk_allocation = GFP_ATOMIC;
+
+ /* FIXME: disable Nagle's algorithm */
+
+ /*
+ * Intercept TCP callbacks for sendfile like receive
+ * processing.
+ */
+ conn->recv_lock = &sk->sk_callback_lock;
+ iscsi_conn_set_callbacks(conn);
+ tcp_conn->sendpage = tcp_conn->sock->ops->sendpage;
+ /*
+ * set receive state machine into initial state
+ */
+ tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
+
+ return 0;
+}
+
+/* called with host lock */
+static void
+iscsi_tcp_mgmt_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask,
+ char *data, uint32_t data_size)
+{
+ struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data;
+
+ iscsi_buf_init_iov(&tcp_mtask->headbuf, (char*)mtask->hdr,
+ sizeof(struct iscsi_hdr));
+ tcp_mtask->xmstate = XMSTATE_IMM_HDR;
+ tcp_mtask->sent = 0;
+
+ if (mtask->data_count)
+ iscsi_buf_init_iov(&tcp_mtask->sendbuf, (char*)mtask->data,
+ mtask->data_count);
+}
+
+static int
+iscsi_r2tpool_alloc(struct iscsi_session *session)
+{
+ int i;
+ int cmd_i;
+
+ /*
+ * initialize per-task: R2T pool and xmit queue
+ */
+ for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
+ struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+
+ /*
+ * pre-allocated x4 as much r2ts to handle race when
+ * target acks DataOut faster than we data_xmit() queues
+ * could replenish r2tqueue.
+ */
+
+ /* R2T pool */
+ if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4,
+ (void***)&tcp_ctask->r2ts,
+ sizeof(struct iscsi_r2t_info))) {
+ goto r2t_alloc_fail;
+ }
+
+ /* R2T xmit queue */
+ tcp_ctask->r2tqueue = kfifo_alloc(
+ session->max_r2t * 4 * sizeof(void*), GFP_KERNEL, NULL);
+ if (tcp_ctask->r2tqueue == ERR_PTR(-ENOMEM)) {
+ iscsi_pool_free(&tcp_ctask->r2tpool,
+ (void**)tcp_ctask->r2ts);
+ goto r2t_alloc_fail;
+ }
+ }
+
+ return 0;
+
+r2t_alloc_fail:
+ for (i = 0; i < cmd_i; i++) {
+ struct iscsi_cmd_task *ctask = session->cmds[i];
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+
+ kfifo_free(tcp_ctask->r2tqueue);
+ iscsi_pool_free(&tcp_ctask->r2tpool,
+ (void**)tcp_ctask->r2ts);
+ }
+ return -ENOMEM;
+}
+
+static void
+iscsi_r2tpool_free(struct iscsi_session *session)
+{
+ int i;
+
+ for (i = 0; i < session->cmds_max; i++) {
+ struct iscsi_cmd_task *ctask = session->cmds[i];
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+
+ kfifo_free(tcp_ctask->r2tqueue);
+ iscsi_pool_free(&tcp_ctask->r2tpool,
+ (void**)tcp_ctask->r2ts);
+ }
+}
+
+static int
+iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
+ char *buf, int buflen)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_session *session = conn->session;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ int value;
+
+ switch(param) {
+ case ISCSI_PARAM_HDRDGST_EN:
+ iscsi_set_param(cls_conn, param, buf, buflen);
+ tcp_conn->hdr_size = sizeof(struct iscsi_hdr);
+ if (conn->hdrdgst_en)
+ tcp_conn->hdr_size += sizeof(__u32);
+ break;
+ case ISCSI_PARAM_DATADGST_EN:
+ iscsi_set_param(cls_conn, param, buf, buflen);
+ tcp_conn->sendpage = conn->datadgst_en ?
+ sock_no_sendpage : tcp_conn->sock->ops->sendpage;
+ break;
+ case ISCSI_PARAM_MAX_R2T:
+ sscanf(buf, "%d", &value);
+ if (session->max_r2t == roundup_pow_of_two(value))
+ break;
+ iscsi_r2tpool_free(session);
+ iscsi_set_param(cls_conn, param, buf, buflen);
+ if (session->max_r2t & (session->max_r2t - 1))
+ session->max_r2t = roundup_pow_of_two(session->max_r2t);
+ if (iscsi_r2tpool_alloc(session))
+ return -ENOMEM;
+ break;
+ default:
+ return iscsi_set_param(cls_conn, param, buf, buflen);
+ }
+
+ return 0;
+}
+
+static int
+iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct inet_sock *inet;
+ struct ipv6_pinfo *np;
+ struct sock *sk;
+ int len;
+
+ switch(param) {
+ case ISCSI_PARAM_CONN_PORT:
+ mutex_lock(&conn->xmitmutex);
+ if (!tcp_conn->sock) {
+ mutex_unlock(&conn->xmitmutex);
+ return -EINVAL;
+ }
+
+ inet = inet_sk(tcp_conn->sock->sk);
+ len = sprintf(buf, "%hu\n", be16_to_cpu(inet->dport));
+ mutex_unlock(&conn->xmitmutex);
+ break;
+ case ISCSI_PARAM_CONN_ADDRESS:
+ mutex_lock(&conn->xmitmutex);
+ if (!tcp_conn->sock) {
+ mutex_unlock(&conn->xmitmutex);
+ return -EINVAL;
+ }
+
+ sk = tcp_conn->sock->sk;
+ if (sk->sk_family == PF_INET) {
+ inet = inet_sk(sk);
+ len = sprintf(buf, NIPQUAD_FMT "\n",
+ NIPQUAD(inet->daddr));
+ } else {
+ np = inet6_sk(sk);
+ len = sprintf(buf, NIP6_FMT "\n", NIP6(np->daddr));
+ }
+ mutex_unlock(&conn->xmitmutex);
+ break;
+ default:
+ return iscsi_conn_get_param(cls_conn, param, buf);
+ }
+
+ return len;
+}
+
+static void
+iscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+
+ stats->txdata_octets = conn->txdata_octets;
+ stats->rxdata_octets = conn->rxdata_octets;
+ stats->scsicmd_pdus = conn->scsicmd_pdus_cnt;
+ stats->dataout_pdus = conn->dataout_pdus_cnt;
+ stats->scsirsp_pdus = conn->scsirsp_pdus_cnt;
+ stats->datain_pdus = conn->datain_pdus_cnt;
+ stats->r2t_pdus = conn->r2t_pdus_cnt;
+ stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt;
+ stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt;
+ stats->custom_length = 3;
+ strcpy(stats->custom[0].desc, "tx_sendpage_failures");
+ stats->custom[0].value = tcp_conn->sendpage_failures_cnt;
+ strcpy(stats->custom[1].desc, "rx_discontiguous_hdr");
+ stats->custom[1].value = tcp_conn->discontiguous_hdr_cnt;
+ strcpy(stats->custom[2].desc, "eh_abort_cnt");
+ stats->custom[2].value = conn->eh_abort_cnt;
+}
+
+static struct iscsi_cls_session *
+iscsi_tcp_session_create(struct iscsi_transport *iscsit,
+ struct scsi_transport_template *scsit,
+ uint32_t initial_cmdsn, uint32_t *hostno)
+{
+ struct iscsi_cls_session *cls_session;
+ struct iscsi_session *session;
+ uint32_t hn;
+ int cmd_i;
+
+ cls_session = iscsi_session_setup(iscsit, scsit,
+ sizeof(struct iscsi_tcp_cmd_task),
+ sizeof(struct iscsi_tcp_mgmt_task),
+ initial_cmdsn, &hn);
+ if (!cls_session)
+ return NULL;
+ *hostno = hn;
+
+ session = class_to_transport_session(cls_session);
+ for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
+ struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+
+ ctask->hdr = &tcp_ctask->hdr;
+ }
+
+ for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) {
+ struct iscsi_mgmt_task *mtask = session->mgmt_cmds[cmd_i];
+ struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data;
+
+ mtask->hdr = &tcp_mtask->hdr;
+ }
+
+ if (iscsi_r2tpool_alloc(class_to_transport_session(cls_session)))
+ goto r2tpool_alloc_fail;
+
+ return cls_session;
+
+r2tpool_alloc_fail:
+ iscsi_session_teardown(cls_session);
+ return NULL;
+}
+
+static void iscsi_tcp_session_destroy(struct iscsi_cls_session *cls_session)
+{
+ iscsi_r2tpool_free(class_to_transport_session(cls_session));
+ iscsi_session_teardown(cls_session);
+}
+
+static struct scsi_host_template iscsi_sht = {
+ .name = "iSCSI Initiator over TCP/IP",
+ .queuecommand = iscsi_queuecommand,
+ .change_queue_depth = iscsi_change_queue_depth,
+ .can_queue = ISCSI_XMIT_CMDS_MAX - 1,
+ .sg_tablesize = ISCSI_SG_TABLESIZE,
+ .max_sectors = 0xFFFF,
+ .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN,
+ .eh_abort_handler = iscsi_eh_abort,
+ .eh_host_reset_handler = iscsi_eh_host_reset,
+ .use_clustering = DISABLE_CLUSTERING,
+ .proc_name = "iscsi_tcp",
+ .this_id = -1,
+};
+
+static struct iscsi_transport iscsi_tcp_transport = {
+ .owner = THIS_MODULE,
+ .name = "tcp",
+ .caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST
+ | CAP_DATADGST,
+ .param_mask = ISCSI_MAX_RECV_DLENGTH |
+ ISCSI_MAX_XMIT_DLENGTH |
+ ISCSI_HDRDGST_EN |
+ ISCSI_DATADGST_EN |
+ ISCSI_INITIAL_R2T_EN |
+ ISCSI_MAX_R2T |
+ ISCSI_IMM_DATA_EN |
+ ISCSI_FIRST_BURST |
+ ISCSI_MAX_BURST |
+ ISCSI_PDU_INORDER_EN |
+ ISCSI_DATASEQ_INORDER_EN |
+ ISCSI_ERL |
+ ISCSI_CONN_PORT |
+ ISCSI_CONN_ADDRESS |
+ ISCSI_EXP_STATSN |
+ ISCSI_PERSISTENT_PORT |
+ ISCSI_PERSISTENT_ADDRESS |
+ ISCSI_TARGET_NAME |
+ ISCSI_TPGT,
+ .host_template = &iscsi_sht,
+ .conndata_size = sizeof(struct iscsi_conn),
+ .max_conn = 1,
+ .max_cmd_len = ISCSI_TCP_MAX_CMD_LEN,
+ /* session management */
+ .create_session = iscsi_tcp_session_create,
+ .destroy_session = iscsi_tcp_session_destroy,
+ /* connection management */
+ .create_conn = iscsi_tcp_conn_create,
+ .bind_conn = iscsi_tcp_conn_bind,
+ .destroy_conn = iscsi_tcp_conn_destroy,
+ .set_param = iscsi_conn_set_param,
+ .get_conn_param = iscsi_tcp_conn_get_param,
+ .get_session_param = iscsi_session_get_param,
+ .start_conn = iscsi_conn_start,
+ .stop_conn = iscsi_tcp_conn_stop,
+ /* IO */
+ .send_pdu = iscsi_conn_send_pdu,
+ .get_stats = iscsi_conn_get_stats,
+ .init_cmd_task = iscsi_tcp_cmd_init,
+ .init_mgmt_task = iscsi_tcp_mgmt_init,
+ .xmit_cmd_task = iscsi_tcp_ctask_xmit,
+ .xmit_mgmt_task = iscsi_tcp_mtask_xmit,
+ .cleanup_cmd_task = iscsi_tcp_cleanup_ctask,
+ /* recovery */
+ .session_recovery_timedout = iscsi_session_recovery_timedout,
+};
+
+static int __init
+iscsi_tcp_init(void)
+{
+ if (iscsi_max_lun < 1) {
+ printk(KERN_ERR "iscsi_tcp: Invalid max_lun value of %u\n",
+ iscsi_max_lun);
+ return -EINVAL;
+ }
+ iscsi_tcp_transport.max_lun = iscsi_max_lun;
+
+ if (!iscsi_register_transport(&iscsi_tcp_transport))
+ return -ENODEV;
+
+ return 0;
+}
+
+static void __exit
+iscsi_tcp_exit(void)
+{
+ iscsi_unregister_transport(&iscsi_tcp_transport);
+}
+
+module_init(iscsi_tcp_init);
+module_exit(iscsi_tcp_exit);
Index: kernel/libiscsi.c
===================================================================
--- kernel/libiscsi.c (revision 0)
+++ kernel/libiscsi.c (revision 779)
@@ -0,0 +1,1995 @@
+/*
+ * iSCSI lib functions
+ *
+ * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004 - 2006 Mike Christie
+ * Copyright (C) 2004 - 2005 Dmitry Yusupov
+ * Copyright (C) 2004 - 2005 Alex Aizman
+ * maintained by open-iscsi@googlegroups.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/kfifo.h>
+#include <linux/delay.h>
+#include <asm/unaligned.h>
+#include <net/tcp.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_transport.h>
+#include "iscsi_proto.h"
+#include "scsi_transport_iscsi.h"
+#include "libiscsi.h"
+
+struct iscsi_session *
+class_to_transport_session(struct iscsi_cls_session *cls_session)
+{
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ return iscsi_hostdata(shost->hostdata);
+}
+EXPORT_SYMBOL_GPL(class_to_transport_session);
+
+#define INVALID_SN_DELTA 0xffff
+
+int
+iscsi_check_assign_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
+{
+ uint32_t max_cmdsn = be32_to_cpu(hdr->max_cmdsn);
+ uint32_t exp_cmdsn = be32_to_cpu(hdr->exp_cmdsn);
+
+ if (max_cmdsn < exp_cmdsn -1 &&
+ max_cmdsn > exp_cmdsn - INVALID_SN_DELTA)
+ return ISCSI_ERR_MAX_CMDSN;
+ if (max_cmdsn > session->max_cmdsn ||
+ max_cmdsn < session->max_cmdsn - INVALID_SN_DELTA)
+ session->max_cmdsn = max_cmdsn;
+ if (exp_cmdsn > session->exp_cmdsn ||
+ exp_cmdsn < session->exp_cmdsn - INVALID_SN_DELTA)
+ session->exp_cmdsn = exp_cmdsn;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iscsi_check_assign_cmdsn);
+
+void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask,
+ struct iscsi_data *hdr)
+{
+ struct iscsi_conn *conn = ctask->conn;
+
+ memset(hdr, 0, sizeof(struct iscsi_data));
+ hdr->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
+ hdr->datasn = cpu_to_be32(ctask->unsol_datasn);
+ ctask->unsol_datasn++;
+ hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
+ memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun));
+
+ hdr->itt = ctask->hdr->itt;
+ hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
+ hdr->offset = cpu_to_be32(ctask->unsol_offset);
+
+ if (ctask->unsol_count > conn->max_xmit_dlength) {
+ hton24(hdr->dlength, conn->max_xmit_dlength);
+ ctask->data_count = conn->max_xmit_dlength;
+ ctask->unsol_offset += ctask->data_count;
+ hdr->flags = 0;
+ } else {
+ hton24(hdr->dlength, ctask->unsol_count);
+ ctask->data_count = ctask->unsol_count;
+ hdr->flags = ISCSI_FLAG_CMD_FINAL;
+ }
+}
+EXPORT_SYMBOL_GPL(iscsi_prep_unsolicit_data_pdu);
+
+/**
+ * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu
+ * @ctask: iscsi cmd task
+ *
+ * Prep basic iSCSI PDU fields for a scsi cmd pdu. The LLD should set
+ * fields like dlength or final based on how much data it sends
+ */
+static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_conn *conn = ctask->conn;
+ struct iscsi_session *session = conn->session;
+ struct iscsi_cmd *hdr = ctask->hdr;
+ struct scsi_cmnd *sc = ctask->sc;
+
+ hdr->opcode = ISCSI_OP_SCSI_CMD;
+ hdr->flags = ISCSI_ATTR_SIMPLE;
+ int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
+ hdr->itt = build_itt(ctask->itt, conn->id, session->age);
+ hdr->data_length = cpu_to_be32(sc->request_bufflen);
+ hdr->cmdsn = cpu_to_be32(session->cmdsn);
+ session->cmdsn++;
+ hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
+ memcpy(hdr->cdb, sc->cmnd, sc->cmd_len);
+ memset(&hdr->cdb[sc->cmd_len], 0, MAX_COMMAND_SIZE - sc->cmd_len);
+
+ ctask->data_count = 0;
+ if (sc->sc_data_direction == DMA_TO_DEVICE) {
+ hdr->flags |= ISCSI_FLAG_CMD_WRITE;
+ /*
+ * Write counters:
+ *
+ * imm_count bytes to be sent right after
+ * SCSI PDU Header
+ *
+ * unsol_count bytes(as Data-Out) to be sent
+ * without R2T ack right after
+ * immediate data
+ *
+ * r2t_data_count bytes to be sent via R2T ack's
+ *
+ * pad_count bytes to be sent as zero-padding
+ */
+ ctask->imm_count = 0;
+ ctask->unsol_count = 0;
+ ctask->unsol_offset = 0;
+ ctask->unsol_datasn = 0;
+
+ if (session->imm_data_en) {
+ if (ctask->total_length >= session->first_burst)
+ ctask->imm_count = min(session->first_burst,
+ conn->max_xmit_dlength);
+ else
+ ctask->imm_count = min(ctask->total_length,
+ conn->max_xmit_dlength);
+ hton24(ctask->hdr->dlength, ctask->imm_count);
+ } else
+ zero_data(ctask->hdr->dlength);
+
+ if (!session->initial_r2t_en) {
+ ctask->unsol_count = min(session->first_burst,
+ ctask->total_length) - ctask->imm_count;
+ ctask->unsol_offset = ctask->imm_count;
+ }
+
+ if (!ctask->unsol_count)
+ /* No unsolicit Data-Out's */
+ ctask->hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ } else {
+ ctask->datasn = 0;
+ hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ zero_data(hdr->dlength);
+
+ if (sc->sc_data_direction == DMA_FROM_DEVICE)
+ hdr->flags |= ISCSI_FLAG_CMD_READ;
+ }
+
+ conn->scsicmd_pdus_cnt++;
+}
+EXPORT_SYMBOL_GPL(iscsi_prep_scsi_cmd_pdu);
+
+/**
+ * iscsi_complete_command - return command back to scsi-ml
+ * @ctask: iscsi cmd task
+ *
+ * Must be called with session lock.
+ * This function returns the scsi command to scsi-ml and returns
+ * the cmd task to the pool of available cmd tasks.
+ */
+static void iscsi_complete_command(struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_session *session = ctask->conn->session;
+ struct scsi_cmnd *sc = ctask->sc;
+
+ ctask->state = ISCSI_TASK_COMPLETED;
+ ctask->sc = NULL;
+ /* SCSI eh reuses commands to verify us */
+ sc->SCp.ptr = NULL;
+ list_del_init(&ctask->running);
+ __kfifo_put(session->cmdpool.queue, (void*)&ctask, sizeof(void*));
+ sc->scsi_done(sc);
+}
+
+static void __iscsi_get_ctask(struct iscsi_cmd_task *ctask)
+{
+ atomic_inc(&ctask->refcount);
+}
+
+static void iscsi_get_ctask(struct iscsi_cmd_task *ctask)
+{
+ spin_lock_bh(&ctask->conn->session->lock);
+ __iscsi_get_ctask(ctask);
+ spin_unlock_bh(&ctask->conn->session->lock);
+}
+
+static void __iscsi_put_ctask(struct iscsi_cmd_task *ctask)
+{
+ if (atomic_dec_and_test(&ctask->refcount))
+ iscsi_complete_command(ctask);
+}
+
+static void iscsi_put_ctask(struct iscsi_cmd_task *ctask)
+{
+ spin_lock_bh(&ctask->conn->session->lock);
+ __iscsi_put_ctask(ctask);
+ spin_unlock_bh(&ctask->conn->session->lock);
+}
+
+/**
+ * iscsi_cmd_rsp - SCSI Command Response processing
+ * @conn: iscsi connection
+ * @hdr: iscsi header
+ * @ctask: scsi command task
+ * @data: cmd data buffer
+ * @datalen: len of buffer
+ *
+ * iscsi_cmd_rsp sets up the scsi_cmnd fields based on the PDU and
+ * then completes the command and task.
+ **/
+static int iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ struct iscsi_cmd_task *ctask, char *data,
+ int datalen)
+{
+ int rc;
+ struct iscsi_cmd_rsp *rhdr = (struct iscsi_cmd_rsp *)hdr;
+ struct iscsi_session *session = conn->session;
+ struct scsi_cmnd *sc = ctask->sc;
+
+ rc = iscsi_check_assign_cmdsn(session, (struct iscsi_nopin*)rhdr);
+ if (rc) {
+ sc->result = DID_ERROR << 16;
+ goto out;
+ }
+
+ conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1;
+
+ sc->result = (DID_OK << 16) | rhdr->cmd_status;
+
+ if (rhdr->response != ISCSI_STATUS_CMD_COMPLETED) {
+ sc->result = DID_ERROR << 16;
+ goto out;
+ }
+
+ if (rhdr->cmd_status == SAM_STAT_CHECK_CONDITION) {
+ uint16_t senselen;
+
+ if (datalen < 2) {
+invalid_datalen:
+ printk(KERN_ERR "iscsi: Got CHECK_CONDITION but "
+ "invalid data buffer size of %d\n", datalen);
+ sc->result = DID_BAD_TARGET << 16;
+ goto out;
+ }
+
+ senselen = be16_to_cpu(get_unaligned((__be16 *) data));
+ if (datalen < senselen)
+ goto invalid_datalen;
+
+ memcpy(sc->sense_buffer, data + 2,
+ min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
+ debug_scsi("copied %d bytes of sense\n",
+ min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
+ }
+
+ if (sc->sc_data_direction == DMA_TO_DEVICE)
+ goto out;
+
+ if (rhdr->flags & ISCSI_FLAG_CMD_UNDERFLOW) {
+ int res_count = be32_to_cpu(rhdr->residual_count);
+
+ if (res_count > 0 && res_count <= sc->request_bufflen)
+ sc->resid = res_count;
+ else
+ sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
+ } else if (rhdr->flags & ISCSI_FLAG_CMD_BIDI_UNDERFLOW)
+ sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
+ else if (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW)
+ sc->resid = be32_to_cpu(rhdr->residual_count);
+
+out:
+ debug_scsi("done [sc %lx res %d itt 0x%x]\n",
+ (long)sc, sc->result, ctask->itt);
+ conn->scsirsp_pdus_cnt++;
+
+ __iscsi_put_ctask(ctask);
+ return rc;
+}
+
+static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+{
+ struct iscsi_tm_rsp *tmf = (struct iscsi_tm_rsp *)hdr;
+
+ conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
+ conn->tmfrsp_pdus_cnt++;
+
+ if (conn->tmabort_state != TMABORT_INITIAL)
+ return;
+
+ if (tmf->response == ISCSI_TMF_RSP_COMPLETE)
+ conn->tmabort_state = TMABORT_SUCCESS;
+ else if (tmf->response == ISCSI_TMF_RSP_NO_TASK)
+ conn->tmabort_state = TMABORT_NOT_FOUND;
+ else
+ conn->tmabort_state = TMABORT_FAILED;
+ wake_up(&conn->ehwait);
+}
+
+static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ char *data, int datalen)
+{
+ struct iscsi_reject *reject = (struct iscsi_reject *)hdr;
+ struct iscsi_hdr rejected_pdu;
+ uint32_t itt;
+
+ conn->exp_statsn = be32_to_cpu(reject->statsn) + 1;
+
+ if (reject->reason == ISCSI_REASON_DATA_DIGEST_ERROR) {
+ if (ntoh24(reject->dlength) > datalen)
+ return ISCSI_ERR_PROTO;
+
+ if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) {
+ memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr));
+ itt = get_itt(rejected_pdu.itt);
+ printk(KERN_ERR "itt 0x%x had pdu (op 0x%x) rejected "
+ "due to DataDigest error.\n", itt,
+ rejected_pdu.opcode);
+ }
+ }
+ return 0;
+}
+
+/**
+ * __iscsi_complete_pdu - complete pdu
+ * @conn: iscsi conn
+ * @hdr: iscsi header
+ * @data: data buffer
+ * @datalen: len of data buffer
+ *
+ * Completes pdu processing by freeing any resources allocated at
+ * queuecommand or send generic. session lock must be held and verify
+ * itt must have been called.
+ */
+int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ char *data, int datalen)
+{
+ struct iscsi_session *session = conn->session;
+ int opcode = hdr->opcode & ISCSI_OPCODE_MASK, rc = 0;
+ struct iscsi_cmd_task *ctask;
+ struct iscsi_mgmt_task *mtask;
+ uint32_t itt;
+
+ if (hdr->itt != RESERVED_ITT)
+ itt = get_itt(hdr->itt);
+ else
+ itt = ~0U;
+
+ if (itt < session->cmds_max) {
+ ctask = session->cmds[itt];
+
+ debug_scsi("cmdrsp [op 0x%x cid %d itt 0x%x len %d]\n",
+ opcode, conn->id, ctask->itt, datalen);
+
+ switch(opcode) {
+ case ISCSI_OP_SCSI_CMD_RSP:
+ BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
+ rc = iscsi_scsi_cmd_rsp(conn, hdr, ctask, data,
+ datalen);
+ break;
+ case ISCSI_OP_SCSI_DATA_IN:
+ BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
+ if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
+ conn->scsirsp_pdus_cnt++;
+ __iscsi_put_ctask(ctask);
+ }
+ break;
+ case ISCSI_OP_R2T:
+ /* LLD handles this for now */
+ break;
+ default:
+ rc = ISCSI_ERR_BAD_OPCODE;
+ break;
+ }
+ } else if (itt >= ISCSI_MGMT_ITT_OFFSET &&
+ itt < ISCSI_MGMT_ITT_OFFSET + session->mgmtpool_max) {
+ mtask = session->mgmt_cmds[itt - ISCSI_MGMT_ITT_OFFSET];
+
+ debug_scsi("immrsp [op 0x%x cid %d itt 0x%x len %d]\n",
+ opcode, conn->id, mtask->itt, datalen);
+
+ rc = iscsi_check_assign_cmdsn(session,
+ (struct iscsi_nopin*)hdr);
+ if (rc)
+ goto done;
+
+ switch(opcode) {
+ case ISCSI_OP_LOGOUT_RSP:
+ if (datalen) {
+ rc = ISCSI_ERR_PROTO;
+ break;
+ }
+ conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
+ /* fall through */
+ case ISCSI_OP_LOGIN_RSP:
+ case ISCSI_OP_TEXT_RSP:
+ /*
+ * login related PDU's exp_statsn is handled in
+ * userspace
+ */
+ if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
+ rc = ISCSI_ERR_CONN_FAILED;
+ list_del(&mtask->running);
+ if (conn->login_mtask != mtask)
+ __kfifo_put(session->mgmtpool.queue,
+ (void*)&mtask, sizeof(void*));
+ break;
+ case ISCSI_OP_SCSI_TMFUNC_RSP:
+ if (datalen) {
+ rc = ISCSI_ERR_PROTO;
+ break;
+ }
+
+ iscsi_tmf_rsp(conn, hdr);
+ break;
+ case ISCSI_OP_NOOP_IN:
+ if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) || datalen) {
+ rc = ISCSI_ERR_PROTO;
+ break;
+ }
+ conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
+
+ if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
+ rc = ISCSI_ERR_CONN_FAILED;
+ list_del(&mtask->running);
+ if (conn->login_mtask != mtask)
+ __kfifo_put(session->mgmtpool.queue,
+ (void*)&mtask, sizeof(void*));
+ break;
+ default:
+ rc = ISCSI_ERR_BAD_OPCODE;
+ break;
+ }
+ } else if (itt == ~0U) {
+ rc = iscsi_check_assign_cmdsn(session,
+ (struct iscsi_nopin*)hdr);
+ if (rc)
+ goto done;
+
+ switch(opcode) {
+ case ISCSI_OP_NOOP_IN:
+ if (datalen) {
+ rc = ISCSI_ERR_PROTO;
+ break;
+ }
+
+ if (hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG))
+ break;
+
+ if (iscsi_recv_pdu(conn->cls_conn, hdr, NULL, 0))
+ rc = ISCSI_ERR_CONN_FAILED;
+ break;
+ case ISCSI_OP_REJECT:
+ rc = iscsi_handle_reject(conn, hdr, data, datalen);
+ break;
+ case ISCSI_OP_ASYNC_EVENT:
+ conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
+ if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
+ rc = ISCSI_ERR_CONN_FAILED;
+ break;
+ default:
+ rc = ISCSI_ERR_BAD_OPCODE;
+ break;
+ }
+ } else
+ rc = ISCSI_ERR_BAD_ITT;
+
+done:
+ return rc;
+}
+EXPORT_SYMBOL_GPL(__iscsi_complete_pdu);
+
+int iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ char *data, int datalen)
+{
+ int rc;
+
+ spin_lock(&conn->session->lock);
+ rc = __iscsi_complete_pdu(conn, hdr, data, datalen);
+ spin_unlock(&conn->session->lock);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(iscsi_complete_pdu);
+
+/* verify itt (itt encoding: age+cid+itt) */
+int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ uint32_t *ret_itt)
+{
+ struct iscsi_session *session = conn->session;
+ struct iscsi_cmd_task *ctask;
+ uint32_t itt;
+
+ if (hdr->itt != RESERVED_ITT) {
+ if (((__force u32)hdr->itt & ISCSI_AGE_MASK) !=
+ (session->age << ISCSI_AGE_SHIFT)) {
+ printk(KERN_ERR "iscsi: received itt %x expected "
+ "session age (%x)\n", (__force u32)hdr->itt,
+ session->age & ISCSI_AGE_MASK);
+ return ISCSI_ERR_BAD_ITT;
+ }
+
+ if (((__force u32)hdr->itt & ISCSI_CID_MASK) !=
+ (conn->id << ISCSI_CID_SHIFT)) {
+ printk(KERN_ERR "iscsi: received itt %x, expected "
+ "CID (%x)\n", (__force u32)hdr->itt, conn->id);
+ return ISCSI_ERR_BAD_ITT;
+ }
+ itt = get_itt(hdr->itt);
+ } else
+ itt = ~0U;
+
+ if (itt < session->cmds_max) {
+ ctask = session->cmds[itt];
+
+ if (!ctask->sc) {
+ printk(KERN_INFO "iscsi: dropping ctask with "
+ "itt 0x%x\n", ctask->itt);
+ /* force drop */
+ return ISCSI_ERR_NO_SCSI_CMD;
+ }
+
+ if (ctask->sc->SCp.phase != session->age) {
+ printk(KERN_ERR "iscsi: ctask's session age %d, "
+ "expected %d\n", ctask->sc->SCp.phase,
+ session->age);
+ return ISCSI_ERR_SESSION_FAILED;
+ }
+ }
+
+ *ret_itt = itt;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iscsi_verify_itt);
+
+void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
+{
+ struct iscsi_session *session = conn->session;
+ unsigned long flags;
+
+ spin_lock_irqsave(&session->lock, flags);
+ if (session->state == ISCSI_STATE_FAILED) {
+ spin_unlock_irqrestore(&session->lock, flags);
+ return;
+ }
+
+ if (conn->stop_stage == 0)
+ session->state = ISCSI_STATE_FAILED;
+ spin_unlock_irqrestore(&session->lock, flags);
+ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
+ iscsi_conn_error(conn->cls_conn, err);
+}
+EXPORT_SYMBOL_GPL(iscsi_conn_failure);
+
+static int iscsi_xmit_mtask(struct iscsi_conn *conn)
+{
+ struct iscsi_hdr *hdr = conn->mtask->hdr;
+ int rc, was_logout = 0;
+
+ if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT) {
+ conn->session->state = ISCSI_STATE_IN_RECOVERY;
+ iscsi_block_session(session_to_cls(conn->session));
+ was_logout = 1;
+ }
+ rc = conn->session->tt->xmit_mgmt_task(conn, conn->mtask);
+ if (rc)
+ return rc;
+
+ /* done with this in-progress mtask */
+ conn->mtask = NULL;
+
+ if (was_logout) {
+ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+ return -ENODATA;
+ }
+ return 0;
+}
+
+/**
+ * iscsi_data_xmit - xmit any command into the scheduled connection
+ * @conn: iscsi connection
+ *
+ * Notes:
+ * The function can return -EAGAIN in which case the caller must
+ * re-schedule it again later or recover. '0' return code means
+ * successful xmit.
+ **/
+static int iscsi_data_xmit(struct iscsi_conn *conn)
+{
+ struct iscsi_transport *tt;
+ int rc = 0;
+
+ if (unlikely(conn->suspend_tx)) {
+ debug_scsi("conn %d Tx suspended!\n", conn->id);
+ return -ENODATA;
+ }
+ tt = conn->session->tt;
+
+ /*
+ * Transmit in the following order:
+ *
+ * 1) un-finished xmit (ctask or mtask)
+ * 2) immediate control PDUs
+ * 3) write data
+ * 4) SCSI commands
+ * 5) non-immediate control PDUs
+ *
+ * No need to lock around __kfifo_get as long as
+ * there's one producer and one consumer.
+ */
+
+ BUG_ON(conn->ctask && conn->mtask);
+
+ if (conn->ctask) {
+ iscsi_get_ctask(conn->ctask);
+ rc = tt->xmit_cmd_task(conn, conn->ctask);
+ iscsi_put_ctask(conn->ctask);
+ if (rc)
+ goto again;
+ /* done with this in-progress ctask */
+ conn->ctask = NULL;
+ }
+ if (conn->mtask) {
+ rc = iscsi_xmit_mtask(conn);
+ if (rc)
+ goto again;
+ }
+
+ /* process immediate first */
+ if (unlikely(__kfifo_len(conn->immqueue))) {
+ while (__kfifo_get(conn->immqueue, (void*)&conn->mtask,
+ sizeof(void*))) {
+ spin_lock_bh(&conn->session->lock);
+ list_add_tail(&conn->mtask->running,
+ &conn->mgmt_run_list);
+ spin_unlock_bh(&conn->session->lock);
+ rc = iscsi_xmit_mtask(conn);
+ if (rc)
+ goto again;
+ }
+ }
+
+ /* process command queue */
+ spin_lock_bh(&conn->session->lock);
+ while (!list_empty(&conn->xmitqueue)) {
+ /*
+ * iscsi tcp may readd the task to the xmitqueue to send
+ * write data
+ */
+ conn->ctask = list_entry(conn->xmitqueue.next,
+ struct iscsi_cmd_task, running);
+ conn->ctask->state = ISCSI_TASK_RUNNING;
+ list_move_tail(conn->xmitqueue.next, &conn->run_list);
+ __iscsi_get_ctask(conn->ctask);
+ spin_unlock_bh(&conn->session->lock);
+
+ rc = tt->xmit_cmd_task(conn, conn->ctask);
+
+ spin_lock_bh(&conn->session->lock);
+ __iscsi_put_ctask(conn->ctask);
+ if (rc) {
+ spin_unlock_bh(&conn->session->lock);
+ goto again;
+ }
+ }
+ spin_unlock_bh(&conn->session->lock);
+ /* done with this ctask */
+ conn->ctask = NULL;
+
+ /* process the rest control plane PDUs, if any */
+ if (unlikely(__kfifo_len(conn->mgmtqueue))) {
+ while (__kfifo_get(conn->mgmtqueue, (void*)&conn->mtask,
+ sizeof(void*))) {
+ spin_lock_bh(&conn->session->lock);
+ list_add_tail(&conn->mtask->running,
+ &conn->mgmt_run_list);
+ spin_unlock_bh(&conn->session->lock);
+ rc = iscsi_xmit_mtask(conn);
+ if (rc)
+ goto again;
+ }
+ }
+
+ return -ENODATA;
+
+again:
+ if (unlikely(conn->suspend_tx))
+ return -ENODATA;
+
+ return rc;
+}
+
+static void iscsi_xmitworker(struct work_struct *work)
+{
+ struct iscsi_conn *conn =
+ container_of(work, struct iscsi_conn, xmitwork);
+ int rc;
+ /*
+ * serialize Xmit worker on a per-connection basis.
+ */
+ mutex_lock(&conn->xmitmutex);
+ do {
+ rc = iscsi_data_xmit(conn);
+ } while (rc >= 0 || rc == -EAGAIN);
+ mutex_unlock(&conn->xmitmutex);
+}
+
+enum {
+ FAILURE_BAD_HOST = 1,
+ FAILURE_SESSION_FAILED,
+ FAILURE_SESSION_FREED,
+ FAILURE_WINDOW_CLOSED,
+ FAILURE_OOM,
+ FAILURE_SESSION_TERMINATE,
+ FAILURE_SESSION_IN_RECOVERY,
+ FAILURE_SESSION_RECOVERY_TIMEOUT,
+};
+
+int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
+{
+ struct Scsi_Host *host;
+ int reason = 0;
+ struct iscsi_session *session;
+ struct iscsi_conn *conn;
+ struct iscsi_cmd_task *ctask = NULL;
+
+ sc->scsi_done = done;
+ sc->result = 0;
+ sc->SCp.ptr = NULL;
+
+ host = sc->device->host;
+ session = iscsi_hostdata(host->hostdata);
+
+ spin_lock(&session->lock);
+
+ /*
+ * ISCSI_STATE_FAILED is a temp. state. The recovery
+ * code will decide what is best to do with command queued
+ * during this time
+ */
+ if (session->state != ISCSI_STATE_LOGGED_IN &&
+ session->state != ISCSI_STATE_FAILED) {
+ /*
+ * to handle the race between when we set the recovery state
+ * and block the session we requeue here (commands could
+ * be entering our queuecommand while a block is starting
+ * up because the block code is not locked)
+ */
+ if (session->state == ISCSI_STATE_IN_RECOVERY) {
+ reason = FAILURE_SESSION_IN_RECOVERY;
+ goto reject;
+ }
+
+ if (session->state == ISCSI_STATE_RECOVERY_FAILED)
+ reason = FAILURE_SESSION_RECOVERY_TIMEOUT;
+ else if (session->state == ISCSI_STATE_TERMINATE)
+ reason = FAILURE_SESSION_TERMINATE;
+ else
+ reason = FAILURE_SESSION_FREED;
+ goto fault;
+ }
+
+ /*
+ * Check for iSCSI window and take care of CmdSN wrap-around
+ */
+ if ((int)(session->max_cmdsn - session->cmdsn) < 0) {
+ reason = FAILURE_WINDOW_CLOSED;
+ goto reject;
+ }
+
+ conn = session->leadconn;
+ if (!conn) {
+ reason = FAILURE_SESSION_FREED;
+ goto fault;
+ }
+
+ if (!__kfifo_get(session->cmdpool.queue, (void*)&ctask,
+ sizeof(void*))) {
+ reason = FAILURE_OOM;
+ goto reject;
+ }
+ sc->SCp.phase = session->age;
+ sc->SCp.ptr = (char *)ctask;
+
+ atomic_set(&ctask->refcount, 1);
+ ctask->state = ISCSI_TASK_PENDING;
+ ctask->mtask = NULL;
+ ctask->conn = conn;
+ ctask->sc = sc;
+ INIT_LIST_HEAD(&ctask->running);
+ ctask->total_length = sc->request_bufflen;
+ iscsi_prep_scsi_cmd_pdu(ctask);
+
+ session->tt->init_cmd_task(ctask);
+
+ list_add_tail(&ctask->running, &conn->xmitqueue);
+ debug_scsi(
+ "ctask enq [%s cid %d sc %p cdb 0x%x itt 0x%x len %d cmdsn %d "
+ "win %d]\n",
+ sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",
+ conn->id, sc, sc->cmnd[0], ctask->itt, sc->request_bufflen,
+ session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
+ spin_unlock(&session->lock);
+
+ scsi_queue_work(host, &conn->xmitwork);
+ return 0;
+
+reject:
+ spin_unlock(&session->lock);
+ debug_scsi("cmd 0x%x rejected (%d)\n", sc->cmnd[0], reason);
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+fault:
+ spin_unlock(&session->lock);
+ printk(KERN_ERR "iscsi: cmd 0x%x is not queued (%d)\n",
+ sc->cmnd[0], reason);
+ sc->result = (DID_NO_CONNECT << 16);
+ sc->resid = sc->request_bufflen;
+ sc->scsi_done(sc);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iscsi_queuecommand);
+
+int iscsi_change_queue_depth(struct scsi_device *sdev, int depth)
+{
+ if (depth > ISCSI_MAX_CMD_PER_LUN)
+ depth = ISCSI_MAX_CMD_PER_LUN;
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+ return sdev->queue_depth;
+}
+EXPORT_SYMBOL_GPL(iscsi_change_queue_depth);
+
+static int
+iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ char *data, uint32_t data_size)
+{
+ struct iscsi_session *session = conn->session;
+ struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr;
+ struct iscsi_mgmt_task *mtask;
+
+ spin_lock_bh(&session->lock);
+ if (session->state == ISCSI_STATE_TERMINATE) {
+ spin_unlock_bh(&session->lock);
+ return -EPERM;
+ }
+ if (hdr->opcode == (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) ||
+ hdr->opcode == (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
+ /*
+ * Login and Text are sent serially, in
+ * request-followed-by-response sequence.
+ * Same mtask can be used. Same ITT must be used.
+ * Note that login_mtask is preallocated at conn_create().
+ */
+ mtask = conn->login_mtask;
+ else {
+ BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
+ BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
+
+ nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
+ if (!__kfifo_get(session->mgmtpool.queue,
+ (void*)&mtask, sizeof(void*))) {
+ spin_unlock_bh(&session->lock);
+ return -ENOSPC;
+ }
+ }
+
+ /*
+ * pre-format CmdSN for outgoing PDU.
+ */
+ if (hdr->itt != RESERVED_ITT) {
+ hdr->itt = build_itt(mtask->itt, conn->id, session->age);
+ nop->cmdsn = cpu_to_be32(session->cmdsn);
+ if (conn->c_stage == ISCSI_CONN_STARTED &&
+ !(hdr->opcode & ISCSI_OP_IMMEDIATE))
+ session->cmdsn++;
+ } else
+ /* do not advance CmdSN */
+ nop->cmdsn = cpu_to_be32(session->cmdsn);
+
+ if (data_size) {
+ memcpy(mtask->data, data, data_size);
+ mtask->data_count = data_size;
+ } else
+ mtask->data_count = 0;
+
+ INIT_LIST_HEAD(&mtask->running);
+ memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr));
+ if (session->tt->init_mgmt_task)
+ session->tt->init_mgmt_task(conn, mtask, data, data_size);
+ spin_unlock_bh(&session->lock);
+
+ debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n",
+ hdr->opcode, hdr->itt, data_size);
+
+ /*
+ * since send_pdu() could be called at least from two contexts,
+ * we need to serialize __kfifo_put, so we don't have to take
+ * additional lock on fast data-path
+ */
+ if (hdr->opcode & ISCSI_OP_IMMEDIATE)
+ __kfifo_put(conn->immqueue, (void*)&mtask, sizeof(void*));
+ else
+ __kfifo_put(conn->mgmtqueue, (void*)&mtask, sizeof(void*));
+
+ scsi_queue_work(session->host, &conn->xmitwork);
+ return 0;
+}
+
+int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr,
+ char *data, uint32_t data_size)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ int rc;
+
+ mutex_lock(&conn->xmitmutex);
+ rc = iscsi_conn_send_generic(conn, hdr, data, data_size);
+ mutex_unlock(&conn->xmitmutex);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
+
+void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session)
+{
+ struct iscsi_session *session = class_to_transport_session(cls_session);
+ struct iscsi_conn *conn = session->leadconn;
+
+ spin_lock_bh(&session->lock);
+ if (session->state != ISCSI_STATE_LOGGED_IN) {
+ session->state = ISCSI_STATE_RECOVERY_FAILED;
+ if (conn)
+ wake_up(&conn->ehwait);
+ }
+ spin_unlock_bh(&session->lock);
+}
+EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout);
+
+int iscsi_eh_host_reset(struct scsi_cmnd *sc)
+{
+ struct Scsi_Host *host = sc->device->host;
+ struct iscsi_session *session = iscsi_hostdata(host->hostdata);
+ struct iscsi_conn *conn = session->leadconn;
+ int fail_session = 0;
+
+ spin_lock_bh(&session->lock);
+ if (session->state == ISCSI_STATE_TERMINATE) {
+failed:
+ debug_scsi("failing host reset: session terminated "
+ "[CID %d age %d]\n", conn->id, session->age);
+ spin_unlock_bh(&session->lock);
+ return FAILED;
+ }
+
+ if (sc->SCp.phase == session->age) {
+ debug_scsi("failing connection CID %d due to SCSI host reset\n",
+ conn->id);
+ fail_session = 1;
+ }
+ spin_unlock_bh(&session->lock);
+
+ /*
+ * we drop the lock here but the leadconn cannot be destoyed while
+ * we are in the scsi eh
+ */
+ if (fail_session)
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+
+ debug_scsi("iscsi_eh_host_reset wait for relogin\n");
+ wait_event_interruptible(conn->ehwait,
+ session->state == ISCSI_STATE_TERMINATE ||
+ session->state == ISCSI_STATE_LOGGED_IN ||
+ session->state == ISCSI_STATE_RECOVERY_FAILED);
+ if (signal_pending(current))
+ flush_signals(current);
+
+ spin_lock_bh(&session->lock);
+ if (session->state == ISCSI_STATE_LOGGED_IN)
+ printk(KERN_INFO "iscsi: host reset succeeded\n");
+ else
+ goto failed;
+ spin_unlock_bh(&session->lock);
+
+ return SUCCESS;
+}
+EXPORT_SYMBOL_GPL(iscsi_eh_host_reset);
+
+static void iscsi_tmabort_timedout(unsigned long data)
+{
+ struct iscsi_cmd_task *ctask = (struct iscsi_cmd_task *)data;
+ struct iscsi_conn *conn = ctask->conn;
+ struct iscsi_session *session = conn->session;
+
+ spin_lock(&session->lock);
+ if (conn->tmabort_state == TMABORT_INITIAL) {
+ conn->tmabort_state = TMABORT_TIMEDOUT;
+ debug_scsi("tmabort timedout [sc %p itt 0x%x]\n",
+ ctask->sc, ctask->itt);
+ /* unblock eh_abort() */
+ wake_up(&conn->ehwait);
+ }
+ spin_unlock(&session->lock);
+}
+
+/* must be called with the mutex lock */
+static int iscsi_exec_abort_task(struct scsi_cmnd *sc,
+ struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_conn *conn = ctask->conn;
+ struct iscsi_session *session = conn->session;
+ struct iscsi_tm *hdr = &conn->tmhdr;
+ int rc;
+
+ /*
+ * ctask timed out but session is OK requests must be serialized.
+ */
+ memset(hdr, 0, sizeof(struct iscsi_tm));
+ hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
+ hdr->flags = ISCSI_TM_FUNC_ABORT_TASK;
+ hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun));
+ hdr->rtt = ctask->hdr->itt;
+ hdr->refcmdsn = ctask->hdr->cmdsn;
+
+ rc = iscsi_conn_send_generic(conn, (struct iscsi_hdr *)hdr,
+ NULL, 0);
+ if (rc) {
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ debug_scsi("abort sent failure [itt 0x%x] %d\n", ctask->itt,
+ rc);
+ return rc;
+ }
+
+ debug_scsi("abort sent [itt 0x%x]\n", ctask->itt);
+
+ spin_lock_bh(&session->lock);
+ ctask->mtask = (struct iscsi_mgmt_task *)
+ session->mgmt_cmds[get_itt(hdr->itt) -
+ ISCSI_MGMT_ITT_OFFSET];
+
+ if (conn->tmabort_state == TMABORT_INITIAL) {
+ conn->tmfcmd_pdus_cnt++;
+ conn->tmabort_timer.expires = 10*HZ + jiffies;
+ conn->tmabort_timer.function = iscsi_tmabort_timedout;
+ conn->tmabort_timer.data = (unsigned long)ctask;
+ add_timer(&conn->tmabort_timer);
+ debug_scsi("abort set timeout [itt 0x%x]\n", ctask->itt);
+ }
+ spin_unlock_bh(&session->lock);
+ mutex_unlock(&conn->xmitmutex);
+
+ /*
+ * block eh thread until:
+ *
+ * 1) abort response
+ * 2) abort timeout
+ * 3) session is terminated or restarted or userspace has
+ * given up on recovery
+ */
+ wait_event_interruptible(conn->ehwait,
+ sc->SCp.phase != session->age ||
+ session->state != ISCSI_STATE_LOGGED_IN ||
+ conn->tmabort_state != TMABORT_INITIAL);
+ if (signal_pending(current))
+ flush_signals(current);
+ del_timer_sync(&conn->tmabort_timer);
+
+ mutex_lock(&conn->xmitmutex);
+ return 0;
+}
+
+/*
+ * xmit mutex and session lock must be held
+ */
+static struct iscsi_mgmt_task *
+iscsi_remove_mgmt_task(struct kfifo *fifo, uint32_t itt)
+{
+ int i, nr_tasks = __kfifo_len(fifo) / sizeof(void*);
+ struct iscsi_mgmt_task *task;
+
+ debug_scsi("searching %d tasks\n", nr_tasks);
+
+ for (i = 0; i < nr_tasks; i++) {
+ __kfifo_get(fifo, (void*)&task, sizeof(void*));
+ debug_scsi("check task %u\n", task->itt);
+
+ if (task->itt == itt) {
+ debug_scsi("matched task\n");
+ return task;
+ }
+
+ __kfifo_put(fifo, (void*)&task, sizeof(void*));
+ }
+ return NULL;
+}
+
+static int iscsi_ctask_mtask_cleanup(struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_conn *conn = ctask->conn;
+ struct iscsi_session *session = conn->session;
+
+ if (!ctask->mtask)
+ return -EINVAL;
+
+ if (!iscsi_remove_mgmt_task(conn->immqueue, ctask->mtask->itt))
+ list_del(&ctask->mtask->running);
+ __kfifo_put(session->mgmtpool.queue, (void*)&ctask->mtask,
+ sizeof(void*));
+ ctask->mtask = NULL;
+ return 0;
+}
+
+/*
+ * session lock and xmitmutex must be held
+ */
+static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+ int err)
+{
+ struct scsi_cmnd *sc;
+
+ sc = ctask->sc;
+ if (!sc)
+ return;
+
+ conn->session->tt->cleanup_cmd_task(conn, ctask);
+ iscsi_ctask_mtask_cleanup(ctask);
+
+ sc->result = err;
+ sc->resid = sc->request_bufflen;
+ /* release ref from queuecommand */
+ __iscsi_put_ctask(ctask);
+}
+
+int iscsi_eh_abort(struct scsi_cmnd *sc)
+{
+ struct iscsi_cmd_task *ctask;
+ struct iscsi_conn *conn;
+ struct iscsi_session *session;
+ int rc;
+
+ /*
+ * if session was ISCSI_STATE_IN_RECOVERY then we may not have
+ * got the command.
+ */
+ if (!sc->SCp.ptr) {
+ debug_scsi("sc never reached iscsi layer or it completed.\n");
+ return SUCCESS;
+ }
+
+ ctask = (struct iscsi_cmd_task *)sc->SCp.ptr;
+ conn = ctask->conn;
+ session = conn->session;
+
+ conn->eh_abort_cnt++;
+ debug_scsi("aborting [sc %p itt 0x%x]\n", sc, ctask->itt);
+
+ mutex_lock(&conn->xmitmutex);
+ spin_lock_bh(&session->lock);
+
+ /*
+ * If we are not logged in or we have started a new session
+ * then let the host reset code handle this
+ */
+ if (session->state != ISCSI_STATE_LOGGED_IN ||
+ sc->SCp.phase != session->age)
+ goto failed;
+
+ /* ctask completed before time out */
+ if (!ctask->sc) {
+ spin_unlock_bh(&session->lock);
+ debug_scsi("sc completed while abort in progress\n");
+ goto success_rel_mutex;
+ }
+
+ /* what should we do here ? */
+ if (conn->ctask == ctask) {
+ printk(KERN_INFO "iscsi: sc %p itt 0x%x partially sent. "
+ "Failing abort\n", sc, ctask->itt);
+ goto failed;
+ }
+
+ if (ctask->state == ISCSI_TASK_PENDING)
+ goto success_cleanup;
+
+ conn->tmabort_state = TMABORT_INITIAL;
+
+ spin_unlock_bh(&session->lock);
+ rc = iscsi_exec_abort_task(sc, ctask);
+ spin_lock_bh(&session->lock);
+
+ if (rc || sc->SCp.phase != session->age ||
+ session->state != ISCSI_STATE_LOGGED_IN)
+ goto failed;
+ iscsi_ctask_mtask_cleanup(ctask);
+
+ switch (conn->tmabort_state) {
+ case TMABORT_SUCCESS:
+ goto success_cleanup;
+ case TMABORT_NOT_FOUND:
+ if (!ctask->sc) {
+ /* ctask completed before tmf abort response */
+ spin_unlock_bh(&session->lock);
+ debug_scsi("sc completed while abort in progress\n");
+ goto success_rel_mutex;
+ }
+ /* fall through */
+ default:
+ /* timedout or failed */
+ spin_unlock_bh(&session->lock);
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ spin_lock_bh(&session->lock);
+ goto failed;
+ }
+
+success_cleanup:
+ debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
+ spin_unlock_bh(&session->lock);
+
+ /*
+ * clean up task if aborted. we have the xmitmutex so grab
+ * the recv lock as a writer
+ */
+ write_lock_bh(conn->recv_lock);
+ spin_lock(&session->lock);
+ fail_command(conn, ctask, DID_ABORT << 16);
+ spin_unlock(&session->lock);
+ write_unlock_bh(conn->recv_lock);
+
+success_rel_mutex:
+ mutex_unlock(&conn->xmitmutex);
+ return SUCCESS;
+
+failed:
+ spin_unlock_bh(&session->lock);
+ mutex_unlock(&conn->xmitmutex);
+
+ debug_scsi("abort failed [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
+ return FAILED;
+}
+EXPORT_SYMBOL_GPL(iscsi_eh_abort);
+
+int
+iscsi_pool_init(struct iscsi_queue *q, int max, void ***items, int item_size)
+{
+ int i;
+
+ *items = kmalloc(max * sizeof(void*), GFP_KERNEL);
+ if (*items == NULL)
+ return -ENOMEM;
+
+ q->max = max;
+ q->pool = kmalloc(max * sizeof(void*), GFP_KERNEL);
+ if (q->pool == NULL) {
+ kfree(*items);
+ return -ENOMEM;
+ }
+
+ q->queue = kfifo_init((void*)q->pool, max * sizeof(void*),
+ GFP_KERNEL, NULL);
+ if (q->queue == ERR_PTR(-ENOMEM)) {
+ kfree(q->pool);
+ kfree(*items);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < max; i++) {
+ q->pool[i] = kmalloc(item_size, GFP_KERNEL);
+ if (q->pool[i] == NULL) {
+ int j;
+
+ for (j = 0; j < i; j++)
+ kfree(q->pool[j]);
+
+ kfifo_free(q->queue);
+ kfree(q->pool);
+ kfree(*items);
+ return -ENOMEM;
+ }
+ memset(q->pool[i], 0, item_size);
+ (*items)[i] = q->pool[i];
+ __kfifo_put(q->queue, (void*)&q->pool[i], sizeof(void*));
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iscsi_pool_init);
+
+void iscsi_pool_free(struct iscsi_queue *q, void **items)
+{
+ int i;
+
+ for (i = 0; i < q->max; i++)
+ kfree(items[i]);
+ kfree(q->pool);
+ kfree(items);
+}
+EXPORT_SYMBOL_GPL(iscsi_pool_free);
+
+/*
+ * iSCSI Session's hostdata organization:
+ *
+ * *------------------* <== hostdata_session(host->hostdata)
+ * | ptr to class sess|
+ * |------------------| <== iscsi_hostdata(host->hostdata)
+ * | iscsi_session |
+ * *------------------*
+ */
+
+#define hostdata_privsize(_sz) (sizeof(unsigned long) + _sz + \
+ _sz % sizeof(unsigned long))
+
+#define hostdata_session(_hostdata) (iscsi_ptr(*(unsigned long *)_hostdata))
+
+/**
+ * iscsi_session_setup - create iscsi cls session and host and session
+ * @scsit: scsi transport template
+ * @iscsit: iscsi transport template
+ * @initial_cmdsn: initial CmdSN
+ * @hostno: host no allocated
+ *
+ * This can be used by software iscsi_transports that allocate
+ * a session per scsi host.
+ **/
+struct iscsi_cls_session *
+iscsi_session_setup(struct iscsi_transport *iscsit,
+ struct scsi_transport_template *scsit,
+ int cmd_task_size, int mgmt_task_size,
+ uint32_t initial_cmdsn, uint32_t *hostno)
+{
+ struct Scsi_Host *shost;
+ struct iscsi_session *session;
+ struct iscsi_cls_session *cls_session;
+ int cmd_i;
+
+ shost = scsi_host_alloc(iscsit->host_template,
+ hostdata_privsize(sizeof(*session)));
+ if (!shost)
+ return NULL;
+
+ shost->max_id = 1;
+ shost->max_channel = 0;
+ shost->max_lun = iscsit->max_lun;
+ shost->max_cmd_len = iscsit->max_cmd_len;
+ shost->transportt = scsit;
+ shost->transportt->create_work_queue = 1;
+ *hostno = shost->host_no;
+
+ session = iscsi_hostdata(shost->hostdata);
+ memset(session, 0, sizeof(struct iscsi_session));
+ session->host = shost;
+ session->state = ISCSI_STATE_FREE;
+ session->mgmtpool_max = ISCSI_MGMT_CMDS_MAX;
+ session->cmds_max = ISCSI_XMIT_CMDS_MAX;
+ session->cmdsn = initial_cmdsn;
+ session->exp_cmdsn = initial_cmdsn + 1;
+ session->max_cmdsn = initial_cmdsn + 1;
+ session->max_r2t = 1;
+ session->tt = iscsit;
+
+ /* initialize SCSI PDU commands pool */
+ if (iscsi_pool_init(&session->cmdpool, session->cmds_max,
+ (void***)&session->cmds,
+ cmd_task_size + sizeof(struct iscsi_cmd_task)))
+ goto cmdpool_alloc_fail;
+
+ /* pre-format cmds pool with ITT */
+ for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
+ struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
+
+ if (cmd_task_size)
+ ctask->dd_data = &ctask[1];
+ ctask->itt = cmd_i;
+ INIT_LIST_HEAD(&ctask->running);
+ }
+
+ spin_lock_init(&session->lock);
+
+ /* initialize immediate command pool */
+ if (iscsi_pool_init(&session->mgmtpool, session->mgmtpool_max,
+ (void***)&session->mgmt_cmds,
+ mgmt_task_size + sizeof(struct iscsi_mgmt_task)))
+ goto mgmtpool_alloc_fail;
+
+
+ /* pre-format immediate cmds pool with ITT */
+ for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) {
+ struct iscsi_mgmt_task *mtask = session->mgmt_cmds[cmd_i];
+
+ if (mgmt_task_size)
+ mtask->dd_data = &mtask[1];
+ mtask->itt = ISCSI_MGMT_ITT_OFFSET + cmd_i;
+ INIT_LIST_HEAD(&mtask->running);
+ }
+
+ if (scsi_add_host(shost, NULL))
+ goto add_host_fail;
+
+ if (!try_module_get(iscsit->owner))
+ goto cls_session_fail;
+
+ cls_session = iscsi_create_session(shost, iscsit, 0);
+ if (!cls_session)
+ goto module_put;
+ *(unsigned long*)shost->hostdata = (unsigned long)cls_session;
+
+ return cls_session;
+
+module_put:
+ module_put(iscsit->owner);
+cls_session_fail:
+ scsi_remove_host(shost);
+add_host_fail:
+ iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds);
+mgmtpool_alloc_fail:
+ iscsi_pool_free(&session->cmdpool, (void**)session->cmds);
+cmdpool_alloc_fail:
+ scsi_host_put(shost);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(iscsi_session_setup);
+
+/**
+ * iscsi_session_teardown - destroy session, host, and cls_session
+ * shost: scsi host
+ *
+ * This can be used by software iscsi_transports that allocate
+ * a session per scsi host.
+ **/
+void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
+{
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+ struct module *owner = cls_session->transport->owner;
+
+ scsi_remove_host(shost);
+
+ iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds);
+ iscsi_pool_free(&session->cmdpool, (void**)session->cmds);
+
+ kfree(session->targetname);
+
+ iscsi_destroy_session(cls_session);
+ scsi_host_put(shost);
+ module_put(owner);
+}
+EXPORT_SYMBOL_GPL(iscsi_session_teardown);
+
+/**
+ * iscsi_conn_setup - create iscsi_cls_conn and iscsi_conn
+ * @cls_session: iscsi_cls_session
+ * @conn_idx: cid
+ **/
+struct iscsi_cls_conn *
+iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
+{
+ struct iscsi_session *session = class_to_transport_session(cls_session);
+ struct iscsi_conn *conn;
+ struct iscsi_cls_conn *cls_conn;
+ char *data;
+
+ cls_conn = iscsi_create_conn(cls_session, conn_idx);
+ if (!cls_conn)
+ return NULL;
+ conn = cls_conn->dd_data;
+ memset(conn, 0, sizeof(*conn));
+
+ conn->session = session;
+ conn->cls_conn = cls_conn;
+ conn->c_stage = ISCSI_CONN_INITIAL_STAGE;
+ conn->id = conn_idx;
+ conn->exp_statsn = 0;
+ conn->tmabort_state = TMABORT_INITIAL;
+ INIT_LIST_HEAD(&conn->run_list);
+ INIT_LIST_HEAD(&conn->mgmt_run_list);
+ INIT_LIST_HEAD(&conn->xmitqueue);
+
+ /* initialize general immediate & non-immediate PDU commands queue */
+ conn->immqueue = kfifo_alloc(session->mgmtpool_max * sizeof(void*),
+ GFP_KERNEL, NULL);
+ if (conn->immqueue == ERR_PTR(-ENOMEM))
+ goto immqueue_alloc_fail;
+
+ conn->mgmtqueue = kfifo_alloc(session->mgmtpool_max * sizeof(void*),
+ GFP_KERNEL, NULL);
+ if (conn->mgmtqueue == ERR_PTR(-ENOMEM))
+ goto mgmtqueue_alloc_fail;
+
+ INIT_WORK(&conn->xmitwork, iscsi_xmitworker);
+
+ /* allocate login_mtask used for the login/text sequences */
+ spin_lock_bh(&session->lock);
+ if (!__kfifo_get(session->mgmtpool.queue,
+ (void*)&conn->login_mtask,
+ sizeof(void*))) {
+ spin_unlock_bh(&session->lock);
+ goto login_mtask_alloc_fail;
+ }
+ spin_unlock_bh(&session->lock);
+
+ data = kmalloc(ISCSI_DEF_MAX_RECV_SEG_LEN, GFP_KERNEL);
+ if (!data)
+ goto login_mtask_data_alloc_fail;
+ conn->login_mtask->data = conn->data = data;
+
+ init_timer(&conn->tmabort_timer);
+ mutex_init(&conn->xmitmutex);
+ init_waitqueue_head(&conn->ehwait);
+
+ return cls_conn;
+
+login_mtask_data_alloc_fail:
+ __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask,
+ sizeof(void*));
+login_mtask_alloc_fail:
+ kfifo_free(conn->mgmtqueue);
+mgmtqueue_alloc_fail:
+ kfifo_free(conn->immqueue);
+immqueue_alloc_fail:
+ iscsi_destroy_conn(cls_conn);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(iscsi_conn_setup);
+
+/**
+ * iscsi_conn_teardown - teardown iscsi connection
+ * cls_conn: iscsi class connection
+ *
+ * TODO: we may need to make this into a two step process
+ * like scsi-mls remove + put host
+ */
+void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_session *session = conn->session;
+ unsigned long flags;
+
+ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+ mutex_lock(&conn->xmitmutex);
+
+ spin_lock_bh(&session->lock);
+ conn->c_stage = ISCSI_CONN_CLEANUP_WAIT;
+ if (session->leadconn == conn) {
+ /*
+ * leading connection? then give up on recovery.
+ */
+ session->state = ISCSI_STATE_TERMINATE;
+ wake_up(&conn->ehwait);
+ }
+ spin_unlock_bh(&session->lock);
+
+ mutex_unlock(&conn->xmitmutex);
+
+ /*
+ * Block until all in-progress commands for this connection
+ * time out or fail.
+ */
+ for (;;) {
+ spin_lock_irqsave(session->host->host_lock, flags);
+ if (!session->host->host_busy) { /* OK for ERL == 0 */
+ spin_unlock_irqrestore(session->host->host_lock, flags);
+ break;
+ }
+ spin_unlock_irqrestore(session->host->host_lock, flags);
+ msleep_interruptible(500);
+ printk(KERN_INFO "iscsi: scsi conn_destroy(): host_busy %d "
+ "host_failed %d\n", session->host->host_busy,
+ session->host->host_failed);
+ /*
+ * force eh_abort() to unblock
+ */
+ wake_up(&conn->ehwait);
+ }
+
+ /* flush queued up work because we free the connection below */
+ scsi_flush_work(session->host);
+
+ spin_lock_bh(&session->lock);
+ kfree(conn->data);
+ kfree(conn->persistent_address);
+ __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask,
+ sizeof(void*));
+ if (session->leadconn == conn) {
+ session->leadconn = NULL;
+ /* no connections exits.. reset sequencing */
+ session->cmdsn = session->max_cmdsn = session->exp_cmdsn = 1;
+ }
+ spin_unlock_bh(&session->lock);
+
+ kfifo_free(conn->immqueue);
+ kfifo_free(conn->mgmtqueue);
+
+ iscsi_destroy_conn(cls_conn);
+}
+EXPORT_SYMBOL_GPL(iscsi_conn_teardown);
+
+int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_session *session = conn->session;
+
+ if (!session) {
+ printk(KERN_ERR "iscsi: can't start unbound connection\n");
+ return -EPERM;
+ }
+
+ if ((session->imm_data_en || !session->initial_r2t_en) &&
+ session->first_burst > session->max_burst) {
+ printk("iscsi: invalid burst lengths: "
+ "first_burst %d max_burst %d\n",
+ session->first_burst, session->max_burst);
+ return -EINVAL;
+ }
+
+ spin_lock_bh(&session->lock);
+ conn->c_stage = ISCSI_CONN_STARTED;
+ session->state = ISCSI_STATE_LOGGED_IN;
+
+ switch(conn->stop_stage) {
+ case STOP_CONN_RECOVER:
+ /*
+ * unblock eh_abort() if it is blocked. re-try all
+ * commands after successful recovery
+ */
+ conn->stop_stage = 0;
+ conn->tmabort_state = TMABORT_INITIAL;
+ session->age++;
+ spin_unlock_bh(&session->lock);
+
+ iscsi_unblock_session(session_to_cls(session));
+ wake_up(&conn->ehwait);
+ return 0;
+ case STOP_CONN_TERM:
+ conn->stop_stage = 0;
+ break;
+ default:
+ break;
+ }
+ spin_unlock_bh(&session->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iscsi_conn_start);
+
+static void
+flush_control_queues(struct iscsi_session *session, struct iscsi_conn *conn)
+{
+ struct iscsi_mgmt_task *mtask, *tmp;
+
+ /* handle pending */
+ while (__kfifo_get(conn->immqueue, (void*)&mtask, sizeof(void*)) ||
+ __kfifo_get(conn->mgmtqueue, (void*)&mtask, sizeof(void*))) {
+ if (mtask == conn->login_mtask)
+ continue;
+ debug_scsi("flushing pending mgmt task itt 0x%x\n", mtask->itt);
+ __kfifo_put(session->mgmtpool.queue, (void*)&mtask,
+ sizeof(void*));
+ }
+
+ /* handle running */
+ list_for_each_entry_safe(mtask, tmp, &conn->mgmt_run_list, running) {
+ debug_scsi("flushing running mgmt task itt 0x%x\n", mtask->itt);
+ list_del(&mtask->running);
+
+ if (mtask == conn->login_mtask)
+ continue;
+ __kfifo_put(session->mgmtpool.queue, (void*)&mtask,
+ sizeof(void*));
+ }
+
+ conn->mtask = NULL;
+}
+
+/* Fail commands. Mutex and session lock held and recv side suspended */
+static void fail_all_commands(struct iscsi_conn *conn)
+{
+ struct iscsi_cmd_task *ctask, *tmp;
+
+ /* flush pending */
+ list_for_each_entry_safe(ctask, tmp, &conn->xmitqueue, running) {
+ debug_scsi("failing pending sc %p itt 0x%x\n", ctask->sc,
+ ctask->itt);
+ fail_command(conn, ctask, DID_BUS_BUSY << 16);
+ }
+
+ /* fail all other running */
+ list_for_each_entry_safe(ctask, tmp, &conn->run_list, running) {
+ debug_scsi("failing in progress sc %p itt 0x%x\n",
+ ctask->sc, ctask->itt);
+ fail_command(conn, ctask, DID_BUS_BUSY << 16);
+ }
+
+ conn->ctask = NULL;
+}
+
+static void iscsi_start_session_recovery(struct iscsi_session *session,
+ struct iscsi_conn *conn, int flag)
+{
+ int old_stop_stage;
+
+ spin_lock_bh(&session->lock);
+ if (conn->stop_stage == STOP_CONN_TERM) {
+ spin_unlock_bh(&session->lock);
+ return;
+ }
+
+ /*
+ * When this is called for the in_login state, we only want to clean
+ * up the login task and connection. We do not need to block and set
+ * the recovery state again
+ */
+ if (flag == STOP_CONN_TERM)
+ session->state = ISCSI_STATE_TERMINATE;
+ else if (conn->stop_stage != STOP_CONN_RECOVER)
+ session->state = ISCSI_STATE_IN_RECOVERY;
+
+ old_stop_stage = conn->stop_stage;
+ conn->stop_stage = flag;
+ conn->c_stage = ISCSI_CONN_STOPPED;
+ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+ spin_unlock_bh(&session->lock);
+
+ write_lock_bh(conn->recv_lock);
+ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
+ write_unlock_bh(conn->recv_lock);
+
+ mutex_lock(&conn->xmitmutex);
+ /*
+ * for connection level recovery we should not calculate
+ * header digest. conn->hdr_size used for optimization
+ * in hdr_extract() and will be re-negotiated at
+ * set_param() time.
+ */
+ if (flag == STOP_CONN_RECOVER) {
+ conn->hdrdgst_en = 0;
+ conn->datadgst_en = 0;
+ if (session->state == ISCSI_STATE_IN_RECOVERY &&
+ old_stop_stage != STOP_CONN_RECOVER) {
+ debug_scsi("blocking session\n");
+ iscsi_block_session(session_to_cls(session));
+ }
+ }
+
+ /*
+ * flush queues.
+ */
+ spin_lock_bh(&session->lock);
+ fail_all_commands(conn);
+ flush_control_queues(session, conn);
+ spin_unlock_bh(&session->lock);
+
+ mutex_unlock(&conn->xmitmutex);
+}
+
+void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_session *session = conn->session;
+
+ switch (flag) {
+ case STOP_CONN_RECOVER:
+ case STOP_CONN_TERM:
+ iscsi_start_session_recovery(session, conn, flag);
+ break;
+ default:
+ printk(KERN_ERR "iscsi: invalid stop flag %d\n", flag);
+ }
+}
+EXPORT_SYMBOL_GPL(iscsi_conn_stop);
+
+int iscsi_conn_bind(struct iscsi_cls_session *cls_session,
+ struct iscsi_cls_conn *cls_conn, int is_leading)
+{
+ struct iscsi_session *session = class_to_transport_session(cls_session);
+ struct iscsi_conn *conn = cls_conn->dd_data;
+
+ spin_lock_bh(&session->lock);
+ if (is_leading)
+ session->leadconn = conn;
+ spin_unlock_bh(&session->lock);
+
+ /*
+ * Unblock xmitworker(), Login Phase will pass through.
+ */
+ clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
+ clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iscsi_conn_bind);
+
+
+int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf, int buflen)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_session *session = conn->session;
+ uint32_t value;
+
+ switch(param) {
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ sscanf(buf, "%d", &conn->max_recv_dlength);
+ break;
+ case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+ sscanf(buf, "%d", &conn->max_xmit_dlength);
+ break;
+ case ISCSI_PARAM_HDRDGST_EN:
+ sscanf(buf, "%d", &conn->hdrdgst_en);
+ break;
+ case ISCSI_PARAM_DATADGST_EN:
+ sscanf(buf, "%d", &conn->datadgst_en);
+ break;
+ case ISCSI_PARAM_INITIAL_R2T_EN:
+ sscanf(buf, "%d", &session->initial_r2t_en);
+ break;
+ case ISCSI_PARAM_MAX_R2T:
+ sscanf(buf, "%d", &session->max_r2t);
+ break;
+ case ISCSI_PARAM_IMM_DATA_EN:
+ sscanf(buf, "%d", &session->imm_data_en);
+ break;
+ case ISCSI_PARAM_FIRST_BURST:
+ sscanf(buf, "%d", &session->first_burst);
+ break;
+ case ISCSI_PARAM_MAX_BURST:
+ sscanf(buf, "%d", &session->max_burst);
+ break;
+ case ISCSI_PARAM_PDU_INORDER_EN:
+ sscanf(buf, "%d", &session->pdu_inorder_en);
+ break;
+ case ISCSI_PARAM_DATASEQ_INORDER_EN:
+ sscanf(buf, "%d", &session->dataseq_inorder_en);
+ break;
+ case ISCSI_PARAM_ERL:
+ sscanf(buf, "%d", &session->erl);
+ break;
+ case ISCSI_PARAM_IFMARKER_EN:
+ sscanf(buf, "%d", &value);
+ BUG_ON(value);
+ break;
+ case ISCSI_PARAM_OFMARKER_EN:
+ sscanf(buf, "%d", &value);
+ BUG_ON(value);
+ break;
+ case ISCSI_PARAM_EXP_STATSN:
+ sscanf(buf, "%u", &conn->exp_statsn);
+ break;
+ case ISCSI_PARAM_TARGET_NAME:
+ /* this should not change between logins */
+ if (session->targetname)
+ break;
+
+ session->targetname = kstrdup(buf, GFP_KERNEL);
+ if (!session->targetname)
+ return -ENOMEM;
+ break;
+ case ISCSI_PARAM_TPGT:
+ sscanf(buf, "%d", &session->tpgt);
+ break;
+ case ISCSI_PARAM_PERSISTENT_PORT:
+ sscanf(buf, "%d", &conn->persistent_port);
+ break;
+ case ISCSI_PARAM_PERSISTENT_ADDRESS:
+ /*
+ * this is the address returned in discovery so it should
+ * not change between logins.
+ */
+ if (conn->persistent_address)
+ break;
+
+ conn->persistent_address = kstrdup(buf, GFP_KERNEL);
+ if (!conn->persistent_address)
+ return -ENOMEM;
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iscsi_set_param);
+
+int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
+ enum iscsi_param param, char *buf)
+{
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+ int len;
+
+ switch(param) {
+ case ISCSI_PARAM_INITIAL_R2T_EN:
+ len = sprintf(buf, "%d\n", session->initial_r2t_en);
+ break;
+ case ISCSI_PARAM_MAX_R2T:
+ len = sprintf(buf, "%hu\n", session->max_r2t);
+ break;
+ case ISCSI_PARAM_IMM_DATA_EN:
+ len = sprintf(buf, "%d\n", session->imm_data_en);
+ break;
+ case ISCSI_PARAM_FIRST_BURST:
+ len = sprintf(buf, "%u\n", session->first_burst);
+ break;
+ case ISCSI_PARAM_MAX_BURST:
+ len = sprintf(buf, "%u\n", session->max_burst);
+ break;
+ case ISCSI_PARAM_PDU_INORDER_EN:
+ len = sprintf(buf, "%d\n", session->pdu_inorder_en);
+ break;
+ case ISCSI_PARAM_DATASEQ_INORDER_EN:
+ len = sprintf(buf, "%d\n", session->dataseq_inorder_en);
+ break;
+ case ISCSI_PARAM_ERL:
+ len = sprintf(buf, "%d\n", session->erl);
+ break;
+ case ISCSI_PARAM_TARGET_NAME:
+ len = sprintf(buf, "%s\n", session->targetname);
+ break;
+ case ISCSI_PARAM_TPGT:
+ len = sprintf(buf, "%d\n", session->tpgt);
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(iscsi_session_get_param);
+
+int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ int len;
+
+ switch(param) {
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ len = sprintf(buf, "%u\n", conn->max_recv_dlength);
+ break;
+ case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+ len = sprintf(buf, "%u\n", conn->max_xmit_dlength);
+ break;
+ case ISCSI_PARAM_HDRDGST_EN:
+ len = sprintf(buf, "%d\n", conn->hdrdgst_en);
+ break;
+ case ISCSI_PARAM_DATADGST_EN:
+ len = sprintf(buf, "%d\n", conn->datadgst_en);
+ break;
+ case ISCSI_PARAM_IFMARKER_EN:
+ len = sprintf(buf, "%d\n", conn->ifmarker_en);
+ break;
+ case ISCSI_PARAM_OFMARKER_EN:
+ len = sprintf(buf, "%d\n", conn->ofmarker_en);
+ break;
+ case ISCSI_PARAM_EXP_STATSN:
+ len = sprintf(buf, "%u\n", conn->exp_statsn);
+ break;
+ case ISCSI_PARAM_PERSISTENT_PORT:
+ len = sprintf(buf, "%d\n", conn->persistent_port);
+ break;
+ case ISCSI_PARAM_PERSISTENT_ADDRESS:
+ len = sprintf(buf, "%s\n", conn->persistent_address);
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(iscsi_conn_get_param);
+
+MODULE_AUTHOR("Mike Christie");
+MODULE_DESCRIPTION("iSCSI library functions");
+MODULE_LICENSE("GPL");
Index: kernel/iscsi_tcp.h
===================================================================
--- kernel/iscsi_tcp.h (revision 0)
+++ kernel/iscsi_tcp.h (revision 779)
@@ -0,0 +1,167 @@
+/*
+ * iSCSI Initiator TCP Transport
+ * Copyright (C) 2004 Dmitry Yusupov
+ * Copyright (C) 2004 Alex Aizman
+ * Copyright (C) 2005 - 2006 Mike Christie
+ * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
+ * maintained by open-iscsi@googlegroups.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * See the file COPYING included with this distribution for more details.
+ */
+
+#ifndef ISCSI_TCP_H
+#define ISCSI_TCP_H
+
+#include "libiscsi.h"
+
+/* Socket's Receive state machine */
+#define IN_PROGRESS_WAIT_HEADER 0x0
+#define IN_PROGRESS_HEADER_GATHER 0x1
+#define IN_PROGRESS_DATA_RECV 0x2
+#define IN_PROGRESS_DDIGEST_RECV 0x3
+
+/* xmit state machine */
+#define XMSTATE_IDLE 0x0
+#define XMSTATE_R_HDR 0x1
+#define XMSTATE_W_HDR 0x2
+#define XMSTATE_IMM_HDR 0x4
+#define XMSTATE_IMM_DATA 0x8
+#define XMSTATE_UNS_INIT 0x10
+#define XMSTATE_UNS_HDR 0x20
+#define XMSTATE_UNS_DATA 0x40
+#define XMSTATE_SOL_HDR 0x80
+#define XMSTATE_SOL_DATA 0x100
+#define XMSTATE_W_PAD 0x200
+#define XMSTATE_W_RESEND_PAD 0x400
+#define XMSTATE_W_RESEND_DATA_DIGEST 0x800
+
+#define ISCSI_PAD_LEN 4
+#define ISCSI_SG_TABLESIZE SG_ALL
+#define ISCSI_TCP_MAX_CMD_LEN 16
+
+struct crypto_hash;
+struct socket;
+
+/* Socket connection recieve helper */
+struct iscsi_tcp_recv {
+ struct iscsi_hdr *hdr;
+ struct sk_buff *skb;
+ int offset;
+ int len;
+ int hdr_offset;
+ int copy;
+ int copied;
+ int padding;
+ struct iscsi_cmd_task *ctask; /* current cmd in progress */
+
+ /* copied and flipped values */
+ int datalen;
+ int datadgst;
+ char zero_copy_hdr;
+};
+
+struct iscsi_tcp_conn {
+ struct iscsi_conn *iscsi_conn;
+ struct socket *sock;
+ struct iscsi_hdr hdr; /* header placeholder */
+ char hdrext[4*sizeof(__u16) +
+ sizeof(__u32)];
+ int data_copied;
+ int stop_stage; /* conn_stop() flag: *
+ * stop to recover, *
+ * stop to terminate */
+ /* iSCSI connection-wide sequencing */
+ int hdr_size; /* PDU header size */
+
+ /* control data */
+ struct iscsi_tcp_recv in; /* TCP receive context */
+ int in_progress; /* connection state machine */
+
+ /* old values for socket callbacks */
+ void (*old_data_ready)(struct sock *, int);
+ void (*old_state_change)(struct sock *);
+ void (*old_write_space)(struct sock *);
+
+ /* data and header digests */
+ struct hash_desc tx_hash; /* CRC32C (Tx) */
+ struct hash_desc rx_hash; /* CRC32C (Rx) */
+
+ /* MIB custom statistics */
+ uint32_t sendpage_failures_cnt;
+ uint32_t discontiguous_hdr_cnt;
+
+ ssize_t (*sendpage)(struct socket *, struct page *, int, size_t, int);
+};
+
+struct iscsi_buf {
+ struct scatterlist sg;
+ unsigned int sent;
+ char use_sendmsg;
+};
+
+struct iscsi_data_task {
+ struct iscsi_data hdr; /* PDU */
+ char hdrext[sizeof(__u32)]; /* Header-Digest */
+ struct iscsi_buf digestbuf; /* digest buffer */
+ uint32_t digest; /* data digest */
+};
+
+struct iscsi_tcp_mgmt_task {
+ struct iscsi_hdr hdr;
+ char hdrext[sizeof(__u32)]; /* Header-Digest */
+ int xmstate; /* mgmt xmit progress */
+ struct iscsi_buf headbuf; /* header buffer */
+ struct iscsi_buf sendbuf; /* in progress buffer */
+ int sent;
+};
+
+struct iscsi_r2t_info {
+ __be32 ttt; /* copied from R2T */
+ __be32 exp_statsn; /* copied from R2T */
+ uint32_t data_length; /* copied from R2T */
+ uint32_t data_offset; /* copied from R2T */
+ struct iscsi_buf headbuf; /* Data-Out Header Buffer */
+ struct iscsi_buf sendbuf; /* Data-Out in progress buffer*/
+ int sent; /* R2T sequence progress */
+ int data_count; /* DATA-Out payload progress */
+ struct scatterlist *sg; /* per-R2T SG list */
+ int solicit_datasn;
+ struct iscsi_data_task dtask; /* which data task */
+};
+
+struct iscsi_tcp_cmd_task {
+ struct iscsi_cmd hdr;
+ char hdrext[4*sizeof(__u16)+ /* AHS */
+ sizeof(__u32)]; /* HeaderDigest */
+ char pad[ISCSI_PAD_LEN];
+ int pad_count; /* padded bytes */
+ struct iscsi_buf headbuf; /* header buf (xmit) */
+ struct iscsi_buf sendbuf; /* in progress buffer*/
+ int xmstate; /* xmit xtate machine */
+ int sent;
+ struct scatterlist *sg; /* per-cmd SG list */
+ struct scatterlist *bad_sg; /* assert statement */
+ int sg_count; /* SG's to process */
+ uint32_t exp_r2tsn;
+ int data_offset;
+ struct iscsi_r2t_info *r2t; /* in progress R2T */
+ struct iscsi_queue r2tpool;
+ struct kfifo *r2tqueue;
+ struct iscsi_r2t_info **r2ts;
+ int digest_count;
+ uint32_t immdigest; /* for imm data */
+ struct iscsi_buf immbuf; /* for imm data digest */
+ struct iscsi_data_task unsol_dtask; /* unsol data task */
+};
+
+#endif /* ISCSI_H */
Index: kernel/libiscsi.h
===================================================================
--- kernel/libiscsi.h (revision 0)
+++ kernel/libiscsi.h (revision 779)
@@ -0,0 +1,311 @@
+/*
+ * iSCSI lib definitions
+ *
+ * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004 - 2006 Mike Christie
+ * Copyright (C) 2004 - 2005 Dmitry Yusupov
+ * Copyright (C) 2004 - 2005 Alex Aizman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef LIBISCSI_H
+#define LIBISCSI_H
+
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include "iscsi_proto.h"
+#include "iscsi_if.h"
+
+struct scsi_transport_template;
+struct scsi_device;
+struct Scsi_Host;
+struct scsi_cmnd;
+struct socket;
+struct iscsi_transport;
+struct iscsi_cls_session;
+struct iscsi_cls_conn;
+struct iscsi_session;
+struct iscsi_nopin;
+
+/* #define DEBUG_SCSI */
+#ifdef DEBUG_SCSI
+#define debug_scsi(fmt...) printk(KERN_INFO "iscsi: " fmt)
+#else
+#define debug_scsi(fmt...)
+#endif
+
+#define ISCSI_XMIT_CMDS_MAX 128 /* must be power of 2 */
+#define ISCSI_MGMT_CMDS_MAX 32 /* must be power of 2 */
+#define ISCSI_CONN_MAX 1
+
+#define ISCSI_MGMT_ITT_OFFSET 0xa00
+
+#define ISCSI_DEF_CMD_PER_LUN 32
+#define ISCSI_MAX_CMD_PER_LUN 128
+
+/* Task Mgmt states */
+#define TMABORT_INITIAL 0x0
+#define TMABORT_SUCCESS 0x1
+#define TMABORT_FAILED 0x2
+#define TMABORT_TIMEDOUT 0x3
+#define TMABORT_NOT_FOUND 0x4
+
+/* Connection suspend "bit" */
+#define ISCSI_SUSPEND_BIT 1
+
+#define ISCSI_ITT_MASK (0xfff)
+#define ISCSI_CID_SHIFT 12
+#define ISCSI_CID_MASK (0xffff << ISCSI_CID_SHIFT)
+#define ISCSI_AGE_SHIFT 28
+#define ISCSI_AGE_MASK (0xf << ISCSI_AGE_SHIFT)
+
+struct iscsi_mgmt_task {
+ /*
+ * Becuae LLDs allocate their hdr differently, this is a pointer to
+ * that storage. It must be setup at session creation time.
+ */
+ struct iscsi_hdr *hdr;
+ char *data; /* mgmt payload */
+ int data_count; /* counts data to be sent */
+ uint32_t itt; /* this ITT */
+ void *dd_data; /* driver/transport data */
+ struct list_head running;
+};
+
+enum {
+ ISCSI_TASK_COMPLETED,
+ ISCSI_TASK_PENDING,
+ ISCSI_TASK_RUNNING,
+};
+
+struct iscsi_cmd_task {
+ /*
+ * Becuae LLDs allocate their hdr differently, this is a pointer to
+ * that storage. It must be setup at session creation time.
+ */
+ struct iscsi_cmd *hdr;
+ int itt; /* this ITT */
+ int datasn; /* DataSN */
+
+ uint32_t unsol_datasn;
+ int imm_count; /* imm-data (bytes) */
+ int unsol_count; /* unsolicited (bytes)*/
+ /* offset in unsolicited stream (bytes); */
+ int unsol_offset;
+ int data_count; /* remaining Data-Out */
+ struct scsi_cmnd *sc; /* associated SCSI cmd*/
+ int total_length;
+ struct iscsi_conn *conn; /* used connection */
+ struct iscsi_mgmt_task *mtask; /* tmf mtask in progr */
+
+ /* state set/tested under session->lock */
+ int state;
+ atomic_t refcount;
+ struct list_head running; /* running cmd list */
+ void *dd_data; /* driver/transport data */
+};
+
+struct iscsi_conn {
+ struct iscsi_cls_conn *cls_conn; /* ptr to class connection */
+ void *dd_data; /* iscsi_transport data */
+ struct iscsi_session *session; /* parent session */
+ /*
+ * LLDs should set this lock. It protects the transport recv
+ * code
+ */
+ rwlock_t *recv_lock;
+ /*
+ * conn_stop() flag: stop to recover, stop to terminate
+ */
+ int stop_stage;
+
+ /* iSCSI connection-wide sequencing */
+ uint32_t exp_statsn;
+
+ /* control data */
+ int id; /* CID */
+ int c_stage; /* connection state */
+ /*
+ * Preallocated buffer for pdus that have data but do not
+ * originate from scsi-ml. We never have two pdus using the
+ * buffer at the same time. It is only allocated to
+ * the default max recv size because the pdus we support
+ * should always fit in this buffer
+ */
+ char *data;
+ struct iscsi_mgmt_task *login_mtask; /* mtask used for login/text */
+ struct iscsi_mgmt_task *mtask; /* xmit mtask in progress */
+ struct iscsi_cmd_task *ctask; /* xmit ctask in progress */
+
+ /* xmit */
+ struct kfifo *immqueue; /* immediate xmit queue */
+ struct kfifo *mgmtqueue; /* mgmt (control) xmit queue */
+ struct list_head mgmt_run_list; /* list of control tasks */
+ struct list_head xmitqueue; /* data-path cmd queue */
+ struct list_head run_list; /* list of cmds in progress */
+ struct work_struct xmitwork; /* per-conn. xmit workqueue */
+ /*
+ * serializes connection xmit, access to kfifos:
+ * xmitqueue, immqueue, mgmtqueue
+ */
+ struct mutex xmitmutex;
+
+ unsigned long suspend_tx; /* suspend Tx */
+ unsigned long suspend_rx; /* suspend Rx */
+
+ /* abort */
+ wait_queue_head_t ehwait; /* used in eh_abort() */
+ struct iscsi_tm tmhdr;
+ struct timer_list tmabort_timer;
+ int tmabort_state; /* see TMABORT_INITIAL, etc.*/
+
+ /* negotiated params */
+ int max_recv_dlength; /* initiator_max_recv_dsl*/
+ int max_xmit_dlength; /* target_max_recv_dsl */
+ int hdrdgst_en;
+ int datadgst_en;
+ int ifmarker_en;
+ int ofmarker_en;
+ /* values userspace uses to id a conn */
+ int persistent_port;
+ char *persistent_address;
+
+ /* MIB-statistics */
+ uint64_t txdata_octets;
+ uint64_t rxdata_octets;
+ uint32_t scsicmd_pdus_cnt;
+ uint32_t dataout_pdus_cnt;
+ uint32_t scsirsp_pdus_cnt;
+ uint32_t datain_pdus_cnt;
+ uint32_t r2t_pdus_cnt;
+ uint32_t tmfcmd_pdus_cnt;
+ int32_t tmfrsp_pdus_cnt;
+
+ /* custom statistics */
+ uint32_t eh_abort_cnt;
+};
+
+struct iscsi_queue {
+ struct kfifo *queue; /* FIFO Queue */
+ void **pool; /* Pool of elements */
+ int max; /* Max number of elements */
+};
+
+struct iscsi_session {
+ /* iSCSI session-wide sequencing */
+ uint32_t cmdsn;
+ uint32_t exp_cmdsn;
+ uint32_t max_cmdsn;
+
+ /* configuration */
+ int initial_r2t_en;
+ int max_r2t;
+ int imm_data_en;
+ int first_burst;
+ int max_burst;
+ int time2wait;
+ int time2retain;
+ int pdu_inorder_en;
+ int dataseq_inorder_en;
+ int erl;
+ int tpgt;
+ char *targetname;
+
+ /* control data */
+ struct iscsi_transport *tt;
+ struct Scsi_Host *host;
+ struct iscsi_conn *leadconn; /* leading connection */
+ spinlock_t lock; /* protects session state, *
+ * sequence numbers, *
+ * session resources: *
+ * - cmdpool, *
+ * - mgmtpool, *
+ * - r2tpool */
+ int state; /* session state */
+ int age; /* counts session re-opens */
+
+ int cmds_max; /* size of cmds array */
+ struct iscsi_cmd_task **cmds; /* Original Cmds arr */
+ struct iscsi_queue cmdpool; /* PDU's pool */
+ int mgmtpool_max; /* size of mgmt array */
+ struct iscsi_mgmt_task **mgmt_cmds; /* Original mgmt arr */
+ struct iscsi_queue mgmtpool; /* Mgmt PDU's pool */
+};
+
+/*
+ * scsi host template
+ */
+extern int iscsi_change_queue_depth(struct scsi_device *sdev, int depth);
+extern int iscsi_eh_abort(struct scsi_cmnd *sc);
+extern int iscsi_eh_host_reset(struct scsi_cmnd *sc);
+extern int iscsi_queuecommand(struct scsi_cmnd *sc,
+ void (*done)(struct scsi_cmnd *));
+
+/*
+ * session management
+ */
+extern struct iscsi_cls_session *
+iscsi_session_setup(struct iscsi_transport *, struct scsi_transport_template *,
+ int, int, uint32_t, uint32_t *);
+extern void iscsi_session_teardown(struct iscsi_cls_session *);
+extern struct iscsi_session *class_to_transport_session(struct iscsi_cls_session *);
+extern void iscsi_session_recovery_timedout(struct iscsi_cls_session *);
+extern int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf, int buflen);
+extern int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
+ enum iscsi_param param, char *buf);
+
+#define session_to_cls(_sess) \
+ hostdata_session(_sess->host->hostdata)
+
+/*
+ * connection management
+ */
+extern struct iscsi_cls_conn *iscsi_conn_setup(struct iscsi_cls_session *,
+ uint32_t);
+extern void iscsi_conn_teardown(struct iscsi_cls_conn *);
+extern int iscsi_conn_start(struct iscsi_cls_conn *);
+extern void iscsi_conn_stop(struct iscsi_cls_conn *, int);
+extern int iscsi_conn_bind(struct iscsi_cls_session *, struct iscsi_cls_conn *,
+ int);
+extern void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err);
+extern int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf);
+
+/*
+ * pdu and task processing
+ */
+extern int iscsi_check_assign_cmdsn(struct iscsi_session *,
+ struct iscsi_nopin *);
+extern void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *,
+ struct iscsi_data *hdr);
+extern int iscsi_conn_send_pdu(struct iscsi_cls_conn *, struct iscsi_hdr *,
+ char *, uint32_t);
+extern int iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *,
+ char *, int);
+extern int __iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *,
+ char *, int);
+extern int iscsi_verify_itt(struct iscsi_conn *, struct iscsi_hdr *,
+ uint32_t *);
+
+/*
+ * generic helpers
+ */
+extern void iscsi_pool_free(struct iscsi_queue *, void **);
+extern int iscsi_pool_init(struct iscsi_queue *, int, void ***, int);
+
+#endif
Index: kernel/scsi_transport_iscsi.c
===================================================================
--- kernel/scsi_transport_iscsi.c (revision 0)
+++ kernel/scsi_transport_iscsi.c (revision 779)
@@ -0,0 +1,1475 @@
+/*
+ * iSCSI transport class definitions
+ *
+ * Copyright (C) IBM Corporation, 2004
+ * Copyright (C) Mike Christie, 2004 - 2005
+ * Copyright (C) Dmitry Yusupov, 2004 - 2005
+ * Copyright (C) Alex Aizman, 2004 - 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <net/tcp.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport.h>
+#include "scsi_transport_iscsi.h"
+#include "iscsi_if.h"
+
+#define ISCSI_SESSION_ATTRS 11
+#define ISCSI_CONN_ATTRS 11
+#define ISCSI_HOST_ATTRS 0
+#define ISCSI_TRANSPORT_VERSION "2.0-724"
+
+struct iscsi_internal {
+ int daemon_pid;
+ struct scsi_transport_template t;
+ struct iscsi_transport *iscsi_transport;
+ struct list_head list;
+ struct class_device cdev;
+
+ struct class_device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1];
+ struct transport_container conn_cont;
+ struct class_device_attribute *conn_attrs[ISCSI_CONN_ATTRS + 1];
+ struct transport_container session_cont;
+ struct class_device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1];
+};
+
+static atomic_t iscsi_session_nr; /* sysfs session id for next new session */
+
+/*
+ * list of registered transports and lock that must
+ * be held while accessing list. The iscsi_transport_lock must
+ * be acquired after the rx_queue_mutex.
+ */
+static LIST_HEAD(iscsi_transports);
+static DEFINE_SPINLOCK(iscsi_transport_lock);
+
+#define to_iscsi_internal(tmpl) \
+ container_of(tmpl, struct iscsi_internal, t)
+
+#define cdev_to_iscsi_internal(_cdev) \
+ container_of(_cdev, struct iscsi_internal, cdev)
+
+static void iscsi_transport_release(struct class_device *cdev)
+{
+ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev);
+ kfree(priv);
+}
+
+/*
+ * iscsi_transport_class represents the iscsi_transports that are
+ * registered.
+ */
+static struct class iscsi_transport_class = {
+ .name = "iscsi_transport",
+ .release = iscsi_transport_release,
+};
+
+static ssize_t
+show_transport_handle(struct class_device *cdev, char *buf)
+{
+ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev);
+ return sprintf(buf, "%llu\n", (unsigned long long)iscsi_handle(priv->iscsi_transport));
+}
+static CLASS_DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);
+
+#define show_transport_attr(name, format) \
+static ssize_t \
+show_transport_##name(struct class_device *cdev, char *buf) \
+{ \
+ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev); \
+ return sprintf(buf, format"\n", priv->iscsi_transport->name); \
+} \
+static CLASS_DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);
+
+show_transport_attr(caps, "0x%x");
+show_transport_attr(max_lun, "%d");
+show_transport_attr(max_conn, "%d");
+show_transport_attr(max_cmd_len, "%d");
+
+static struct attribute *iscsi_transport_attrs[] = {
+ &class_device_attr_handle.attr,
+ &class_device_attr_caps.attr,
+ &class_device_attr_max_lun.attr,
+ &class_device_attr_max_conn.attr,
+ &class_device_attr_max_cmd_len.attr,
+ NULL,
+};
+
+static struct attribute_group iscsi_transport_group = {
+ .attrs = iscsi_transport_attrs,
+};
+
+static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
+ struct class_device *cdev)
+{
+ struct Scsi_Host *shost = dev_to_shost(dev);
+ struct iscsi_host *ihost = shost->shost_data;
+
+ memset(ihost, 0, sizeof(*ihost));
+ INIT_LIST_HEAD(&ihost->sessions);
+ mutex_init(&ihost->mutex);
+ return 0;
+}
+
+static DECLARE_TRANSPORT_CLASS(iscsi_host_class,
+ "iscsi_host",
+ iscsi_setup_host,
+ NULL,
+ NULL);
+
+static DECLARE_TRANSPORT_CLASS(iscsi_session_class,
+ "iscsi_session",
+ NULL,
+ NULL,
+ NULL);
+
+static DECLARE_TRANSPORT_CLASS(iscsi_connection_class,
+ "iscsi_connection",
+ NULL,
+ NULL,
+ NULL);
+
+static struct sock *nls;
+static DEFINE_MUTEX(rx_queue_mutex);
+
+static LIST_HEAD(sesslist);
+static DEFINE_SPINLOCK(sesslock);
+static LIST_HEAD(connlist);
+static DEFINE_SPINLOCK(connlock);
+
+static uint32_t iscsi_conn_get_sid(struct iscsi_cls_conn *conn)
+{
+ struct iscsi_cls_session *sess = iscsi_dev_to_session(conn->dev.parent);
+ return sess->sid;
+}
+
+/*
+ * Returns the matching session to a given sid
+ */
+static struct iscsi_cls_session *iscsi_session_lookup(uint32_t sid)
+{
+ unsigned long flags;
+ struct iscsi_cls_session *sess;
+
+ spin_lock_irqsave(&sesslock, flags);
+ list_for_each_entry(sess, &sesslist, sess_list) {
+ if (sess->sid == sid) {
+ spin_unlock_irqrestore(&sesslock, flags);
+ return sess;
+ }
+ }
+ spin_unlock_irqrestore(&sesslock, flags);
+ return NULL;
+}
+
+/*
+ * Returns the matching connection to a given sid / cid tuple
+ */
+static struct iscsi_cls_conn *iscsi_conn_lookup(uint32_t sid, uint32_t cid)
+{
+ unsigned long flags;
+ struct iscsi_cls_conn *conn;
+
+ spin_lock_irqsave(&connlock, flags);
+ list_for_each_entry(conn, &connlist, conn_list) {
+ if ((conn->cid == cid) && (iscsi_conn_get_sid(conn) == sid)) {
+ spin_unlock_irqrestore(&connlock, flags);
+ return conn;
+ }
+ }
+ spin_unlock_irqrestore(&connlock, flags);
+ return NULL;
+}
+
+/*
+ * The following functions can be used by LLDs that allocate
+ * their own scsi_hosts or by software iscsi LLDs
+ */
+static void iscsi_session_release(struct device *dev)
+{
+ struct iscsi_cls_session *session = iscsi_dev_to_session(dev);
+ struct Scsi_Host *shost;
+
+ shost = iscsi_session_to_shost(session);
+ scsi_host_put(shost);
+ kfree(session);
+}
+
+static int iscsi_is_session_dev(const struct device *dev)
+{
+ return dev->release == iscsi_session_release;
+}
+
+static int iscsi_user_scan(struct Scsi_Host *shost, uint channel,
+ uint id, uint lun)
+{
+ struct iscsi_host *ihost = shost->shost_data;
+ struct iscsi_cls_session *session;
+
+ mutex_lock(&ihost->mutex);
+ list_for_each_entry(session, &ihost->sessions, host_list) {
+ if ((channel == SCAN_WILD_CARD || channel == 0) &&
+ (id == SCAN_WILD_CARD || id == session->target_id))
+ scsi_scan_target(&session->dev, 0,
+ session->target_id, lun, 1);
+ }
+ mutex_unlock(&ihost->mutex);
+
+ return 0;
+}
+
+static void session_recovery_timedout(struct work_struct *work)
+{
+ struct iscsi_cls_session *session =
+ container_of(work, struct iscsi_cls_session,
+ recovery_work.work);
+
+ dev_printk(KERN_INFO, &session->dev, "iscsi: session recovery timed "
+ "out after %d secs\n", session->recovery_tmo);
+
+ if (session->transport->session_recovery_timedout)
+ session->transport->session_recovery_timedout(session);
+
+ scsi_target_unblock(&session->dev);
+}
+
+void iscsi_unblock_session(struct iscsi_cls_session *session)
+{
+ if (!cancel_delayed_work(&session->recovery_work))
+ flush_scheduled_work();
+ scsi_target_unblock(&session->dev);
+}
+EXPORT_SYMBOL_GPL(iscsi_unblock_session);
+
+void iscsi_block_session(struct iscsi_cls_session *session)
+{
+ scsi_target_block(&session->dev);
+ schedule_delayed_work(&session->recovery_work,
+ session->recovery_tmo * HZ);
+}
+EXPORT_SYMBOL_GPL(iscsi_block_session);
+
+struct iscsi_cls_session *
+iscsi_alloc_session(struct Scsi_Host *shost,
+ struct iscsi_transport *transport)
+{
+ struct iscsi_cls_session *session;
+
+ session = kzalloc(sizeof(*session) + transport->sessiondata_size,
+ GFP_KERNEL);
+ if (!session)
+ return NULL;
+
+ session->transport = transport;
+ session->recovery_tmo = 120;
+ INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);
+ INIT_LIST_HEAD(&session->host_list);
+ INIT_LIST_HEAD(&session->sess_list);
+
+ /* this is released in the dev's release function */
+ scsi_host_get(shost);
+ session->dev.parent = &shost->shost_gendev;
+ session->dev.release = iscsi_session_release;
+ device_initialize(&session->dev);
+ if (transport->sessiondata_size)
+ session->dd_data = &session[1];
+ return session;
+}
+EXPORT_SYMBOL_GPL(iscsi_alloc_session);
+
+int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
+{
+ struct Scsi_Host *shost = iscsi_session_to_shost(session);
+ struct iscsi_host *ihost;
+ int err;
+
+ ihost = shost->shost_data;
+ session->sid = atomic_add_return(1, &iscsi_session_nr);
+ session->target_id = target_id;
+
+ snprintf(session->dev.bus_id, BUS_ID_SIZE, "session%u",
+ session->sid);
+ err = device_add(&session->dev);
+ if (err) {
+ dev_printk(KERN_ERR, &session->dev, "iscsi: could not "
+ "register session's dev\n");
+ goto release_host;
+ }
+ transport_register_device(&session->dev);
+
+ mutex_lock(&ihost->mutex);
+ list_add(&session->host_list, &ihost->sessions);
+ mutex_unlock(&ihost->mutex);
+ return 0;
+
+release_host:
+ scsi_host_put(shost);
+ return err;
+}
+EXPORT_SYMBOL_GPL(iscsi_add_session);
+
+/**
+ * iscsi_create_session - create iscsi class session
+ * @shost: scsi host
+ * @transport: iscsi transport
+ *
+ * This can be called from a LLD or iscsi_transport.
+ **/
+struct iscsi_cls_session *
+iscsi_create_session(struct Scsi_Host *shost,
+ struct iscsi_transport *transport,
+ unsigned int target_id)
+{
+ struct iscsi_cls_session *session;
+
+ session = iscsi_alloc_session(shost, transport);
+ if (!session)
+ return NULL;
+
+ if (iscsi_add_session(session, target_id)) {
+ iscsi_free_session(session);
+ return NULL;
+ }
+ return session;
+}
+EXPORT_SYMBOL_GPL(iscsi_create_session);
+
+void iscsi_remove_session(struct iscsi_cls_session *session)
+{
+ struct Scsi_Host *shost = iscsi_session_to_shost(session);
+ struct iscsi_host *ihost = shost->shost_data;
+
+ if (!cancel_delayed_work(&session->recovery_work))
+ flush_scheduled_work();
+
+ mutex_lock(&ihost->mutex);
+ list_del(&session->host_list);
+ mutex_unlock(&ihost->mutex);
+
+ scsi_remove_target(&session->dev);
+
+ transport_unregister_device(&session->dev);
+ device_del(&session->dev);
+}
+EXPORT_SYMBOL_GPL(iscsi_remove_session);
+
+void iscsi_free_session(struct iscsi_cls_session *session)
+{
+ put_device(&session->dev);
+}
+
+EXPORT_SYMBOL_GPL(iscsi_free_session);
+
+/**
+ * iscsi_destroy_session - destroy iscsi session
+ * @session: iscsi_session
+ *
+ * Can be called by a LLD or iscsi_transport. There must not be
+ * any running connections.
+ **/
+int iscsi_destroy_session(struct iscsi_cls_session *session)
+{
+ iscsi_remove_session(session);
+ iscsi_free_session(session);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iscsi_destroy_session);
+
+static void iscsi_conn_release(struct device *dev)
+{
+ struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev);
+ struct device *parent = conn->dev.parent;
+
+ kfree(conn);
+ put_device(parent);
+}
+
+static int iscsi_is_conn_dev(const struct device *dev)
+{
+ return dev->release == iscsi_conn_release;
+}
+
+/**
+ * iscsi_create_conn - create iscsi class connection
+ * @session: iscsi cls session
+ * @cid: connection id
+ *
+ * This can be called from a LLD or iscsi_transport. The connection
+ * is child of the session so cid must be unique for all connections
+ * on the session.
+ *
+ * Since we do not support MCS, cid will normally be zero. In some cases
+ * for software iscsi we could be trying to preallocate a connection struct
+ * in which case there could be two connection structs and cid would be
+ * non-zero.
+ **/
+struct iscsi_cls_conn *
+iscsi_create_conn(struct iscsi_cls_session *session, uint32_t cid)
+{
+ struct iscsi_transport *transport = session->transport;
+ struct iscsi_cls_conn *conn;
+ int err;
+
+ conn = kzalloc(sizeof(*conn) + transport->conndata_size, GFP_KERNEL);
+ if (!conn)
+ return NULL;
+
+ if (transport->conndata_size)
+ conn->dd_data = &conn[1];
+
+ INIT_LIST_HEAD(&conn->conn_list);
+ conn->transport = transport;
+ conn->cid = cid;
+
+ /* this is released in the dev's release function */
+ if (!get_device(&session->dev))
+ goto free_conn;
+
+ snprintf(conn->dev.bus_id, BUS_ID_SIZE, "connection%d:%u",
+ session->sid, cid);
+ conn->dev.parent = &session->dev;
+ conn->dev.release = iscsi_conn_release;
+ err = device_register(&conn->dev);
+ if (err) {
+ dev_printk(KERN_ERR, &conn->dev, "iscsi: could not register "
+ "connection's dev\n");
+ goto release_parent_ref;
+ }
+ transport_register_device(&conn->dev);
+ return conn;
+
+release_parent_ref:
+ put_device(&session->dev);
+free_conn:
+ kfree(conn);
+ return NULL;
+}
+
+EXPORT_SYMBOL_GPL(iscsi_create_conn);
+
+/**
+ * iscsi_destroy_conn - destroy iscsi class connection
+ * @session: iscsi cls session
+ *
+ * This can be called from a LLD or iscsi_transport.
+ **/
+int iscsi_destroy_conn(struct iscsi_cls_conn *conn)
+{
+ transport_unregister_device(&conn->dev);
+ device_unregister(&conn->dev);
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(iscsi_destroy_conn);
+
+/*
+ * iscsi interface functions
+ */
+static struct iscsi_internal *
+iscsi_if_transport_lookup(struct iscsi_transport *tt)
+{
+ struct iscsi_internal *priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iscsi_transport_lock, flags);
+ list_for_each_entry(priv, &iscsi_transports, list) {
+ if (tt == priv->iscsi_transport) {
+ spin_unlock_irqrestore(&iscsi_transport_lock, flags);
+ return priv;
+ }
+ }
+ spin_unlock_irqrestore(&iscsi_transport_lock, flags);
+ return NULL;
+}
+
+static int
+iscsi_broadcast_skb(struct sk_buff *skb, gfp_t gfp)
+{
+ int rc;
+
+ rc = netlink_broadcast(nls, skb, 0, 1, gfp);
+ if (rc < 0) {
+ printk(KERN_ERR "iscsi: can not broadcast skb (%d)\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int
+iscsi_unicast_skb(struct sk_buff *skb, int pid)
+{
+ int rc;
+
+ rc = netlink_unicast(nls, skb, pid, MSG_DONTWAIT);
+ if (rc < 0) {
+ printk(KERN_ERR "iscsi: can not unicast skb (%d)\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
+ char *data, uint32_t data_size)
+{
+ struct nlmsghdr *nlh;
+ struct sk_buff *skb;
+ struct iscsi_uevent *ev;
+ char *pdu;
+ struct iscsi_internal *priv;
+ int len = NLMSG_SPACE(sizeof(*ev) + sizeof(struct iscsi_hdr) +
+ data_size);
+
+ priv = iscsi_if_transport_lookup(conn->transport);
+ if (!priv)
+ return -EINVAL;
+
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (!skb) {
+ iscsi_conn_error(conn, ISCSI_ERR_CONN_FAILED);
+ dev_printk(KERN_ERR, &conn->dev, "iscsi: can not deliver "
+ "control PDU: OOM\n");
+ return -ENOMEM;
+ }
+
+ nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0);
+ ev = NLMSG_DATA(nlh);
+ memset(ev, 0, sizeof(*ev));
+ ev->transport_handle = iscsi_handle(conn->transport);
+ ev->type = ISCSI_KEVENT_RECV_PDU;
+ ev->r.recv_req.cid = conn->cid;
+ ev->r.recv_req.sid = iscsi_conn_get_sid(conn);
+ pdu = (char*)ev + sizeof(*ev);
+ memcpy(pdu, hdr, sizeof(struct iscsi_hdr));
+ memcpy(pdu + sizeof(struct iscsi_hdr), data, data_size);
+
+ return iscsi_unicast_skb(skb, priv->daemon_pid);
+}
+EXPORT_SYMBOL_GPL(iscsi_recv_pdu);
+
+void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error)
+{
+ struct nlmsghdr *nlh;
+ struct sk_buff *skb;
+ struct iscsi_uevent *ev;
+ struct iscsi_internal *priv;
+ int len = NLMSG_SPACE(sizeof(*ev));
+
+ priv = iscsi_if_transport_lookup(conn->transport);
+ if (!priv)
+ return;
+
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (!skb) {
+ dev_printk(KERN_ERR, &conn->dev, "iscsi: gracefully ignored "
+ "conn error (%d)\n", error);
+ return;
+ }
+
+ nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0);
+ ev = NLMSG_DATA(nlh);
+ ev->transport_handle = iscsi_handle(conn->transport);
+ ev->type = ISCSI_KEVENT_CONN_ERROR;
+ ev->r.connerror.error = error;
+ ev->r.connerror.cid = conn->cid;
+ ev->r.connerror.sid = iscsi_conn_get_sid(conn);
+
+ iscsi_broadcast_skb(skb, GFP_ATOMIC);
+
+ dev_printk(KERN_INFO, &conn->dev, "iscsi: detected conn error (%d)\n",
+ error);
+}
+EXPORT_SYMBOL_GPL(iscsi_conn_error);
+
+static int
+iscsi_if_send_reply(int pid, int seq, int type, int done, int multi,
+ void *payload, int size)
+{
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+ int len = NLMSG_SPACE(size);
+ int flags = multi ? NLM_F_MULTI : 0;
+ int t = done ? NLMSG_DONE : type;
+
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_ERR "Could not allocate skb to send reply.\n");
+ return -ENOMEM;
+ }
+
+ nlh = __nlmsg_put(skb, pid, seq, t, (len - sizeof(*nlh)), 0);
+ nlh->nlmsg_flags = flags;
+ memcpy(NLMSG_DATA(nlh), payload, size);
+ return iscsi_unicast_skb(skb, pid);
+}
+
+static int
+iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
+{
+ struct iscsi_uevent *ev = NLMSG_DATA(nlh);
+ struct iscsi_stats *stats;
+ struct sk_buff *skbstat;
+ struct iscsi_cls_conn *conn;
+ struct nlmsghdr *nlhstat;
+ struct iscsi_uevent *evstat;
+ struct iscsi_internal *priv;
+ int len = NLMSG_SPACE(sizeof(*ev) +
+ sizeof(struct iscsi_stats) +
+ sizeof(struct iscsi_stats_custom) *
+ ISCSI_STATS_CUSTOM_MAX);
+ int err = 0;
+
+ priv = iscsi_if_transport_lookup(transport);
+ if (!priv)
+ return -EINVAL;
+
+ conn = iscsi_conn_lookup(ev->u.get_stats.sid, ev->u.get_stats.cid);
+ if (!conn)
+ return -EEXIST;
+
+ do {
+ int actual_size;
+
+ skbstat = alloc_skb(len, GFP_ATOMIC);
+ if (!skbstat) {
+ dev_printk(KERN_ERR, &conn->dev, "iscsi: can not "
+ "deliver stats: OOM\n");
+ return -ENOMEM;
+ }
+
+ nlhstat = __nlmsg_put(skbstat, priv->daemon_pid, 0, 0,
+ (len - sizeof(*nlhstat)), 0);
+ evstat = NLMSG_DATA(nlhstat);
+ memset(evstat, 0, sizeof(*evstat));
+ evstat->transport_handle = iscsi_handle(conn->transport);
+ evstat->type = nlh->nlmsg_type;
+ evstat->u.get_stats.cid =
+ ev->u.get_stats.cid;
+ evstat->u.get_stats.sid =
+ ev->u.get_stats.sid;
+ stats = (struct iscsi_stats *)
+ ((char*)evstat + sizeof(*evstat));
+ memset(stats, 0, sizeof(*stats));
+
+ transport->get_stats(conn, stats);
+ actual_size = NLMSG_SPACE(sizeof(struct iscsi_uevent) +
+ sizeof(struct iscsi_stats) +
+ sizeof(struct iscsi_stats_custom) *
+ stats->custom_length);
+ actual_size -= sizeof(*nlhstat);
+ actual_size = NLMSG_LENGTH(actual_size);
+ skb_trim(skbstat, NLMSG_ALIGN(actual_size));
+ nlhstat->nlmsg_len = actual_size;
+
+ err = iscsi_unicast_skb(skbstat, priv->daemon_pid);
+ } while (err < 0 && err != -ECONNREFUSED);
+
+ return err;
+}
+
+/**
+ * iscsi_if_destroy_session_done - send session destr. completion event
+ * @conn: last connection for session
+ *
+ * This is called by HW iscsi LLDs to notify userpsace that its HW has
+ * removed a session.
+ **/
+int iscsi_if_destroy_session_done(struct iscsi_cls_conn *conn)
+{
+ struct iscsi_internal *priv;
+ struct iscsi_cls_session *session;
+ struct Scsi_Host *shost;
+ struct iscsi_uevent *ev;
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+ unsigned long flags;
+ int rc, len = NLMSG_SPACE(sizeof(*ev));
+
+ priv = iscsi_if_transport_lookup(conn->transport);
+ if (!priv)
+ return -EINVAL;
+
+ session = iscsi_dev_to_session(conn->dev.parent);
+ shost = iscsi_session_to_shost(session);
+
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (!skb) {
+ dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of "
+ "session creation event\n");
+ return -ENOMEM;
+ }
+
+ nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0);
+ ev = NLMSG_DATA(nlh);
+ ev->transport_handle = iscsi_handle(conn->transport);
+ ev->type = ISCSI_KEVENT_DESTROY_SESSION;
+ ev->r.d_session.host_no = shost->host_no;
+ ev->r.d_session.sid = session->sid;
+
+ /*
+ * this will occur if the daemon is not up, so we just warn
+ * the user and when the daemon is restarted it will handle it
+ */
+ rc = iscsi_broadcast_skb(skb, GFP_KERNEL);
+ if (rc < 0)
+ dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of "
+ "session destruction event. Check iscsi daemon\n");
+
+ spin_lock_irqsave(&sesslock, flags);
+ list_del(&session->sess_list);
+ spin_unlock_irqrestore(&sesslock, flags);
+
+ spin_lock_irqsave(&connlock, flags);
+ conn->active = 0;
+ list_del(&conn->conn_list);
+ spin_unlock_irqrestore(&connlock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(iscsi_if_destroy_session_done);
+
+/**
+ * iscsi_if_create_session_done - send session creation completion event
+ * @conn: leading connection for session
+ *
+ * This is called by HW iscsi LLDs to notify userpsace that its HW has
+ * created a session or a existing session is back in the logged in state.
+ **/
+int iscsi_if_create_session_done(struct iscsi_cls_conn *conn)
+{
+ struct iscsi_internal *priv;
+ struct iscsi_cls_session *session;
+ struct Scsi_Host *shost;
+ struct iscsi_uevent *ev;
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+ unsigned long flags;
+ int rc, len = NLMSG_SPACE(sizeof(*ev));
+
+ priv = iscsi_if_transport_lookup(conn->transport);
+ if (!priv)
+ return -EINVAL;
+
+ session = iscsi_dev_to_session(conn->dev.parent);
+ shost = iscsi_session_to_shost(session);
+
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (!skb) {
+ dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of "
+ "session creation event\n");
+ return -ENOMEM;
+ }
+
+ nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0);
+ ev = NLMSG_DATA(nlh);
+ ev->transport_handle = iscsi_handle(conn->transport);
+ ev->type = ISCSI_UEVENT_CREATE_SESSION;
+ ev->r.c_session_ret.host_no = shost->host_no;
+ ev->r.c_session_ret.sid = session->sid;
+
+ /*
+ * this will occur if the daemon is not up, so we just warn
+ * the user and when the daemon is restarted it will handle it
+ */
+ rc = iscsi_broadcast_skb(skb, GFP_KERNEL);
+ if (rc < 0)
+ dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of "
+ "session creation event. Check iscsi daemon\n");
+
+ spin_lock_irqsave(&sesslock, flags);
+ list_add(&session->sess_list, &sesslist);
+ spin_unlock_irqrestore(&sesslock, flags);
+
+ spin_lock_irqsave(&connlock, flags);
+ list_add(&conn->conn_list, &connlist);
+ conn->active = 1;
+ spin_unlock_irqrestore(&connlock, flags);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(iscsi_if_create_session_done);
+
+static int
+iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_uevent *ev)
+{
+ struct iscsi_transport *transport = priv->iscsi_transport;
+ struct iscsi_cls_session *session;
+ unsigned long flags;
+ uint32_t hostno;
+
+ session = transport->create_session(transport, &priv->t,
+ ev->u.c_session.initial_cmdsn,
+ &hostno);
+ if (!session)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&sesslock, flags);
+ list_add(&session->sess_list, &sesslist);
+ spin_unlock_irqrestore(&sesslock, flags);
+
+ ev->r.c_session_ret.host_no = hostno;
+ ev->r.c_session_ret.sid = session->sid;
+ return 0;
+}
+
+static int
+iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev)
+{
+ struct iscsi_cls_conn *conn;
+ struct iscsi_cls_session *session;
+ unsigned long flags;
+
+ session = iscsi_session_lookup(ev->u.c_conn.sid);
+ if (!session) {
+ printk(KERN_ERR "iscsi: invalid session %d\n",
+ ev->u.c_conn.sid);
+ return -EINVAL;
+ }
+
+ conn = transport->create_conn(session, ev->u.c_conn.cid);
+ if (!conn) {
+ printk(KERN_ERR "iscsi: couldn't create a new "
+ "connection for session %d\n",
+ session->sid);
+ return -ENOMEM;
+ }
+
+ ev->r.c_conn_ret.sid = session->sid;
+ ev->r.c_conn_ret.cid = conn->cid;
+
+ spin_lock_irqsave(&connlock, flags);
+ list_add(&conn->conn_list, &connlist);
+ conn->active = 1;
+ spin_unlock_irqrestore(&connlock, flags);
+
+ return 0;
+}
+
+static int
+iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev)
+{
+ unsigned long flags;
+ struct iscsi_cls_conn *conn;
+
+ conn = iscsi_conn_lookup(ev->u.d_conn.sid, ev->u.d_conn.cid);
+ if (!conn)
+ return -EINVAL;
+ spin_lock_irqsave(&connlock, flags);
+ conn->active = 0;
+ list_del(&conn->conn_list);
+ spin_unlock_irqrestore(&connlock, flags);
+
+ if (transport->destroy_conn)
+ transport->destroy_conn(conn);
+ return 0;
+}
+
+static int
+iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev)
+{
+ char *data = (char*)ev + sizeof(*ev);
+ struct iscsi_cls_conn *conn;
+ struct iscsi_cls_session *session;
+ int err = 0, value = 0;
+
+ session = iscsi_session_lookup(ev->u.set_param.sid);
+ conn = iscsi_conn_lookup(ev->u.set_param.sid, ev->u.set_param.cid);
+ if (!conn || !session)
+ return -EINVAL;
+
+ switch (ev->u.set_param.param) {
+ case ISCSI_PARAM_SESS_RECOVERY_TMO:
+ sscanf(data, "%d", &value);
+ if (value != 0)
+ session->recovery_tmo = value;
+ break;
+ default:
+ err = transport->set_param(conn, ev->u.set_param.param,
+ data, ev->u.set_param.len);
+ }
+
+ return err;
+}
+
+static int
+iscsi_if_transport_ep(struct iscsi_transport *transport,
+ struct iscsi_uevent *ev, int msg_type)
+{
+ struct sockaddr *dst_addr;
+ int rc = 0;
+
+ switch (msg_type) {
+ case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
+ if (!transport->ep_connect)
+ return -EINVAL;
+
+ dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
+ rc = transport->ep_connect(dst_addr,
+ ev->u.ep_connect.non_blocking,
+ &ev->r.ep_connect_ret.handle);
+ break;
+ case ISCSI_UEVENT_TRANSPORT_EP_POLL:
+ if (!transport->ep_poll)
+ return -EINVAL;
+
+ ev->r.retcode = transport->ep_poll(ev->u.ep_poll.ep_handle,
+ ev->u.ep_poll.timeout_ms);
+ break;
+ case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
+ if (!transport->ep_disconnect)
+ return -EINVAL;
+
+ transport->ep_disconnect(ev->u.ep_disconnect.ep_handle);
+ break;
+ }
+ return rc;
+}
+
+static int
+iscsi_tgt_dscvr(struct iscsi_transport *transport,
+ struct iscsi_uevent *ev)
+{
+ struct sockaddr *dst_addr;
+
+ if (!transport->tgt_dscvr)
+ return -EINVAL;
+
+ dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
+ return transport->tgt_dscvr(ev->u.tgt_dscvr.type,
+ ev->u.tgt_dscvr.host_no,
+ ev->u.tgt_dscvr.enable, dst_addr);
+}
+
+static int
+iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+{
+ int err = 0;
+ struct iscsi_uevent *ev = NLMSG_DATA(nlh);
+ struct iscsi_transport *transport = NULL;
+ struct iscsi_internal *priv;
+ struct iscsi_cls_session *session;
+ struct iscsi_cls_conn *conn;
+ unsigned long flags;
+
+ priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle));
+ if (!priv)
+ return -EINVAL;
+ transport = priv->iscsi_transport;
+
+ if (!try_module_get(transport->owner))
+ return -EINVAL;
+
+ priv->daemon_pid = NETLINK_CREDS(skb)->pid;
+
+ switch (nlh->nlmsg_type) {
+ case ISCSI_UEVENT_CREATE_SESSION:
+ err = iscsi_if_create_session(priv, ev);
+ break;
+ case ISCSI_UEVENT_DESTROY_SESSION:
+ session = iscsi_session_lookup(ev->u.d_session.sid);
+ if (session) {
+ spin_lock_irqsave(&sesslock, flags);
+ list_del(&session->sess_list);
+ spin_unlock_irqrestore(&sesslock, flags);
+
+ transport->destroy_session(session);
+ } else
+ err = -EINVAL;
+ break;
+ case ISCSI_UEVENT_CREATE_CONN:
+ err = iscsi_if_create_conn(transport, ev);
+ break;
+ case ISCSI_UEVENT_DESTROY_CONN:
+ err = iscsi_if_destroy_conn(transport, ev);
+ break;
+ case ISCSI_UEVENT_BIND_CONN:
+ session = iscsi_session_lookup(ev->u.b_conn.sid);
+ conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid);
+
+ if (session && conn)
+ ev->r.retcode = transport->bind_conn(session, conn,
+ ev->u.b_conn.transport_eph,
+ ev->u.b_conn.is_leading);
+ else
+ err = -EINVAL;
+ break;
+ case ISCSI_UEVENT_SET_PARAM:
+ err = iscsi_set_param(transport, ev);
+ break;
+ case ISCSI_UEVENT_START_CONN:
+ conn = iscsi_conn_lookup(ev->u.start_conn.sid, ev->u.start_conn.cid);
+ if (conn)
+ ev->r.retcode = transport->start_conn(conn);
+ else
+ err = -EINVAL;
+ break;
+ case ISCSI_UEVENT_STOP_CONN:
+ conn = iscsi_conn_lookup(ev->u.stop_conn.sid, ev->u.stop_conn.cid);
+ if (conn)
+ transport->stop_conn(conn, ev->u.stop_conn.flag);
+ else
+ err = -EINVAL;
+ break;
+ case ISCSI_UEVENT_SEND_PDU:
+ conn = iscsi_conn_lookup(ev->u.send_pdu.sid, ev->u.send_pdu.cid);
+ if (conn)
+ ev->r.retcode = transport->send_pdu(conn,
+ (struct iscsi_hdr*)((char*)ev + sizeof(*ev)),
+ (char*)ev + sizeof(*ev) + ev->u.send_pdu.hdr_size,
+ ev->u.send_pdu.data_size);
+ else
+ err = -EINVAL;
+ break;
+ case ISCSI_UEVENT_GET_STATS:
+ err = iscsi_if_get_stats(transport, nlh);
+ break;
+ case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
+ case ISCSI_UEVENT_TRANSPORT_EP_POLL:
+ case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
+ err = iscsi_if_transport_ep(transport, ev, nlh->nlmsg_type);
+ break;
+ case ISCSI_UEVENT_TGT_DSCVR:
+ err = iscsi_tgt_dscvr(transport, ev);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ module_put(transport->owner);
+ return err;
+}
+
+/*
+ * Get message from skb (based on rtnetlink_rcv_skb). Each message is
+ * processed by iscsi_if_recv_msg. Malformed skbs with wrong lengths or
+ * invalid creds are discarded silently.
+ */
+static void
+iscsi_if_rx(struct sock *sk, int len)
+{
+ struct sk_buff *skb;
+
+ mutex_lock(&rx_queue_mutex);
+ while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
+ if (NETLINK_CREDS(skb)->uid) {
+ skb_pull(skb, skb->len);
+ goto free_skb;
+ }
+
+ while (skb->len >= NLMSG_SPACE(0)) {
+ int err;
+ uint32_t rlen;
+ struct nlmsghdr *nlh;
+ struct iscsi_uevent *ev;
+
+ nlh = (struct nlmsghdr *)skb->data;
+ if (nlh->nlmsg_len < sizeof(*nlh) ||
+ skb->len < nlh->nlmsg_len) {
+ break;
+ }
+
+ ev = NLMSG_DATA(nlh);
+ rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+ if (rlen > skb->len)
+ rlen = skb->len;
+
+ err = iscsi_if_recv_msg(skb, nlh);
+ if (err) {
+ ev->type = ISCSI_KEVENT_IF_ERROR;
+ ev->iferror = err;
+ }
+ do {
+ /*
+ * special case for GET_STATS:
+ * on success - sending reply and stats from
+ * inside of if_recv_msg(),
+ * on error - fall through.
+ */
+ if (ev->type == ISCSI_UEVENT_GET_STATS && !err)
+ break;
+ err = iscsi_if_send_reply(
+ NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq,
+ nlh->nlmsg_type, 0, 0, ev, sizeof(*ev));
+ } while (err < 0 && err != -ECONNREFUSED);
+ skb_pull(skb, rlen);
+ }
+free_skb:
+ kfree_skb(skb);
+ }
+ mutex_unlock(&rx_queue_mutex);
+}
+
+#define iscsi_cdev_to_conn(_cdev) \
+ iscsi_dev_to_conn(_cdev->dev)
+
+#define ISCSI_CLASS_ATTR(_prefix,_name,_mode,_show,_store) \
+struct class_device_attribute class_device_attr_##_prefix##_##_name = \
+ __ATTR(_name,_mode,_show,_store)
+
+/*
+ * iSCSI connection attrs
+ */
+#define iscsi_conn_attr_show(param) \
+static ssize_t \
+show_conn_param_##param(struct class_device *cdev, char *buf) \
+{ \
+ struct iscsi_cls_conn *conn = iscsi_cdev_to_conn(cdev); \
+ struct iscsi_transport *t = conn->transport; \
+ return t->get_conn_param(conn, param, buf); \
+}
+
+#define iscsi_conn_attr(field, param) \
+ iscsi_conn_attr_show(param) \
+static ISCSI_CLASS_ATTR(conn, field, S_IRUGO, show_conn_param_##param, \
+ NULL);
+
+iscsi_conn_attr(max_recv_dlength, ISCSI_PARAM_MAX_RECV_DLENGTH);
+iscsi_conn_attr(max_xmit_dlength, ISCSI_PARAM_MAX_XMIT_DLENGTH);
+iscsi_conn_attr(header_digest, ISCSI_PARAM_HDRDGST_EN);
+iscsi_conn_attr(data_digest, ISCSI_PARAM_DATADGST_EN);
+iscsi_conn_attr(ifmarker, ISCSI_PARAM_IFMARKER_EN);
+iscsi_conn_attr(ofmarker, ISCSI_PARAM_OFMARKER_EN);
+iscsi_conn_attr(persistent_port, ISCSI_PARAM_PERSISTENT_PORT);
+iscsi_conn_attr(port, ISCSI_PARAM_CONN_PORT);
+iscsi_conn_attr(exp_statsn, ISCSI_PARAM_EXP_STATSN);
+iscsi_conn_attr(persistent_address, ISCSI_PARAM_PERSISTENT_ADDRESS);
+iscsi_conn_attr(address, ISCSI_PARAM_CONN_ADDRESS);
+
+#define iscsi_cdev_to_session(_cdev) \
+ iscsi_dev_to_session(_cdev->dev)
+
+/*
+ * iSCSI session attrs
+ */
+#define iscsi_session_attr_show(param) \
+static ssize_t \
+show_session_param_##param(struct class_device *cdev, char *buf) \
+{ \
+ struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev); \
+ struct iscsi_transport *t = session->transport; \
+ return t->get_session_param(session, param, buf); \
+}
+
+#define iscsi_session_attr(field, param) \
+ iscsi_session_attr_show(param) \
+static ISCSI_CLASS_ATTR(sess, field, S_IRUGO, show_session_param_##param, \
+ NULL);
+
+iscsi_session_attr(targetname, ISCSI_PARAM_TARGET_NAME);
+iscsi_session_attr(initial_r2t, ISCSI_PARAM_INITIAL_R2T_EN);
+iscsi_session_attr(max_outstanding_r2t, ISCSI_PARAM_MAX_R2T);
+iscsi_session_attr(immediate_data, ISCSI_PARAM_IMM_DATA_EN);
+iscsi_session_attr(first_burst_len, ISCSI_PARAM_FIRST_BURST);
+iscsi_session_attr(max_burst_len, ISCSI_PARAM_MAX_BURST);
+iscsi_session_attr(data_pdu_in_order, ISCSI_PARAM_PDU_INORDER_EN);
+iscsi_session_attr(data_seq_in_order, ISCSI_PARAM_DATASEQ_INORDER_EN);
+iscsi_session_attr(erl, ISCSI_PARAM_ERL);
+iscsi_session_attr(tpgt, ISCSI_PARAM_TPGT);
+
+#define iscsi_priv_session_attr_show(field, format) \
+static ssize_t \
+show_priv_session_##field(struct class_device *cdev, char *buf) \
+{ \
+ struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev);\
+ return sprintf(buf, format"\n", session->field); \
+}
+
+#define iscsi_priv_session_attr(field, format) \
+ iscsi_priv_session_attr_show(field, format) \
+static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO, show_priv_session_##field, \
+ NULL)
+iscsi_priv_session_attr(recovery_tmo, "%d");
+
+#define SETUP_PRIV_SESSION_RD_ATTR(field) \
+do { \
+ priv->session_attrs[count] = &class_device_attr_priv_sess_##field; \
+ count++; \
+} while (0)
+
+
+#define SETUP_SESSION_RD_ATTR(field, param_flag) \
+do { \
+ if (tt->param_mask & param_flag) { \
+ priv->session_attrs[count] = &class_device_attr_sess_##field; \
+ count++; \
+ } \
+} while (0)
+
+#define SETUP_CONN_RD_ATTR(field, param_flag) \
+do { \
+ if (tt->param_mask & param_flag) { \
+ priv->conn_attrs[count] = &class_device_attr_conn_##field; \
+ count++; \
+ } \
+} while (0)
+
+static int iscsi_session_match(struct attribute_container *cont,
+ struct device *dev)
+{
+ struct iscsi_cls_session *session;
+ struct Scsi_Host *shost;
+ struct iscsi_internal *priv;
+
+ if (!iscsi_is_session_dev(dev))
+ return 0;
+
+ session = iscsi_dev_to_session(dev);
+ shost = iscsi_session_to_shost(session);
+ if (!shost->transportt)
+ return 0;
+
+ priv = to_iscsi_internal(shost->transportt);
+ if (priv->session_cont.ac.class != &iscsi_session_class.class)
+ return 0;
+
+ return &priv->session_cont.ac == cont;
+}
+
+static int iscsi_conn_match(struct attribute_container *cont,
+ struct device *dev)
+{
+ struct iscsi_cls_session *session;
+ struct iscsi_cls_conn *conn;
+ struct Scsi_Host *shost;
+ struct iscsi_internal *priv;
+
+ if (!iscsi_is_conn_dev(dev))
+ return 0;
+
+ conn = iscsi_dev_to_conn(dev);
+ session = iscsi_dev_to_session(conn->dev.parent);
+ shost = iscsi_session_to_shost(session);
+
+ if (!shost->transportt)
+ return 0;
+
+ priv = to_iscsi_internal(shost->transportt);
+ if (priv->conn_cont.ac.class != &iscsi_connection_class.class)
+ return 0;
+
+ return &priv->conn_cont.ac == cont;
+}
+
+static int iscsi_host_match(struct attribute_container *cont,
+ struct device *dev)
+{
+ struct Scsi_Host *shost;
+ struct iscsi_internal *priv;
+
+ if (!scsi_is_host_device(dev))
+ return 0;
+
+ shost = dev_to_shost(dev);
+ if (!shost->transportt ||
+ shost->transportt->host_attrs.ac.class != &iscsi_host_class.class)
+ return 0;
+
+ priv = to_iscsi_internal(shost->transportt);
+ return &priv->t.host_attrs.ac == cont;
+}
+
+struct scsi_transport_template *
+iscsi_register_transport(struct iscsi_transport *tt)
+{
+ struct iscsi_internal *priv;
+ unsigned long flags;
+ int count = 0, err;
+
+ BUG_ON(!tt);
+
+ priv = iscsi_if_transport_lookup(tt);
+ if (priv)
+ return NULL;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return NULL;
+ INIT_LIST_HEAD(&priv->list);
+ priv->daemon_pid = -1;
+ priv->iscsi_transport = tt;
+ priv->t.user_scan = iscsi_user_scan;
+
+ priv->cdev.class = &iscsi_transport_class;
+ snprintf(priv->cdev.class_id, BUS_ID_SIZE, "%s", tt->name);
+ err = class_device_register(&priv->cdev);
+ if (err)
+ goto free_priv;
+
+ err = sysfs_create_group(&priv->cdev.kobj, &iscsi_transport_group);
+ if (err)
+ goto unregister_cdev;
+
+ /* host parameters */
+ priv->t.host_attrs.ac.attrs = &priv->host_attrs[0];
+ priv->t.host_attrs.ac.class = &iscsi_host_class.class;
+ priv->t.host_attrs.ac.match = iscsi_host_match;
+ priv->t.host_size = sizeof(struct iscsi_host);
+ priv->host_attrs[0] = NULL;
+ transport_container_register(&priv->t.host_attrs);
+
+ /* connection parameters */
+ priv->conn_cont.ac.attrs = &priv->conn_attrs[0];
+ priv->conn_cont.ac.class = &iscsi_connection_class.class;
+ priv->conn_cont.ac.match = iscsi_conn_match;
+ transport_container_register(&priv->conn_cont);
+
+ SETUP_CONN_RD_ATTR(max_recv_dlength, ISCSI_MAX_RECV_DLENGTH);
+ SETUP_CONN_RD_ATTR(max_xmit_dlength, ISCSI_MAX_XMIT_DLENGTH);
+ SETUP_CONN_RD_ATTR(header_digest, ISCSI_HDRDGST_EN);
+ SETUP_CONN_RD_ATTR(data_digest, ISCSI_DATADGST_EN);
+ SETUP_CONN_RD_ATTR(ifmarker, ISCSI_IFMARKER_EN);
+ SETUP_CONN_RD_ATTR(ofmarker, ISCSI_OFMARKER_EN);
+ SETUP_CONN_RD_ATTR(address, ISCSI_CONN_ADDRESS);
+ SETUP_CONN_RD_ATTR(port, ISCSI_CONN_PORT);
+ SETUP_CONN_RD_ATTR(exp_statsn, ISCSI_EXP_STATSN);
+ SETUP_CONN_RD_ATTR(persistent_address, ISCSI_PERSISTENT_ADDRESS);
+ SETUP_CONN_RD_ATTR(persistent_port, ISCSI_PERSISTENT_PORT);
+
+ BUG_ON(count > ISCSI_CONN_ATTRS);
+ priv->conn_attrs[count] = NULL;
+ count = 0;
+
+ /* session parameters */
+ priv->session_cont.ac.attrs = &priv->session_attrs[0];
+ priv->session_cont.ac.class = &iscsi_session_class.class;
+ priv->session_cont.ac.match = iscsi_session_match;
+ transport_container_register(&priv->session_cont);
+
+ SETUP_SESSION_RD_ATTR(initial_r2t, ISCSI_INITIAL_R2T_EN);
+ SETUP_SESSION_RD_ATTR(max_outstanding_r2t, ISCSI_MAX_R2T);
+ SETUP_SESSION_RD_ATTR(immediate_data, ISCSI_IMM_DATA_EN);
+ SETUP_SESSION_RD_ATTR(first_burst_len, ISCSI_FIRST_BURST);
+ SETUP_SESSION_RD_ATTR(max_burst_len, ISCSI_MAX_BURST);
+ SETUP_SESSION_RD_ATTR(data_pdu_in_order, ISCSI_PDU_INORDER_EN);
+ SETUP_SESSION_RD_ATTR(data_seq_in_order, ISCSI_DATASEQ_INORDER_EN);
+ SETUP_SESSION_RD_ATTR(erl, ISCSI_ERL);
+ SETUP_SESSION_RD_ATTR(targetname, ISCSI_TARGET_NAME);
+ SETUP_SESSION_RD_ATTR(tpgt, ISCSI_TPGT);
+ SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo);
+
+ BUG_ON(count > ISCSI_SESSION_ATTRS);
+ priv->session_attrs[count] = NULL;
+
+ spin_lock_irqsave(&iscsi_transport_lock, flags);
+ list_add(&priv->list, &iscsi_transports);
+ spin_unlock_irqrestore(&iscsi_transport_lock, flags);
+
+ printk(KERN_NOTICE "iscsi: registered transport (%s)\n", tt->name);
+ return &priv->t;
+
+unregister_cdev:
+ class_device_unregister(&priv->cdev);
+free_priv:
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(iscsi_register_transport);
+
+int iscsi_unregister_transport(struct iscsi_transport *tt)
+{
+ struct iscsi_internal *priv;
+ unsigned long flags;
+
+ BUG_ON(!tt);
+
+ mutex_lock(&rx_queue_mutex);
+
+ priv = iscsi_if_transport_lookup(tt);
+ BUG_ON (!priv);
+
+ spin_lock_irqsave(&iscsi_transport_lock, flags);
+ list_del(&priv->list);
+ spin_unlock_irqrestore(&iscsi_transport_lock, flags);
+
+ transport_container_unregister(&priv->conn_cont);
+ transport_container_unregister(&priv->session_cont);
+ transport_container_unregister(&priv->t.host_attrs);
+
+ sysfs_remove_group(&priv->cdev.kobj, &iscsi_transport_group);
+ class_device_unregister(&priv->cdev);
+ mutex_unlock(&rx_queue_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iscsi_unregister_transport);
+
+static __init int iscsi_transport_init(void)
+{
+ int err;
+
+ printk(KERN_INFO "Loading iSCSI transport class v%s.\n",
+ ISCSI_TRANSPORT_VERSION);
+
+ atomic_set(&iscsi_session_nr, 0);
+
+ err = class_register(&iscsi_transport_class);
+ if (err)
+ return err;
+
+ err = transport_class_register(&iscsi_host_class);
+ if (err)
+ goto unregister_transport_class;
+
+ err = transport_class_register(&iscsi_connection_class);
+ if (err)
+ goto unregister_host_class;
+
+ err = transport_class_register(&iscsi_session_class);
+ if (err)
+ goto unregister_conn_class;
+
+ nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx,
+ THIS_MODULE);
+ if (!nls) {
+ err = -ENOBUFS;
+ goto unregister_session_class;
+ }
+
+ return 0;
+
+unregister_session_class:
+ transport_class_unregister(&iscsi_session_class);
+unregister_conn_class:
+ transport_class_unregister(&iscsi_connection_class);
+unregister_host_class:
+ transport_class_unregister(&iscsi_host_class);
+unregister_transport_class:
+ class_unregister(&iscsi_transport_class);
+ return err;
+}
+
+static void __exit iscsi_transport_exit(void)
+{
+ sock_release(nls->sk_socket);
+ transport_class_unregister(&iscsi_connection_class);
+ transport_class_unregister(&iscsi_session_class);
+ transport_class_unregister(&iscsi_host_class);
+ class_unregister(&iscsi_transport_class);
+}
+
+module_init(iscsi_transport_init);
+module_exit(iscsi_transport_exit);
+
+MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, "
+ "Dmitry Yusupov <dmitry_yus@yahoo.com>, "
+ "Alex Aizman <itn780@yahoo.com>");
+MODULE_DESCRIPTION("iSCSI Transport Interface");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(ISCSI_TRANSPORT_VERSION);
Index: kernel/Makefile
===================================================================
--- kernel/Makefile (revision 754)
+++ kernel/Makefile (revision 779)
@@ -3,7 +3,7 @@
#
# This Makefile invokes KBuild from outside the kernel directory when
# used from the main open-iscsi package. It also contains all of the
-# KBuild stuff needed to build the the modules.
+# KBuild stuff needed to build the modules.
#
# Kbuild stuff, the following is the only part of this file that KBuild
@@ -36,84 +36,119 @@
# this is the basic Kbuild invocation, just append your make target
KBUILD_BASE = +$(MAKE) -C $(KSRC) M=`pwd` KBUILD_OUTPUT=$(KBUILD_OUTPUT) $(KARCH) V=$(V)
-# fun stuff for maintaining multiple versions
+all: kernel_check
+ $(KBUILD_BASE) modules
+
+# ============ BEGIN code for kernel_check and source patching ================
+# We calculate the Linux version for compilation. Than according to version,
+# if needed, we patch source-code to match the compiling kernel.
+# if you need a new kernel sub-version just add a target below.
+#
+# IMPORTANT: do "make clean" before submitting to SVN so source is in unpatched
+# form.
+
+#some constants
+13_patch=2.6.13_compat.patch
+14to15_patch=2.6.14-and-2.6.15-compat.patch
+16to18_patch=2.6.16-18_compat.patch
+19_patch=2.6.19_compat.patch
+all_patches=13_patch 14to15_patch 16to18_patch 19_patch
+cur_patched=cur_patched
+
+## fun stuff for maintaining multiple versions
+
+# check to see if code is unpatched
+unpatch_code=$(shell test -e $(cur_patched) && echo do_unpatch_code )
+
KSUBLEVEL = $(shell cat $(KSRC)/Makefile | awk -F= '/^SUBLEVEL =/ {print $$2}' | \
sed 's/^[ \t]*//;s/[ \t]*$$//')
-ifeq ($(KSUBLEVEL), 11)
-EXTRA_CFLAGS += -DNETLINK_ISCSI=12
-else
-ifeq ($(KSUBLEVEL), 12)
-EXTRA_CFLAGS += -DNETLINK_ISCSI=12
-endif
-endif
+KERNEL_TARGET=linux_2_6_$(KSUBLEVEL)
+kernel_check: $(KERNEL_TARGET)
-all: kernel_check
- $(KBUILD_BASE) modules
-# ====== BEGIN code for kernel_check
+linux_2_6_13: has_13_patch
+linux_2_6_14: has_14to15_patch
-# generic make function
-# syntax:
-# @$(call generic_check_command, CHECKING-COMMAND, PATCHNAME)
-generic_check_command = if $(1) ; then \
- echo "kernel check... FAILED"; \
- echo "Apply the $(2) first!"; exit 1; fi ; \
- echo "kernel check... OK"
+linux_2_6_15: has_14to15_patch
-# this variable defines how we check for iscsi_compat
-check_iscsi_compat_command = ! grep iscsi_compat scsi_transport_iscsi.c >/dev/null
-# this variable is the actual make function
-check_iscsi_compat = $(call generic_check_command, $(check_iscsi_compat_command), $(1))
-# syntax:
-# @$(call check_iscsi_compat, PATCHNAME)
+linux_2_6_16: has_16to18_patch
-# this if-else ladder is horrible
-# but I don't know any quick way to clean it up
-# since Make doesn't support a switch construct
-.NOTPARALLEL: kernel_check
-kernel_check:
-ifeq ($(KSUBLEVEL),16)
- echo "kernel check... OK"
- cp 2.6.16-2.6.19/*.c .
- cp 2.6.16-2.6.19/*.h .
-else
-ifeq ($(KSUBLEVEL),17)
- echo "kernel check... OK"
- cp 2.6.16-2.6.19/*.c .
- cp 2.6.16-2.6.19/*.h .
-else
-ifeq ($(KSUBLEVEL),18)
- echo "kernel check... OK"
- cp 2.6.16-2.6.19/*.c .
- cp 2.6.16-2.6.19/*.h .
-else
-ifeq ($(KSUBLEVEL),19)
- echo "kernel check... OK"
- cp 2.6.16-2.6.19/*.c .
- cp 2.6.16-2.6.19/*.h .
-else
-ifeq ($(KSUBLEVEL),20)
- echo "kernel check... OK"
- cp 2.6.20/*.c .
- cp 2.6.20/*.h .
-else
- @echo "kernel check... UNSUPPORTED KERNEL DETECTED"
- exit 1;
-endif
-endif
-endif
-endif
-endif
+linux_2_6_17: has_16to18_patch
-# ====== END code for kernel_check
+linux_2_6_18: has_16to18_patch
-clean:
+linux_2_6_19: has_19_patch
+
+linux_2_6_20: $(unpatch_code)
+
+linux_2_6_21: $(unpatch_code)
+
+do_unpatch_code:
+ echo "Un-patching source code for use with linux-2.6.20 and up ..."
+ patch -R -E -p1 < $(cur_patched)
+ rm -f `readlink $(cur_patched)`
+ rm -f $(cur_patched)
+
+# these below targets must be the same as the variable name prefixed by has_
+# otherwise below compat_patch: target will not work
+has_13_patch: $(13_patch)
+ echo "Patching source code for linux-2.6.13 ..."
+ if [ -e $(cur_patched) ]; then \
+ make -C . clean; \
+ fi
+ patch -p1 < $(13_patch)
+ cp $(13_patch) $@
+ ln -s $@ $(cur_patched)
+
+has_14to15_patch: $(14to15_patch)
+ echo "Patching source code for linux-2.6.14-15 ..."
+ if [ -e $(cur_patched) ]; then \
+ make -C . clean; \
+ fi
+ patch -p1 < $(14to15_patch)
+ cp $(14to15_patch) $@
+ ln -s $@ $(cur_patched)
+
+has_16to18_patch: $(16to18_patch)
+ echo "Patching source code for linux-2.6.16-18 ..."
+ if [ -e $(cur_patched) ]; then \
+ make -C . clean; \
+ fi
+ patch -p1 < $(16to18_patch)
+ cp $(16to18_patch) $@
+ ln -s $@ $(cur_patched)
+
+has_19_patch: $(19_patch)
+ echo "Patching source code for linux-2.6.19 ..."
+ if [ -e $(cur_patched) ]; then \
+ make -C . clean; \
+ fi
+ patch -p1 < $(19_patch)
+ cp $(19_patch) $@
+ ln -s $@ $(cur_patched)
+
+# ============ END code for kernel_check and source patching =================
+
+clean: $(unpatch_code)
$(KBUILD_BASE) clean
- rm *.c *.h Module.symvers
-#echo rm -f -r *.mod.c .*cmd *.o *.ko .tmp_versions
+ rm -f Module.symvers
+## The folowing compat_patch target is what we need to do to prepare a clean
+# compat_patch set after new code is check-in to svn. To keep patches fuzzless.
+# the new patches are writen into .new files so svn diff of next file will
+# not trip on them.
+compat_patch: $(unpatch_code)
+ test -z "$(svn diff|head)" || { \
+ echo "please run make compat_patch after changse are submited to svn"; \
+ exit 1; \
+ }
+ for the_patch in all_patches ; do \
+ make -C . has_$(the_patch); \
+ svn diff > ${!the_patch}.new; \
+ done
+
# the following is only for convienience
# do not submit to Linus
# it's also called from the toplevel makefile
Index: kernel/scsi_transport_iscsi.h
===================================================================
--- kernel/scsi_transport_iscsi.h (revision 0)
+++ kernel/scsi_transport_iscsi.h (revision 779)
@@ -0,0 +1,224 @@
+/*
+ * iSCSI transport class definitions
+ *
+ * Copyright (C) IBM Corporation, 2004
+ * Copyright (C) Mike Christie, 2004 - 2006
+ * Copyright (C) Dmitry Yusupov, 2004 - 2005
+ * Copyright (C) Alex Aizman, 2004 - 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef SCSI_TRANSPORT_ISCSI_H
+#define SCSI_TRANSPORT_ISCSI_H
+
+#include <linux/device.h>
+#include "iscsi_if.h"
+
+struct scsi_transport_template;
+struct iscsi_transport;
+struct Scsi_Host;
+struct iscsi_cls_conn;
+struct iscsi_conn;
+struct iscsi_cmd_task;
+struct iscsi_mgmt_task;
+struct sockaddr;
+
+/**
+ * struct iscsi_transport - iSCSI Transport template
+ *
+ * @name: transport name
+ * @caps: iSCSI Data-Path capabilities
+ * @create_session: create new iSCSI session object
+ * @destroy_session: destroy existing iSCSI session object
+ * @create_conn: create new iSCSI connection
+ * @bind_conn: associate this connection with existing iSCSI session
+ * and specified transport descriptor
+ * @destroy_conn: destroy inactive iSCSI connection
+ * @set_param: set iSCSI parameter. Return 0 on success, -ENODATA
+ * when param is not supported, and a -Exx value on other
+ * error.
+ * @get_param get iSCSI parameter. Must return number of bytes
+ * copied to buffer on success, -ENODATA when param
+ * is not supported, and a -Exx value on other error
+ * @start_conn: set connection to be operational
+ * @stop_conn: suspend/recover/terminate connection
+ * @send_pdu: send iSCSI PDU, Login, Logout, NOP-Out, Reject, Text.
+ * @session_recovery_timedout: notify LLD a block during recovery timed out
+ * @init_cmd_task: Initialize a iscsi_cmd_task and any internal structs.
+ * Called from queuecommand with session lock held.
+ * @init_mgmt_task: Initialize a iscsi_mgmt_task and any internal structs.
+ * Called from iscsi_conn_send_generic with xmitmutex.
+ * @xmit_cmd_task: Requests LLD to transfer cmd task. Returns 0 or the
+ * the number of bytes transferred on success, and -Exyz
+ * value on error.
+ * @xmit_mgmt_task: Requests LLD to transfer mgmt task. Returns 0 or the
+ * the number of bytes transferred on success, and -Exyz
+ * value on error.
+ * @cleanup_cmd_task: requests LLD to fail cmd task. Called with xmitmutex
+ * and session->lock after the connection has been
+ * suspended and terminated during recovery. If called
+ * from abort task then connection is not suspended
+ * or terminated but sk_callback_lock is held
+ *
+ * Template API provided by iSCSI Transport
+ */
+struct iscsi_transport {
+ struct module *owner;
+ char *name;
+ unsigned int caps;
+ /* LLD sets this to indicate what values it can export to sysfs */
+ unsigned int param_mask;
+ struct scsi_host_template *host_template;
+ /* LLD connection data size */
+ int conndata_size;
+ /* LLD session data size */
+ int sessiondata_size;
+ int max_lun;
+ unsigned int max_conn;
+ unsigned int max_cmd_len;
+ struct iscsi_cls_session *(*create_session) (struct iscsi_transport *it,
+ struct scsi_transport_template *t, uint32_t sn, uint32_t *hn);
+ void (*destroy_session) (struct iscsi_cls_session *session);
+ struct iscsi_cls_conn *(*create_conn) (struct iscsi_cls_session *sess,
+ uint32_t cid);
+ int (*bind_conn) (struct iscsi_cls_session *session,
+ struct iscsi_cls_conn *cls_conn,
+ uint64_t transport_eph, int is_leading);
+ int (*start_conn) (struct iscsi_cls_conn *conn);
+ void (*stop_conn) (struct iscsi_cls_conn *conn, int flag);
+ void (*destroy_conn) (struct iscsi_cls_conn *conn);
+ int (*set_param) (struct iscsi_cls_conn *conn, enum iscsi_param param,
+ char *buf, int buflen);
+ int (*get_conn_param) (struct iscsi_cls_conn *conn,
+ enum iscsi_param param, char *buf);
+ int (*get_session_param) (struct iscsi_cls_session *session,
+ enum iscsi_param param, char *buf);
+ int (*send_pdu) (struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
+ char *data, uint32_t data_size);
+ void (*get_stats) (struct iscsi_cls_conn *conn,
+ struct iscsi_stats *stats);
+ void (*init_cmd_task) (struct iscsi_cmd_task *ctask);
+ void (*init_mgmt_task) (struct iscsi_conn *conn,
+ struct iscsi_mgmt_task *mtask,
+ char *data, uint32_t data_size);
+ int (*xmit_cmd_task) (struct iscsi_conn *conn,
+ struct iscsi_cmd_task *ctask);
+ void (*cleanup_cmd_task) (struct iscsi_conn *conn,
+ struct iscsi_cmd_task *ctask);
+ int (*xmit_mgmt_task) (struct iscsi_conn *conn,
+ struct iscsi_mgmt_task *mtask);
+ void (*session_recovery_timedout) (struct iscsi_cls_session *session);
+ int (*ep_connect) (struct sockaddr *dst_addr, int non_blocking,
+ uint64_t *ep_handle);
+ int (*ep_poll) (uint64_t ep_handle, int timeout_ms);
+ void (*ep_disconnect) (uint64_t ep_handle);
+ int (*tgt_dscvr) (enum iscsi_tgt_dscvr type, uint32_t host_no,
+ uint32_t enable, struct sockaddr *dst_addr);
+};
+
+/*
+ * transport registration upcalls
+ */
+extern struct scsi_transport_template *iscsi_register_transport(struct iscsi_transport *tt);
+extern int iscsi_unregister_transport(struct iscsi_transport *tt);
+
+/*
+ * control plane upcalls
+ */
+extern void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error);
+extern int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
+ char *data, uint32_t data_size);
+
+
+/* Connection's states */
+#define ISCSI_CONN_INITIAL_STAGE 0
+#define ISCSI_CONN_STARTED 1
+#define ISCSI_CONN_STOPPED 2
+#define ISCSI_CONN_CLEANUP_WAIT 3
+
+struct iscsi_cls_conn {
+ struct list_head conn_list; /* item in connlist */
+ void *dd_data; /* LLD private data */
+ struct iscsi_transport *transport;
+ uint32_t cid; /* connection id */
+
+ int active; /* must be accessed with the connlock */
+ struct device dev; /* sysfs transport/container device */
+};
+
+#define iscsi_dev_to_conn(_dev) \
+ container_of(_dev, struct iscsi_cls_conn, dev)
+
+/* Session's states */
+#define ISCSI_STATE_FREE 1
+#define ISCSI_STATE_LOGGED_IN 2
+#define ISCSI_STATE_FAILED 3
+#define ISCSI_STATE_TERMINATE 4
+#define ISCSI_STATE_IN_RECOVERY 5
+#define ISCSI_STATE_RECOVERY_FAILED 6
+
+struct iscsi_cls_session {
+ struct list_head sess_list; /* item in session_list */
+ struct list_head host_list;
+ struct iscsi_transport *transport;
+
+ /* recovery fields */
+ int recovery_tmo;
+ struct delayed_work recovery_work;
+
+ int target_id;
+
+ int sid; /* session id */
+ void *dd_data; /* LLD private data */
+ struct device dev; /* sysfs transport/container device */
+};
+
+#define iscsi_dev_to_session(_dev) \
+ container_of(_dev, struct iscsi_cls_session, dev)
+
+#define iscsi_session_to_shost(_session) \
+ dev_to_shost(_session->dev.parent)
+
+#define starget_to_session(_stgt) \
+ iscsi_dev_to_session(_stgt->dev.parent)
+
+struct iscsi_host {
+ struct list_head sessions;
+ struct mutex mutex;
+};
+
+/*
+ * session and connection functions that can be used by HW iSCSI LLDs
+ */
+extern struct iscsi_cls_session *iscsi_alloc_session(struct Scsi_Host *shost,
+ struct iscsi_transport *transport);
+extern int iscsi_add_session(struct iscsi_cls_session *session,
+ unsigned int target_id);
+extern int iscsi_if_create_session_done(struct iscsi_cls_conn *conn);
+extern int iscsi_if_destroy_session_done(struct iscsi_cls_conn *conn);
+extern struct iscsi_cls_session *iscsi_create_session(struct Scsi_Host *shost,
+ struct iscsi_transport *t,
+ unsigned int target_id);
+extern void iscsi_remove_session(struct iscsi_cls_session *session);
+extern void iscsi_free_session(struct iscsi_cls_session *session);
+extern int iscsi_destroy_session(struct iscsi_cls_session *session);
+extern struct iscsi_cls_conn *iscsi_create_conn(struct iscsi_cls_session *sess,
+ uint32_t cid);
+extern int iscsi_destroy_conn(struct iscsi_cls_conn *conn);
+extern void iscsi_unblock_session(struct iscsi_cls_session *session);
+extern void iscsi_block_session(struct iscsi_cls_session *session);
+
+
+#endif
Index: kernel/2.6.19_compat.patch
===================================================================
--- kernel/2.6.19_compat.patch (revision 0)
+++ kernel/2.6.19_compat.patch (revision 779)
@@ -0,0 +1,74 @@
+diff -Nurp a/libiscsi.c b/libiscsi.c
+--- a/libiscsi.c 2007-02-21 11:57:49.000000000 +0200
++++ b/libiscsi.c 2007-02-21 11:57:49.000000000 +0200
+@@ -716,10 +716,9 @@ again:
+ return rc;
+ }
+
+-static void iscsi_xmitworker(struct work_struct *work)
++static void iscsi_xmitworker(void *data)
+ {
+- struct iscsi_conn *conn =
+- container_of(work, struct iscsi_conn, xmitwork);
++ struct iscsi_conn *conn = data;
+ int rc;
+ /*
+ * serialize Xmit worker on a per-connection basis.
+@@ -1509,7 +1508,7 @@ iscsi_conn_setup(struct iscsi_cls_sessio
+ if (conn->mgmtqueue == ERR_PTR(-ENOMEM))
+ goto mgmtqueue_alloc_fail;
+
+- INIT_WORK(&conn->xmitwork, iscsi_xmitworker);
++ INIT_WORK(&conn->xmitwork, iscsi_xmitworker, conn);
+
+ /* allocate login_mtask used for the login/text sequences */
+ spin_lock_bh(&session->lock);
+diff -Nurp a/libiscsi.h b/libiscsi.h
+--- a/libiscsi.h 2007-01-10 12:38:25.000000000 +0200
++++ b/libiscsi.h 2007-01-10 12:38:25.000000000 +0200
+@@ -25,8 +25,6 @@
+
+ #include <linux/types.h>
+ #include <linux/mutex.h>
+-#include <linux/timer.h>
+-#include <linux/workqueue.h>
+ #include "iscsi_proto.h"
+ #include "iscsi_if.h"
+
+diff -Nurp a/scsi_transport_iscsi.c b/scsi_transport_iscsi.c
+--- a/scsi_transport_iscsi.c 2007-01-10 12:38:25.000000000 +0200
++++ b/scsi_transport_iscsi.c 2007-01-10 12:38:25.000000000 +0200
+@@ -234,11 +234,9 @@ static int iscsi_user_scan(struct Scsi_H
+ return 0;
+ }
+
+-static void session_recovery_timedout(struct work_struct *work)
++static void session_recovery_timedout(void *data)
+ {
+- struct iscsi_cls_session *session =
+- container_of(work, struct iscsi_cls_session,
+- recovery_work.work);
++ struct iscsi_cls_session *session = data;
+
+ dev_printk(KERN_INFO, &session->dev, "iscsi: session recovery timed "
+ "out after %d secs\n", session->recovery_tmo);
+@@ -278,7 +276,7 @@ iscsi_alloc_session(struct Scsi_Host *sh
+
+ session->transport = transport;
+ session->recovery_tmo = 120;
+- INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);
++ INIT_WORK(&session->recovery_work, session_recovery_timedout, session);
+ INIT_LIST_HEAD(&session->host_list);
+ INIT_LIST_HEAD(&session->sess_list);
+
+diff -Nurp a/scsi_transport_iscsi.h b/scsi_transport_iscsi.h
+--- a/scsi_transport_iscsi.h 2007-01-10 12:38:26.000000000 +0200
++++ b/scsi_transport_iscsi.h 2007-01-10 12:38:25.000000000 +0200
+@@ -176,7 +176,7 @@ struct iscsi_cls_session {
+
+ /* recovery fields */
+ int recovery_tmo;
+- struct delayed_work recovery_work;
++ struct work_struct recovery_work;
+
+ int target_id;
Index: utils/fwparam_ibft/fwparam_ibft.c
===================================================================
--- utils/fwparam_ibft/fwparam_ibft.c (revision 0)
+++ utils/fwparam_ibft/fwparam_ibft.c (revision 779)
@@ -0,0 +1,516 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ * Copyright (C) IBM Corporation, 2006
+ *
+ * Authors: Patrick Mansfield <patmans@us.ibm.com>
+ * Mike Anderson <andmike@us.ibm.com>
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "fwparam_ibft.h"
+
+char *progname;
+int debug;
+char default_file_name[] = "/dev/mem";
+char *filename = default_file_name;
+int boot_selected_only;
+
+const char nulls[16]; /* defaults to zero */
+
+/*
+ * Prefix strings, for the "prefixN:NAME=value".
+ */
+#define NETWORK "network"
+#define INITIATOR "iscsi-initiator"
+#define TGT "target"
+
+
+void
+verify_hdr(char *name, struct ibft_hdr *hdr, int id, int version, int length)
+{
+#define VERIFY_HDR_FIELD(val) \
+ if (hdr->val != val) { \
+ fprintf(stderr, \
+ "%s: error, %s structure expected %s %d but" \
+ " got %d\n", \
+ progname, name, #val, hdr->val, val); \
+ exit(1); \
+ }
+
+ if (debug > 1)
+ fprintf(stderr, "%s: verifying %s header\n", __FUNCTION__,
+ name);
+
+ VERIFY_HDR_FIELD(id);
+ VERIFY_HDR_FIELD(version);
+ VERIFY_HDR_FIELD(length);
+
+#undef VERIFY_HDR_FIELD
+}
+
+#define CHECK_HDR(ident, name) \
+ verify_hdr(#name, &ident->hdr, id_##name, version_##name, \
+ sizeof(*ident))
+
+/*
+ * Format 8 byte scsi LUN. Just format 8 bytes of hex, we could also
+ * format in the format as specified in rfc4173 (1-2-3-4, or 1-2), that is
+ * a nice format for humans :)
+ */
+void
+format_lun(char *buf, size_t size, uint8_t *lun)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ snprintf(buf++, size--, "%x", lun[i]);
+}
+
+void
+dump_lun(char *prefix, char *id, uint8_t *lun)
+{
+ char buf[32];
+
+ format_lun(buf, sizeof(buf), lun);
+
+ if (prefix)
+ printf("%s%s=%s\n", prefix, id, buf);
+ else
+ printf("%s=%s\n", id, buf);
+
+}
+
+void
+dump_word(char *prefix, char *id, unsigned short value)
+{
+ if (prefix)
+ printf("%s%s=%d\n", prefix, id, value);
+ else
+ printf("%s=%d\n", id, value);
+}
+
+void
+dump_string(char *prefix, char *id, char *value, int len)
+{
+ if (len == 0)
+ return;
+ /*
+ * Not checking if the offset is non-zero, it is not even passed
+ * in, else we need to pass a start and offset rather than value.
+ */
+
+ /*
+ * prints the string in "value" that has "len" characters (the
+ * printf "*" * means use the next argument as the length).
+ */
+ if (prefix)
+ printf("%s%s=%.*s\n", prefix, id, len, value);
+ else
+ printf("%s=%.*s\n", id, len, value);
+}
+
+void
+format_ipaddr(char *buf, size_t size, uint8_t *ip)
+{
+ if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 &&
+ ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && ip[7] == 0 &&
+ ip[8] == 0 && ip[9] == 0 && ip[10] == 0xff && ip[11] == 0xff) {
+ /*
+ * IPV4
+ */
+ snprintf(buf, size, "%d.%d.%d.%d", ip[12], ip[13], ip[14], ip[15]);
+ } else {
+ /* XXX ... */
+ fprintf(stderr, "%s: warning no IPV6 support.\n", progname);
+ buf[0] = '\0';
+ return;
+ }
+
+}
+
+/*
+ * Dump the 16 byte ipaddr, as IPV6 or IPV4.
+ */
+void
+dump_ipaddr(char *prefix, char *id, uint8_t *ip)
+{
+ char buf[32];
+
+ /*
+ * Assumes all zero means no IP address.
+ */
+ if (!memcmp(ip, nulls, sizeof(nulls)))
+ return;
+
+ format_ipaddr(buf, sizeof(buf), ip);
+
+ if (prefix)
+ printf("%s%s=%s\n", prefix, id, buf);
+ else
+ printf("%s=%s\n", id, buf);
+
+}
+
+/*
+ * Dump the 8 byte mac address
+ */
+void
+dump_mac(char *prefix, char *id, uint8_t *mac)
+{
+ int i;
+
+ if (prefix)
+ printf("%s%s=", prefix, id);
+ else
+ printf("%s=", id);
+
+ for (i = 0; i < 5; i++)
+ printf("%02x:", mac[i]);
+ printf("%02x\n", mac[i]);
+}
+
+
+void
+dump_initiator_prefix(void *ibft_loc, struct ibft_initiator *initiator, char *prefix)
+{
+ if (!initiator)
+ return;
+ /*
+ * Not all fields are (or were) supported by open-iscsi. Plus,
+ * some of these are for discovery.
+ */
+ dump_ipaddr(prefix, "ISNS", initiator->isns_server);
+ dump_ipaddr(prefix, "SLP", initiator->slp_server);
+ dump_ipaddr(prefix, "PRIMARY_RADIUS_SERVER", initiator->pri_radius_server);
+ dump_ipaddr(prefix, "SECONDARY_RADIUS_SERVER", initiator->sec_radius_server);
+ dump_string(prefix, "NAME", ibft_loc +
+ initiator->initiator_name_off, initiator->initiator_name_len);
+}
+
+void
+dump_nic_prefix(void *ibft_loc, struct ibft_nic *nic, char *prefix)
+{
+
+ if (!nic)
+ return;
+
+ dump_mac(prefix, "HWADDR", nic->mac);
+ /*
+ * Assume dhcp if any non-zero portions of its address are set
+ * (again, undocumented).
+ */
+ if (memcmp(nic->dhcp, nulls, sizeof(nic->dhcp))) {
+ dump_ipaddr(prefix, "DHCP", nic->dhcp);
+ } else {
+ dump_ipaddr(prefix, "IPADDR", nic->ip_addr);
+ /*
+ * XXX: Not sure how a mask "prefix" will be used in network
+ * bringup, this sounds less flexible than the normal
+ * masks used.
+ */
+ printf("%s%s:%d\n", prefix, "MASK", nic->subnet_mask_prefix);
+ dump_ipaddr(prefix, "GATEWAY", nic->gateway);
+ dump_ipaddr(prefix, "DNSADDR1", nic->primary_dns);
+ dump_ipaddr(prefix, "DNSADDR2", nic->secondary_dns);
+ }
+
+ dump_string(prefix, "HOSTNAME", ibft_loc + nic->hostname_off,
+ nic->hostname_len);
+ /*
+ * XXX unknown vlan:
+ */
+ dump_word(prefix, "VLAN", nic->vlan);
+ /*
+ * XXX sort of unknown pci_bdf: 8 bits bus, 5 bits device, 3 bits
+ * function.
+ */
+ if (prefix )
+ printf("%s%s=%d:%d:%d\n", prefix, "PCI_BDF",
+ /* bus */ (nic->pci_bdf & 0xff00) >> 8,
+ /* device */ (nic->pci_bdf & 0xf8) >> 3,
+ /* function */ (nic->pci_bdf & 0x07));
+ else
+ printf("%s=%d:%d:%d\n", "PCI_BDF",
+ /* bus */ (nic->pci_bdf & 0xff00) >> 8,
+ /* device */ (nic->pci_bdf & 0xf8) >> 3,
+ /* function */ (nic->pci_bdf & 0x07));
+}
+
+void
+dump_tgt_prefix(void *ibft_loc, struct ibft_tgt *tgt, char *prefix)
+{
+
+ if (!tgt)
+ return;
+
+ dump_ipaddr(prefix, "IPADDR", tgt->ip_addr);
+ dump_word(prefix, "PORT", tgt->port);
+ /*
+ * XXX there should at least be a "no LUN specified field", or
+ * have different location objects, so the setup can search for
+ * the appropriate LU (like mount by label, or use of the
+ * /dev/disk/by-id names, or ....
+ *
+ * Like:
+ * uint8_t lu_type; 0: nothing specified, 1: LUN, 2: misc
+ * name - OS can use any way it wants, would have embedded a
+ * "NAME=string", like "LABEL=myrootvolume", or
+ * "DEV_NAME=/dev/disk/by-id/scsi-198279562093043094003030903".
+ * union lu_value {
+ * uint8_t lun[8];
+ * uint8_t misc_name[64];
+ * };
+ *
+ * Maybe just add an extension header, and let the admin/user put
+ * strings like: "area:VALUE=string" into it?
+ */
+ dump_lun(prefix, "LUN", tgt->lun);
+ dump_string(prefix, "NAME", ibft_loc + tgt->tgt_name_off,
+ tgt->tgt_name_len);
+ /*
+ * Note: don't dump the nic association, just let the IP address take
+ * care of the routing.
+ */
+ /*
+ * Note: don't dump the chap "type", just the chap names and secrets
+ * if any are specified - they imply CHAP and reversed CHAP.
+ */
+ dump_string(prefix, "CHAP_NAME", ibft_loc + tgt->chap_name_off,
+ tgt->chap_name_len);
+ dump_string(prefix, "CHAP_PASSWORD", ibft_loc + tgt->chap_secret_off,
+ tgt->chap_secret_len);
+ dump_string(prefix, "CHAP_NAME_IN", ibft_loc + tgt->rev_chap_name_off,
+ tgt->rev_chap_name_len);
+ dump_string(prefix, "CHAP_PASSWORD_IN",
+ ibft_loc + tgt->rev_chap_secret_off,
+ tgt->rev_chap_secret_len);
+}
+
+/*
+ * Read in and dump ASCII output for ibft starting at ibft_loc.
+ */
+int
+dump_ibft(void *ibft_loc)
+{
+ struct ibft_table_hdr *ibft_hdr = ibft_loc;
+ struct ibft_control *control;
+ struct ibft_initiator *initiator = NULL;
+ struct ibft_nic *nic0 = NULL, *nic1 = NULL;
+ struct ibft_tgt *tgt0 = NULL, *tgt1 = NULL;
+ char sum = 0, *buf = ibft_loc;
+ char prefix[32];
+
+ for (; buf <= (char *) (ibft_loc + ibft_hdr->length);)
+ sum += *buf++;
+ if (sum)
+ fprintf(stderr, "Checksum not zero 0x%x \n", sum);
+
+ control = ibft_loc + sizeof(*ibft_hdr);
+ CHECK_HDR(control, control);
+
+ /*
+ * The ibft is setup to return multiple pieces for each
+ * object (like multiple nic's or multiple targets), but it only
+ * maps 1 initiator, two targets, and two nics, follow that layout
+ * here (i.e. don't search for others).
+ *
+ * Also, unknown what to do for extensions piece, it is not
+ * documented.
+ */
+
+ if (control->initiator_off) {
+ initiator = ibft_loc + control->initiator_off;
+ CHECK_HDR(initiator, initiator);
+ }
+
+ if (control->nic0_off) {
+ nic0 = ibft_loc + control->nic0_off;
+ CHECK_HDR(nic0, nic);
+ }
+
+ if (control->nic1_off) {
+ nic1 = ibft_loc + control->nic1_off;
+ CHECK_HDR(nic1, nic);
+ }
+
+ if (control->tgt0_off) {
+ tgt0 = ibft_loc + control->tgt0_off;
+ CHECK_HDR(tgt0, target);
+ }
+
+ if (control->tgt1_off) {
+ tgt1 = ibft_loc + control->tgt1_off;
+ CHECK_HDR(tgt1, target);
+ }
+
+ if (boot_selected_only) {
+
+ snprintf(prefix, sizeof(prefix), "iSCSI_INITIATOR_");
+
+ if (initiator && (initiator->hdr.flags &
+ INIT_FLAG_FW_SEL_BOOT))
+ dump_initiator_prefix(ibft_loc, initiator, prefix);
+
+ if (nic0 && (nic0->hdr.flags & INIT_FLAG_FW_SEL_BOOT))
+ dump_nic_prefix(ibft_loc, nic0, prefix);
+ else if (nic1 && (nic1->hdr.flags & INIT_FLAG_FW_SEL_BOOT))
+ dump_nic_prefix(ibft_loc, nic1, prefix);
+
+ snprintf(prefix, sizeof(prefix), "iSCSI_TARGET_");
+ if (tgt0 && (tgt0->hdr.flags & INIT_FLAG_FW_SEL_BOOT))
+ dump_tgt_prefix(ibft_loc, tgt0, prefix);
+ else if (tgt1 && (tgt1->hdr.flags & INIT_FLAG_FW_SEL_BOOT))
+ dump_tgt_prefix(ibft_loc, tgt1, prefix);
+
+ } else {
+
+ snprintf(prefix, sizeof(prefix), "%s%d:", INITIATOR, 0);
+ dump_initiator_prefix(ibft_loc, initiator, prefix);
+
+ snprintf(prefix, sizeof(prefix), "%s%d:", NETWORK, 0);
+ dump_nic_prefix(ibft_loc, nic0, prefix);
+ snprintf(prefix, sizeof(prefix), "%s%d:", TGT, 0);
+ dump_tgt_prefix(ibft_loc, tgt0, prefix);
+
+ snprintf(prefix, sizeof(prefix), "%s%d:", NETWORK, 1);
+ dump_nic_prefix(ibft_loc, nic1, prefix);
+ snprintf(prefix, sizeof(prefix), "%s%d:", TGT, 1);
+ dump_tgt_prefix(ibft_loc, tgt1, prefix);
+ }
+
+ return 0;
+}
+
+/*
+ * return the address of the location of string in filebuf, search up to
+ * max bytes of *filebuf, if not found returns NULL.
+ */
+char *
+search_file(char *filebuf, char *string, int len, int max)
+{
+ char *cur = filebuf;
+ char *end = filebuf + max;
+ int i = 0;
+
+ if (debug > 1) {
+ fprintf(stderr,
+ "%s: cur 0x%p, end 0x%p, string '%.4s', len %d\n",
+ __FUNCTION__, cur, end, string, len);
+ }
+ while ((cur < end) && memcmp(cur, string, len)) {
+ if (debug > 2) {
+ fprintf(stderr, "i %d, cur 0x%p: 0x%x ('%c')\n",
+ i, cur, cur[0], cur[0]);
+ i++;
+ }
+ cur++;
+ }
+ if (cur < end)
+ return cur;
+ else
+ return NULL;
+}
+
+int
+main (int argc, char **argv)
+{
+ int fd, option, ret;
+ char *filebuf, *ibft_loc;
+ int start = 512 * 1024; /* 512k */
+ int end_search = (1024 * 1024) - start; /* 512k */
+
+ progname = argv[0];
+
+ while (1) {
+ option = getopt(argc, argv, "f:m:s:e:vhb");
+ if (option == -1)
+ break;
+ switch (option) {
+ case 'b':
+ boot_selected_only = 1;
+ break;
+ case 'e':
+ end_search = strtoul(optarg, NULL, 0);
+ break;
+ case 'f':
+ filename = optarg;
+ break;
+ case 's':
+ start = strtoul(optarg, NULL, 0);
+ break;
+ case 'v':
+ debug++;
+ break;
+ default:
+ fprintf(stderr, "Unknown or bad option '%c'\n", option);
+ case 'h':
+ printf("Usage: %s OPTIONS\n"
+ "-b print only fw boot selected sections\n"
+ "-f file_to_search (default /dev/mem)\n"
+ "-s offset to start search\n"
+ "-e length of search\n"
+ "-v verbose\n",
+ progname);
+ exit(1);
+ }
+ }
+
+ if (debug)
+ fprintf(stderr, "file: %s; start %d, end_search %d, debug %d\n",
+ filename, start, end_search, debug);
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Could not open %s: %s (%d)\n",
+ filename, strerror(errno), errno);
+ exit(1);
+ }
+
+ /*
+ * XXX Possibly warn and exit if start > filesize(fd), or if start +
+ * end_search > filesize(fd). Else, we will get a bus error for
+ * small files (with memmap, and for testing at least, it would
+ * be hard to find a system with less than 1024k).
+ */
+ filebuf = mmap(NULL, end_search, PROT_READ, MAP_PRIVATE, fd, start);
+ if (filebuf == MAP_FAILED) {
+ fprintf(stderr, "Could not mmap %s: %s (%d)\n",
+ filename, strerror(errno), errno);
+ exit(1);
+ }
+
+ ibft_loc = search_file(filebuf, iBFTSTR, strlen(iBFTSTR), end_search);
+ if (ibft_loc) {
+ if (dump_ibft(ibft_loc))
+ ret = 0;
+ else
+ ret = 1;
+ } else
+ ret = 1;
+ munmap(filebuf, end_search);
+ close(fd);
+ exit(ret);
+}
Index: utils/fwparam_ibft/fwparam_ibft.h
===================================================================
--- utils/fwparam_ibft/fwparam_ibft.h (revision 0)
+++ utils/fwparam_ibft/fwparam_ibft.h (revision 779)
@@ -0,0 +1,139 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ * Copyright (C) IBM Corporation, 2006
+ *
+ * Authors: Doug Maxey <dwm@austin.ibm.com>
+ * Patrick Mansfield <patmans@us.ibm.com>
+ *
+ */
+
+#ifndef FWPARAM_IBFT_H_
+#define FWPARAM_IBFT_H_
+
+/* #include <sys/types.h> */
+#include <stdint.h>
+
+/*
+ * Structures here are is based on Doug's original code, and Patrick's
+ * interpretation of the IBM internal design document title the "iSCSI
+ * Boot Firmware Table (iBFT)".
+ */
+#define iBFTSTR "iBFT"
+#define iBFT_SIG { 'i','B','F','T' }
+
+
+/*
+ * These macros are lower case to make the verify_hdr macro easier.
+ */
+#define version_control 1
+#define version_initiator 1
+#define version_nic 1
+#define version_target 1
+#define version_extensions 1
+
+enum ibft_id {
+ id_control = 1,
+ id_initiator,
+ id_nic,
+ id_target,
+ id_extensions,
+};
+
+struct ibft_hdr {
+ uint8_t id;
+ uint8_t version;
+ uint16_t length;
+ uint8_t ind;
+ uint8_t flags;
+};
+
+struct ibft_table_hdr {
+ uint8_t signature[4];
+ uint32_t length;
+ uint8_t revision;
+ uint8_t checksum;
+ uint8_t oemid[6];
+ uint8_t oem_table_id[8];
+ uint8_t rsvd1[24];
+} __attribute__((__packed__));
+
+struct ibft_control {
+ struct ibft_hdr hdr;
+ uint16_t extensions;
+ uint16_t initiator_off;
+ uint16_t nic0_off;
+ uint16_t tgt0_off;
+ uint16_t nic1_off;
+ uint16_t tgt1_off;
+} __attribute__((__packed__));
+
+struct ibft_initiator {
+#define INIT_FLAG_VALID 1
+#define INIT_FLAG_FW_SEL_BOOT 2
+ struct ibft_hdr hdr;
+ uint8_t isns_server[16];
+ uint8_t slp_server[16];
+ uint8_t pri_radius_server[16];
+ uint8_t sec_radius_server[16];
+ uint16_t initiator_name_len;
+ uint16_t initiator_name_off;
+} __attribute__((__packed__));
+
+struct ibft_nic {
+#define NIC_FLAG_VALID 1
+#define NIC_FLAG_FW_SEL_BOOT 2
+ struct ibft_hdr hdr;
+ uint8_t ip_addr[16];
+ uint8_t subnet_mask_prefix;
+ uint8_t origin;
+ uint8_t gateway[16];
+ uint8_t primary_dns[16];
+ uint8_t secondary_dns[16];
+ uint8_t dhcp[16];
+ uint16_t vlan;
+ uint8_t mac[6];
+ uint16_t pci_bdf;
+ uint16_t hostname_len;
+ uint16_t hostname_off;
+} __attribute__((__packed__));
+
+struct ibft_tgt {
+#define TGT_FLAG_VALID 1
+#define TGT_FLAG_FW_SEL_BOOT 2
+#define TGT_FLAG_USE_RADIUS_CHAT 4
+#define TGT_FLAG_USE_RADIUS_RCHAT 8
+ struct ibft_hdr hdr;
+ uint8_t ip_addr[16];
+ uint16_t port;
+ uint8_t lun[8];
+#define TGT_CHAP 1
+#define TGT_MUTUAL_CHAP 2
+ uint8_t chap_type;
+ uint8_t nic_assoc;
+ uint16_t tgt_name_len;
+ uint16_t tgt_name_off;
+ uint16_t chap_name_len;
+ uint16_t chap_name_off;
+ uint16_t chap_secret_len;
+ uint16_t chap_secret_off;
+ uint16_t rev_chap_name_len;
+ uint16_t rev_chap_name_off;
+ uint16_t rev_chap_secret_len;
+ uint16_t rev_chap_secret_off;
+} __attribute__((__packed__));
+
+#endif /* FWPARAM_IBFT_H_ */
Index: utils/fwparam_ibft/Makefile
===================================================================
--- utils/fwparam_ibft/Makefile (revision 0)
+++ utils/fwparam_ibft/Makefile (revision 779)
@@ -0,0 +1,11 @@
+OPTFLAGS ?= -O2 -g
+WARNFLAGS ?= -Wall -Wstrict-prototypes
+CFLAGS += $(OPTFLAGS) $(WARNFLAGS)
+PROGRAMS = fwparam_ibft
+
+all: $(PROGRAMS)
+
+fwparam_ibft: fwparam_ibft.c fwparam_ibft.h
+ $(CC) $(CFLAGS) $^ -o $@
+clean:
+ rm -f *.o $(PROGRAMS)
Index: utils/iscsi-iname.c
===================================================================
--- utils/iscsi-iname.c (revision 0)
+++ utils/iscsi-iname.c (revision 779)
@@ -0,0 +1,140 @@
+/*
+ * iSCSI InitiatorName creation utility
+ * Copyright (C) 2001 Cisco Systems, Inc.
+ * maintained by linux-iscsi-devel@lists.sourceforge.net
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * See the file COPYING included with this distribution for more details.
+ *
+ * $Id: iscsi-iname.c,v 1.1.2.3 2005/03/15 06:33:44 wysochanski Exp $
+ *
+ * iscsi-iname.c - Compute an iSCSI InitiatorName for this host.
+ * Note that to ensure uniqueness, the system time is
+ * a factor. This name must be cached and only regenerated
+ * if there is no cached value.
+ */
+
+#include <stdint.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/utsname.h>
+#include <sys/time.h>
+
+#include "md5.h"
+
+#define RANDOM_NUM_GENERATOR "/dev/urandom"
+
+int
+main(int argc, char *argv[])
+{
+ char iname[256];
+ struct timeval time;
+ struct utsname system_info;
+ long hostid;
+ struct MD5Context context;
+ unsigned char digest[16];
+ unsigned char *bytes = digest;
+ unsigned char entropy[16];
+ int e;
+ int fd;
+ char *prefix;
+
+ /* initialize */
+ memset(iname, 0, sizeof (iname));
+ memset(digest, 0, sizeof (digest));
+ memset(&context, 0, sizeof (context));
+ MD5Init(&context);
+
+ /* take a prefix if given, otherwise use a default. */
+ if (argc > 1 && argv[1]) {
+ prefix = argv[1];
+ if (( strcmp(prefix, "-h") == 0 ) ||
+ ( strcmp(prefix, "--help") == 0 )) {
+ printf("\nDisplays the iSCSI initiator name\n");
+ exit(0);
+ } else if ( strcmp(prefix, "-p") == 0 ) {
+ prefix = argv[2];
+ } else {
+ printf("\nUsage: iscsi-iname [-h | --help | "
+ "-p <prefix>]\n");
+ exit(0);
+ }
+ } else {
+ prefix = "iqn.2005-03.org.open-iscsi";
+ }
+
+ /* try to feed some entropy from the pool to MD5 in order to get
+ * uniqueness properties
+ */
+
+ if ((fd = open(RANDOM_NUM_GENERATOR, O_RDONLY))) {
+ e = read(fd, &entropy, 16);
+ if (e >= 1)
+ MD5Update(&context, (md5byte *)entropy, e);
+ close(fd);
+ }
+
+ /* time the name is created is a factor in order to get
+ * uniqueness properties
+ */
+ if (gettimeofday(&time, NULL) < 0) {
+ perror("error: gettimeofday failed");
+ return 1;
+ }
+ MD5Update(&context, (md5byte *) & time.tv_sec, sizeof (time.tv_sec));
+ MD5Update(&context, (md5byte *) & time.tv_usec, sizeof (time.tv_usec));
+
+ /* hostid */
+ hostid = gethostid();
+ MD5Update(&context, (md5byte *) & hostid, sizeof (hostid));
+
+ /* get the hostname and system name */
+ if (uname(&system_info) < 0) {
+ perror("error: uname failed");
+ return 1;
+ }
+ MD5Update(&context, (md5byte *) system_info.sysname,
+ sizeof (system_info.sysname));
+ MD5Update(&context, (md5byte *) system_info.nodename,
+ sizeof (system_info.nodename));
+ MD5Update(&context, (md5byte *) system_info.release,
+ sizeof (system_info.release));
+ MD5Update(&context, (md5byte *) system_info.version,
+ sizeof (system_info.version));
+ MD5Update(&context, (md5byte *) system_info.machine,
+ sizeof (system_info.machine));
+
+ /* compute the md5 hash of all the bits we just collected */
+ MD5Final(digest, &context);
+
+ /* vary which md5 bytes we pick (though we probably don't need to do
+ * this, since hopefully MD5 produces results such that each byte is as
+ * good as any other).
+ */
+
+ if ((fd = open(RANDOM_NUM_GENERATOR, O_RDONLY))) {
+ if (read(fd, entropy, 1) == 1)
+ bytes = &digest[(entropy[0] % (sizeof(digest) - 6))];
+ close(fd);
+ }
+
+ /* print the prefix followed by 6 bytes of the MD5 hash */
+ sprintf(iname, "%s:%x%x%x%x%x%x", prefix,
+ bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]);
+
+ iname[sizeof (iname) - 1] = '\0';
+ printf("%s\n", iname);
+ return 0;
+}
Index: utils/md5.c
===================================================================
--- utils/md5.c (revision 0)
+++ utils/md5.c (revision 779)
@@ -0,0 +1,242 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * Changed so as no longer to depend on Colin Plumb's `usual.h' header
+ * definitions; now uses stuff from dpkg's config.h.
+ * - Ian Jackson <ijackson@nyx.cs.du.edu>.
+ * Still in the public domain.
+ */
+
+#include <string.h>
+
+#include "md5.h"
+
+#if (__BYTE_ORDER == __BIG_ENDIAN)
+/*
+ * we can compile this away for little endian since
+ * it does not do anything on those archs
+ */
+void
+byteSwap(uint32_t * buf, unsigned words)
+{
+ md5byte *p = (md5byte *) buf;
+
+ do {
+ *buf++ = (uint32_t) ((unsigned) p[3] << 8 | p[2]) << 16 |
+ ((unsigned) p[1] << 8 | p[0]);
+ p += 4;
+ } while (--words);
+}
+#else
+#define byteSwap(buf,words)
+#endif
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void
+MD5Init(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bytes[0] = 0;
+ ctx->bytes[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void
+MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len)
+{
+ uint32_t t;
+
+ /* Update byte count */
+
+ t = ctx->bytes[0];
+ if ((ctx->bytes[0] = t + len) < t)
+ ctx->bytes[1]++; /* Carry from low to high */
+
+ t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */
+ if (t > len) {
+ memcpy((md5byte *) ctx->in + 64 - t, buf, len);
+ return;
+ }
+ /* First chunk is an odd size */
+ memcpy((md5byte *) ctx->in + 64 - t, buf, t);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ buf += t;
+ len -= t;
+
+ /* Process data in 64-byte chunks */
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void
+MD5Final(md5byte digest[16], struct MD5Context *ctx)
+{
+ int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */
+ md5byte *p = (md5byte *) ctx->in + count;
+
+ /* Set the first char of padding to 0x80. There is always room. */
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 56 bytes (-8..55) */
+ count = 56 - 1 - count;
+
+ if (count < 0) { /* Padding forces an extra block */
+ memset(p, 0, count + 8);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ p = (md5byte *) ctx->in;
+ count = 56;
+ }
+ memset(p, 0, count);
+ byteSwap(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ctx->in[14] = ctx->bytes[0] << 3;
+ ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
+ MD5Transform(ctx->buf, ctx->in);
+
+ byteSwap(ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof (ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f,w,x,y,z,in,s) \
+ (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void
+MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+ register uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#endif
Index: utils/md5.h
===================================================================
--- utils/md5.h (revision 0)
+++ utils/md5.h (revision 779)
@@ -0,0 +1,41 @@
+/*
+ * This is the header file for the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * Changed so as no longer to depend on Colin Plumb's `usual.h'
+ * header definitions; now uses stuff from dpkg's config.h
+ * - Ian Jackson <ijackson@nyx.cs.du.edu>.
+ * Still in the public domain.
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+#include <stdint.h>
+
+#define md5byte unsigned char
+
+struct MD5Context {
+ uint32_t buf[4];
+ uint32_t bytes[2];
+ uint32_t in[16];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+void MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+
+#endif /* !MD5_H */
Index: utils/Makefile
===================================================================
--- utils/Makefile (revision 0)
+++ utils/Makefile (revision 779)
@@ -0,0 +1,12 @@
+# This Makefile will work only with GNU make.
+
+CFLAGS += $(OPTFLAGS) -O2 -fno-inline -Wall -Wstrict-prototypes -g
+PROGRAMS = iscsi-iname
+
+all: $(PROGRAMS)
+
+iscsi-iname: md5.o iscsi-iname.o
+ $(CC) $(CFLAGS) $^ $(DBM_LIB) -o $@
+
+clean:
+ rm -f *.o $(PROGRAMS)
Index: include/iscsi_proto.h
===================================================================
--- include/iscsi_proto.h (revision 754)
+++ include/iscsi_proto.h (revision 779)
@@ -41,6 +41,29 @@
#define zero_data(p) {p[0]=0;p[1]=0;p[2]=0;}
/*
+ * If running svn modules we may need to define these.
+ * This should not go upstream since this is already properly defined there
+ */
+#ifdef __CHECKER__
+#define __bitwise__ __attribute__((bitwise))
+#else
+#define __bitwise__
+#endif
+#ifdef __CHECK_ENDIAN__
+#define __bitwise __bitwise__
+#else
+#define __bitwise
+#endif
+
+/* initiator tags; opaque for target */
+typedef uint32_t __bitwise__ itt_t;
+/* below makes sense only for initiator that created this tag */
+#define build_itt(itt, id, age) ((__force itt_t)\
+ ((itt) | ((id) << ISCSI_CID_SHIFT) | ((age) << ISCSI_AGE_SHIFT)))
+#define get_itt(itt) ((__force uint32_t)(itt_t)(itt) & ISCSI_ITT_MASK)
+#define RESERVED_ITT ((__force itt_t)0xffffffff)
+
+/*
* iSCSI Template Message Header
*/
struct iscsi_hdr {
@@ -50,7 +73,7 @@
uint8_t hlength; /* AHSs total length */
uint8_t dlength[3]; /* Data length */
uint8_t lun[8];
- __be32 itt; /* Initiator Task Tag */
+ itt_t itt; /* Initiator Task Tag, opaque for target */
__be32 ttt; /* Target Task Tag */
__be32 statsn;
__be32 exp_statsn;
@@ -111,7 +134,7 @@
uint8_t hlength;
uint8_t dlength[3];
uint8_t lun[8];
- __be32 itt; /* Initiator Task Tag */
+ itt_t itt; /* Initiator Task Tag */
__be32 data_length;
__be32 cmdsn;
__be32 exp_statsn;
@@ -148,7 +171,7 @@
uint8_t hlength;
uint8_t dlength[3];
uint8_t rsvd[8];
- __be32 itt; /* Initiator Task Tag */
+ itt_t itt; /* Initiator Task Tag */
__be32 rsvd1;
__be32 statsn;
__be32 exp_cmdsn;
@@ -206,7 +229,7 @@
uint8_t rsvd3;
uint8_t dlength[3];
uint8_t lun[8];
- __be32 itt; /* Initiator Task Tag */
+ itt_t itt; /* Initiator Task Tag */
__be32 ttt; /* Target Transfer Tag */
__be32 cmdsn;
__be32 exp_statsn;
@@ -221,7 +244,7 @@
uint8_t rsvd3;
uint8_t dlength[3];
uint8_t lun[8];
- __be32 itt; /* Initiator Task Tag */
+ itt_t itt; /* Initiator Task Tag */
__be32 ttt; /* Target Transfer Tag */
__be32 statsn;
__be32 exp_cmdsn;
@@ -237,8 +260,8 @@
uint8_t hlength;
uint8_t dlength[3];
uint8_t lun[8];
- __be32 itt; /* Initiator Task Tag */
- __be32 rtt; /* Reference Task Tag */
+ itt_t itt; /* Initiator Task Tag */
+ itt_t rtt; /* Reference Task Tag */
__be32 cmdsn;
__be32 exp_statsn;
__be32 refcmdsn;
@@ -267,8 +290,8 @@
uint8_t hlength;
uint8_t dlength[3];
uint8_t rsvd2[8];
- __be32 itt; /* Initiator Task Tag */
- __be32 rtt; /* Reference Task Tag */
+ itt_t itt; /* Initiator Task Tag */
+ itt_t rtt; /* Reference Task Tag */
__be32 statsn;
__be32 exp_cmdsn;
__be32 max_cmdsn;
@@ -293,7 +316,7 @@
uint8_t hlength;
uint8_t dlength[3];
uint8_t lun[8];
- __be32 itt; /* Initiator Task Tag */
+ itt_t itt; /* Initiator Task Tag */
__be32 ttt; /* Target Transfer Tag */
__be32 statsn;
__be32 exp_cmdsn;
@@ -311,7 +334,7 @@
uint8_t rsvd3;
uint8_t dlength[3];
uint8_t lun[8];
- __be32 itt;
+ itt_t itt;
__be32 ttt;
__be32 rsvd4;
__be32 exp_statsn;
@@ -331,7 +354,7 @@
uint8_t hlength;
uint8_t dlength[3];
uint8_t lun[8];
- __be32 itt;
+ itt_t itt;
__be32 ttt;
__be32 statsn;
__be32 exp_cmdsn;
@@ -355,7 +378,7 @@
uint8_t hlength;
uint8_t dlength[3];
uint8_t rsvd4[8];
- __be32 itt;
+ itt_t itt;
__be32 ttt;
__be32 cmdsn;
__be32 exp_statsn;
@@ -373,7 +396,7 @@
uint8_t hlength;
uint8_t dlength[3];
uint8_t rsvd4[8];
- __be32 itt;
+ itt_t itt;
__be32 ttt;
__be32 statsn;
__be32 exp_cmdsn;
@@ -392,7 +415,7 @@
uint8_t dlength[3];
uint8_t isid[6]; /* Initiator Session ID */
__be16 tsih; /* Target Session Handle */
- __be32 itt; /* Initiator Task Tag */
+ itt_t itt; /* Initiator Task Tag */
__be16 cid;
__be16 rsvd3;
__be32 cmdsn;
@@ -421,7 +444,7 @@
uint8_t dlength[3];
uint8_t isid[6]; /* Initiator Session ID */
__be16 tsih; /* Target Session Handle */
- __be32 itt; /* Initiator Task Tag */
+ itt_t itt; /* Initiator Task Tag */
__be32 rsvd3;
__be32 statsn;
__be32 exp_cmdsn;
@@ -478,7 +501,7 @@
uint8_t hlength;
uint8_t dlength[3];
uint8_t rsvd2[8];
- __be32 itt; /* Initiator Task Tag */
+ itt_t itt; /* Initiator Task Tag */
__be16 cid;
uint8_t rsvd3[2];
__be32 cmdsn;
@@ -505,7 +528,7 @@
uint8_t hlength;
uint8_t dlength[3];
uint8_t rsvd3[8];
- __be32 itt; /* Initiator Task Tag */
+ itt_t itt; /* Initiator Task Tag */
__be32 rsvd4;
__be32 statsn;
__be32 exp_cmdsn;
@@ -528,7 +551,7 @@
uint8_t opcode;
uint8_t flags;
uint8_t rsvd2[14];
- __be32 itt;
+ itt_t itt;
__be32 begrun;
__be32 runlength;
__be32 exp_statsn;
@@ -580,8 +603,18 @@
#define VALUE_MAXLEN 255
#define TARGET_NAME_MAXLEN VALUE_MAXLEN
-#define DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH 8192
+#define ISCSI_DEF_MAX_RECV_SEG_LEN 8192
+#define ISCSI_MIN_MAX_RECV_SEG_LEN 512
+#define ISCSI_MAX_MAX_RECV_SEG_LEN 16777215
+#define ISCSI_DEF_FIRST_BURST_LEN 65536
+#define ISCSI_MIN_FIRST_BURST_LEN 512
+#define ISCSI_MAX_FIRST_BURST_LEN 16777215
+
+#define ISCSI_DEF_MAX_BURST_LEN 262144
+#define ISCSI_MIN_MAX_BURST_LEN 512
+#define ISCSI_MAX_MAX_BURST_LEN 16777215
+
/************************* RFC 3720 End *****************************/
#endif /* ISCSI_PROTO_H */
Index: doc/iscsiadm.8
===================================================================
--- doc/iscsiadm.8 (revision 754)
+++ doc/iscsiadm.8 (revision 779)
@@ -5,15 +5,25 @@
\fBiscsiadm\fR -m discovery [ -dhV ] [ -t type -p ip:port [ -l ] ] |
[ -o operation ] [ -n name ] [ -v value ]
-\fBiscsiadm\fR -m node [ -dhV ] [ -L all,manual,automatic ] [ -U all,manual,automatic ] [ -S ] [ [ -T targetname -p ip:port | -M sysdir ] [ -l | -u ] ]
+\fBiscsiadm\fR -m node [ -dhV ] [ -L all,manual,automatic ] [ -U all,manual,automatic ] [ -S ] [ [ -T targetname -p ip:port ] [ -l | -u ] ]
[ [ -o operation ] [ -n name ] [ -v value ] [ -p ip:port ] ]
-\fBiscsiadm\fR -m session [ -dhV ] [ -r sessionid [ -R ] [ -u | -s ] ]
+\fBiscsiadm\fR -m session [ -dhV ] [ -r sessionid | sysfsdir [ -R ] [ -u | -s ] ]
.SH "DESCRIPTION"
The iscsiadm utility is a command-line tool allowing discovery and login
to iSCSI targets, as well as access and management of the open-iscsi
database.
+
+Open-iscsi does not use the term node as defined by the iSCSI RFC,
+where a node is a single iSCSI initiator or target. Open-iscsi uses the
+term node to refer to a portal on a target, so tools like iscsiadm
+require that --targetname and --portal argument be used when in node mode.
+
+For session mode, a session id (sid) is used. The sid of a session can be
+found by running iscsiadm -m session -i. The session id is not currently
+persistent and is partially determined by when the session is setup.
+
.PP
Note that many of the node and discovery operations require that the iSCSI
daemon (iscsid) be running.
@@ -72,7 +82,7 @@
Specifies a database operator \fIop\fR. \fIop\fR must be one of
\fInew\fR, \fIdelete\fR, \fIupdate\fR or \fIshow\fR.
.IP
-This option is only valid for all modes.
+This option is only valid for all modes, but delete should not be used on a running session.
.IP
\fInew\fR is currently valid only for node and session mode. It creates a new
database record for a given \fIportal\fR (IP address and port number).
@@ -95,16 +105,23 @@
.IP
This option is only valid for discovery, or for node operations with
the \fInew\fR operator.
+.IP
+This should be used along with --target in node mode, to specify what the open-iscsi docs refer to as a node or node record. Note: open-iscsi's use of the word node, does not match the iSCSI RFC's iSCSI Node term.
.TP
\fB\-T\fR, \fB\-\-targetname=\fItargetname\fR
Use target \fItargetname\fR.
+.IP
+This should be used along with --portal in node mode, to specify what the open-iscsi docs refer to as a node or node record. Note: open-iscsi's use of the word node, does not match the iSCSI RFC's iSCSI Node term.
.TP
-\fB\-r\fR, \fB\-\-sid=\fIsid\fR
-Use session ID \fIsid\fR.
+\fB\-r\fR, \fB\-\-sid=\fIsid | sysfsdir\fR
+Use session ID \fIsid\fR. The sid of a session can be found from running
+iscsiadm in session mode with the --info argument.
+
+Instead of sid, a sysfs path containing the session can be used. For example using one of the following: /sys/devices/platform/hostH/sessionS/targetH:B:I/H:B:I:L, /sys/devices/platform/hostH/sessionS/targetH:B:I, or /sys/devices/platform/hostH/sessionS, for the sysfsdir argument would result in the session with sid S to be used.
.IP
-\fIsid\fR is only required for session mode.
+\fIsid | sysfsdir\fR is only required for session mode.
.TP
\fB\-R\fR, \fB\-\-rescan\fR
@@ -129,8 +146,8 @@
.TP
\fB\-t\fR, \fB\-\-type=\fItype\fR
\fItype\fR must be \fIsendtargets\fR (or abbreviated as \fIst\fR),
-\fIslp\fR, or \fIisns\fR. Currently only sendtargets is supported, see the
-DISCOVERY TYPES section.
+\fIslp\fR, or \fIisns\fR. Currently only sendtargets and iSNS is supported,
+see the DISCOVERY TYPES section.
.IP
This option is only valid for discovery mode.
@@ -178,18 +195,18 @@
.TP
.B
iSNS
-iSNS (Internet Storage Name Service) is a proposed standard to record
-information about storage volumes within a larger network. In theory
-it can be implemented for iSCSI and Fibre Channel. However, no
-implementation of the central nameserver mandated by this standard is
-currently available.
+iSNS (Internet Storage Name Service) records information about storage
+volumes within a larger network. To utilize iSNS, the address of the
+iSNS server must be set in iscsid.conf using the "isns.address" value,
+and iscsiadm must be run in discovery mode with the "isns" discovery type.
+
.P
-Currently open-iscsi supports only the
+iscsiadm supports the
.B
-SendTargets
+iSNS (isns)
or
.B
-st
+SendTargets (st)
discovery type. An SLP implementation is under development.
.SH EXAMPLES
Index: usr/mgmt_ipc.c
===================================================================
--- usr/mgmt_ipc.c (revision 754)
+++ usr/mgmt_ipc.c (revision 779)
@@ -207,6 +207,12 @@
return MGMT_IPC_ERR;
}
+static mgmt_ipc_err_e
+mgmt_ipc_isns_dev_attr_query(queue_task_t *qtask)
+{
+ return isns_dev_attr_query_task(qtask);
+}
+
static int
mgmt_peeruser(int sock, char *user)
{
@@ -291,6 +297,21 @@
#endif
}
+void
+mgmt_ipc_write_rsp(queue_task_t *qtask, mgmt_ipc_err_e err)
+{
+ log_debug(4, "%s: rsp to fd %d", __FUNCTION__,
+ qtask->mgmt_ipc_fd);
+
+ if (qtask->mgmt_ipc_fd < 0)
+ return;
+
+ qtask->rsp.err = err;
+ write(qtask->mgmt_ipc_fd, &qtask->rsp, sizeof(qtask->rsp));
+ close(qtask->mgmt_ipc_fd);
+ free(qtask);
+}
+
static int
mgmt_ipc_handle(int accept_fd)
{
@@ -332,8 +353,8 @@
rc = -ENOMEM;
goto err;
}
- memcpy(&qtask->u.login.req, &req, sizeof(iscsiadm_req_t));
- qtask->u.login.mgmt_ipc_fd = fd;
+ memcpy(&qtask->req, &req, sizeof(iscsiadm_req_t));
+ qtask->mgmt_ipc_fd = fd;
switch(req.command) {
case MGMT_IPC_SESSION_LOGIN:
@@ -379,6 +400,9 @@
immrsp = 1;
rc = 1;
break;
+ case MGMT_IPC_ISNS_DEV_ATTR_QUERY:
+ rsp.err = mgmt_ipc_isns_dev_attr_query(qtask);
+ break;
default:
log_error("unknown request: %s(%d) %u",
__FUNCTION__, __LINE__, req.command);
@@ -428,10 +452,12 @@
#define POLL_CTRL 0
#define POLL_IPC 1
-#define POLL_MAX 2
+#define POLL_ISNS 2
+#define POLL_MAX 3
/* TODO: this should go somewhere else */
-void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd)
+void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd,
+ int isns_fd)
{
struct pollfd poll_array[POLL_MAX];
int res;
@@ -441,6 +467,13 @@
poll_array[POLL_IPC].fd = mgmt_ipc_fd;
poll_array[POLL_IPC].events = POLLIN;
+ if (isns_fd < 0)
+ poll_array[POLL_ISNS].fd = poll_array[POLL_ISNS].events = 0;
+ else {
+ poll_array[POLL_ISNS].fd = isns_fd;
+ poll_array[POLL_ISNS].events = POLLIN;
+ }
+
while (1) {
res = poll(poll_array, POLL_MAX, ACTOR_RESOLUTION);
if (res > 0) {
@@ -451,6 +484,9 @@
if (poll_array[POLL_IPC].revents)
if (mgmt_ipc_handle(mgmt_ipc_fd) == 1)
break;
+ if (poll_array[POLL_ISNS].revents)
+ isns_handle(isns_fd);
+
} else if (res < 0) {
if (errno == EINTR) {
log_debug(1, "event_loop interrupted");
Index: usr/iscsi_settings.h
===================================================================
--- usr/iscsi_settings.h (revision 0)
+++ usr/iscsi_settings.h (revision 779)
@@ -0,0 +1,15 @@
+/*
+ * Default initiator settings. These may not be the same as
+ * in the RFC. See iscsi_proto.h for those.
+ */
+/* timeouts in seconds */
+#define DEF_LOGIN_TIMEO 15
+#define DEF_LOGOUT_TIMEO 15
+#define DEF_NOOP_OUT_INTERVAL 10
+#define DEF_NOOP_OUT_TIMEO 15
+#define DEF_REPLACEMENT_TIMEO 120
+
+/* data and segment lengths in bytes */
+#define DEF_INI_FIRST_BURST_LEN 262144
+#define DEF_INI_MAX_BURST_LEN 16776192
+#define DEF_INI_MAX_RECV_SEG_LEN 131072
Index: usr/mgmt_ipc.h
===================================================================
--- usr/mgmt_ipc.h (revision 754)
+++ usr/mgmt_ipc.h (revision 779)
@@ -44,6 +44,7 @@
MGMT_IPC_ERR_TRANS_CAPS = 14,
MGMT_IPC_ERR_EXISTS = 15,
MGMT_IPC_ERR_INVALID_REQ = 16,
+ MGMT_IPC_ERR_ISNS_UNAVAILABLE = 17,
} mgmt_ipc_err_e;
typedef enum iscsiadm_cmd {
@@ -60,6 +61,7 @@
MGMT_IPC_IMMEDIATE_STOP = 11,
MGMT_IPC_SESSION_SYNC = 12,
MGMT_IPC_SESSION_INFO = 13,
+ MGMT_IPC_ISNS_DEV_ATTR_QUERY = 14,
} iscsiadm_cmd_e;
typedef enum iscsi_conn_state_e {
@@ -125,7 +127,10 @@
struct iscsi_ipc *ipc;
void need_reap(void);
-void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd);
+void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd, int isns_fd);
+
+struct queue_task;
+void mgmt_ipc_write_rsp(struct queue_task *qtask, mgmt_ipc_err_e err);
int mgmt_ipc_listen(void);
void mgmt_ipc_close(int fd);
Index: usr/ioctl.c
===================================================================
--- usr/ioctl.c (revision 754)
+++ usr/ioctl.c (revision 779)
@@ -44,10 +44,10 @@
static void *pdu_sendbuf;
#define IOCTL_BUF_DEFAULT_MAX \
- (DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH + sizeof(struct iscsi_hdr))
+ (ISCSI_DEF_MAX_RECV_SEG_LEN + sizeof(struct iscsi_hdr))
#define PDU_SENDBUF_DEFAULT_MAX \
- (DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH + sizeof(struct iscsi_hdr))
+ (ISCSI_DEF_MAX_RECV_SEG_LEN + sizeof(struct iscsi_hdr))
static int
kread(char *data, int count)
Index: usr/util.c
===================================================================
--- usr/util.c (revision 754)
+++ usr/util.c (revision 779)
@@ -19,6 +19,7 @@
#include "config.h"
#include "initiator.h"
#include "version.h"
+#include "iscsi_settings.h"
void daemon_init(void)
{
@@ -170,11 +171,11 @@
rec->session.auth.password_in_length = 0;
rec->session.err_timeo.abort_timeout = 10;
rec->session.err_timeo.reset_timeout = 30;
- rec->session.timeo.replacement_timeout = 120;
+ rec->session.timeo.replacement_timeout = DEF_REPLACEMENT_TIMEO;
rec->session.iscsi.InitialR2T = 0;
rec->session.iscsi.ImmediateData = 1;
- rec->session.iscsi.FirstBurstLength = 256 * 1024;
- rec->session.iscsi.MaxBurstLength = (16 * 1024 * 1024) - 1024;
+ rec->session.iscsi.FirstBurstLength = DEF_INI_FIRST_BURST_LEN;
+ rec->session.iscsi.MaxBurstLength = DEF_INI_MAX_BURST_LEN;
rec->session.iscsi.DefaultTime2Wait = 0;
rec->session.iscsi.DefaultTime2Retain = 0;
rec->session.iscsi.MaxConnections = 1;
@@ -186,17 +187,18 @@
rec->conn[i].port = DEF_ISCSI_PORT;
rec->conn[i].tcp.window_size = 512 * 1024;
rec->conn[i].tcp.type_of_service = 0;
- rec->conn[i].timeo.login_timeout=15;
- rec->conn[i].timeo.logout_timeout=15;
+ rec->conn[i].timeo.login_timeout= DEF_LOGIN_TIMEO;
+ rec->conn[i].timeo.logout_timeout= DEF_LOGOUT_TIMEO;
rec->conn[i].timeo.auth_timeout = 45;
rec->conn[i].timeo.active_timeout=5;
rec->conn[i].timeo.idle_timeout = 60;
rec->conn[i].timeo.ping_timeout = 5;
- rec->conn[i].timeo.noop_out_interval = 0;
- rec->conn[i].timeo.noop_out_timeout = 0;
+ rec->conn[i].timeo.noop_out_interval = DEF_NOOP_OUT_INTERVAL;
+ rec->conn[i].timeo.noop_out_timeout = DEF_NOOP_OUT_TIMEO;
- rec->conn[i].iscsi.MaxRecvDataSegmentLength = 128 * 1024;
+ rec->conn[i].iscsi.MaxRecvDataSegmentLength =
+ DEF_INI_MAX_RECV_SEG_LEN;
rec->conn[i].iscsi.HeaderDigest = CONFIG_DIGEST_PREFER_OFF;
rec->conn[i].iscsi.DataDigest = CONFIG_DIGEST_NEVER;
rec->conn[i].iscsi.IFMarker = 0;
@@ -224,6 +226,8 @@
/* 13 */ "daemon access denied",
/* 14 */ "iSCSI transport capability failure",
/* 15 */ "already exists",
+ /* 16 */ "Unknown request",
+ /* 17 */ "encountered iSNS failure",
};
log_error("initiator reported error (%d - %s)", err, err_msgs[err]);
}
Index: usr/Makefile
===================================================================
--- usr/Makefile (revision 754)
+++ usr/Makefile (revision 779)
@@ -34,11 +34,11 @@
PROGRAMS = iscsid iscsiadm iscsistart
# sources shared between iscsid, iscsiadm and iscsistart
-ISCSI_LIB_SRCS = util.o io.o auth.o login.o log.o md5.o sha1.o iscsi_sysfs.o
+ISCSI_LIB_SRCS = util.o io.o auth.o login.o log.o md5.o sha1.o iscsi_sysfs.o idbm.o
# sources shared between iscsid and iscsiadm
-COMMON_SRCS = $(ISCSI_LIB_SRCS) idbm.o
+COMMON_SRCS = $(ISCSI_LIB_SRCS)
# core initiator files
-INITIATOR_SRCS = initiator.o queue.o actor.o mgmt_ipc.o transport.o
+INITIATOR_SRCS = initiator.o queue.o actor.o mgmt_ipc.o isns.o transport.o
all: $(PROGRAMS)
Index: usr/initiator.c
===================================================================
--- usr/initiator.c (revision 754)
+++ usr/initiator.c (revision 779)
@@ -45,6 +45,7 @@
#include "log.h"
#include "util.h"
#include "iscsi_sysfs.h"
+#include "iscsi_settings.h"
static void __session_mainloop(void *data);
static void __conn_error_handle(iscsi_session_t*, iscsi_conn_t*);
@@ -136,21 +137,6 @@
set_device_online);
}
-static void
-write_mgmt_rsp(queue_task_t *qtask, mgmt_ipc_err_e err)
-{
- log_debug(4, "%s: rsp to fd %d", __FUNCTION__,
- qtask->u.login.mgmt_ipc_fd);
- if (qtask->u.login.mgmt_ipc_fd == 0)
- return;
-
- qtask->u.login.rsp.err = err;
- write(qtask->u.login.mgmt_ipc_fd, &qtask->u.login.rsp,
- sizeof(qtask->u.login.rsp));
- close(qtask->u.login.mgmt_ipc_fd);
- free(qtask);
-}
-
static conn_login_status_e
__login_response_status(iscsi_conn_t *conn,
enum iscsi_login_status login_status)
@@ -367,24 +353,65 @@
/* connection's timeouts */
conn->id = cid;
conn->logout_timeout = conn_rec->timeo.logout_timeout;
+ if (!conn->logout_timeout) {
+ log_error("Invalid timeo.logout_timeout. Must be greater "
+ "than zero. Using default %d.\n",
+ DEF_LOGOUT_TIMEO);
+ conn->logout_timeout = DEF_LOGOUT_TIMEO;
+ }
+
conn->login_timeout = conn_rec->timeo.login_timeout;
+ if (!conn->login_timeout) {
+ log_error("Invalid timeo.login_timeout. Must be greater "
+ "than zero. Using default %d.\n",
+ DEF_LOGIN_TIMEO);
+ conn->login_timeout = DEF_LOGIN_TIMEO;
+ }
+
+ /* noop-out setting */
+ conn->noop_out_interval = conn_rec->timeo.noop_out_interval;
+ conn->noop_out_timeout = conn_rec->timeo.noop_out_timeout;
+ if (conn->noop_out_interval && !conn->noop_out_timeout) {
+ log_error("Invalid timeo.noop_out_timeout. Must be greater "
+ "than zero. Using default %d.\n",
+ DEF_NOOP_OUT_TIMEO);
+ conn->noop_out_timeout = DEF_NOOP_OUT_TIMEO;
+ }
+
+ if (conn->noop_out_timeout && !conn->noop_out_interval) {
+ log_error("Invalid timeo.noop_out_interval. Must be greater "
+ "than zero. Using default %d.\n",
+ DEF_NOOP_OUT_INTERVAL);
+ conn->noop_out_timeout = DEF_NOOP_OUT_INTERVAL;
+ }
+
+ /*
+ * currently not used (leftover from linux-iscsi which we
+ * may do one day)
+ */
conn->auth_timeout = conn_rec->timeo.auth_timeout;
conn->active_timeout = conn_rec->timeo.active_timeout;
conn->idle_timeout = conn_rec->timeo.idle_timeout;
conn->ping_timeout = conn_rec->timeo.ping_timeout;
- /* noop-out setting */
- conn->noop_out_interval = conn_rec->timeo.noop_out_interval;
- conn->noop_out_timeout = conn_rec->timeo.noop_out_timeout;
-
/* operational parameters */
conn->max_recv_dlength =
__padding(conn_rec->iscsi.MaxRecvDataSegmentLength);
+ if (conn->max_recv_dlength < ISCSI_MIN_MAX_RECV_SEG_LEN ||
+ conn->max_recv_dlength > ISCSI_MAX_MAX_RECV_SEG_LEN) {
+ log_error("Invalid iscsi.MaxRecvDataSegmentLength. Must be "
+ "within %u and %u. Setting to %u\n",
+ ISCSI_MIN_MAX_RECV_SEG_LEN,
+ ISCSI_MAX_MAX_RECV_SEG_LEN,
+ DEF_INI_MAX_RECV_SEG_LEN);
+ conn->max_recv_dlength = DEF_INI_MAX_RECV_SEG_LEN;
+ }
+
/*
* iSCSI default, unless declared otherwise by the
* target during login
*/
- conn->max_xmit_dlength = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH;
+ conn->max_xmit_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN;
conn->hdrdgst_en = conn_rec->iscsi.HeaderDigest;
conn->datadgst_en = conn_rec->iscsi.DataDigest;
@@ -486,6 +513,7 @@
session->ctrl_fd = control_fd;
session->transport_handle = provider->handle;
session->provider = provider;
+ session->reopen_qtask.mgmt_ipc_fd = -1;
/* save node record. we might need it for redirection */
memcpy(&session->nrec, rec, sizeof(node_rec_t));
@@ -506,7 +534,27 @@
session->initial_r2t_en = rec->session.iscsi.InitialR2T;
session->imm_data_en = rec->session.iscsi.ImmediateData;
session->first_burst = __padding(rec->session.iscsi.FirstBurstLength);
+ if (session->first_burst < ISCSI_MIN_FIRST_BURST_LEN ||
+ session->first_burst > ISCSI_MAX_FIRST_BURST_LEN) {
+ log_error("Invalid iscsi.FirstBurstLength of %u. Must be "
+ "within %u and %u. Setting to %u\n",
+ session->first_burst,
+ ISCSI_MIN_FIRST_BURST_LEN,
+ ISCSI_MAX_FIRST_BURST_LEN,
+ DEF_INI_FIRST_BURST_LEN);
+ session->first_burst = DEF_INI_FIRST_BURST_LEN;
+ }
+
session->max_burst = __padding(rec->session.iscsi.MaxBurstLength);
+ if (session->max_burst < ISCSI_MIN_MAX_BURST_LEN ||
+ session->max_burst > ISCSI_MAX_MAX_BURST_LEN) {
+ log_error("Invalid iscsi.MaxBurstLength of %u. Must be "
+ "within %u and %u. Setting to %u\n",
+ session->max_burst, ISCSI_MIN_MAX_BURST_LEN,
+ ISCSI_MAX_MAX_BURST_LEN, DEF_INI_MAX_BURST_LEN);
+ session->max_burst = DEF_INI_MAX_BURST_LEN;
+ }
+
session->def_time2wait = rec->session.iscsi.DefaultTime2Wait;
session->def_time2retain = rec->session.iscsi.DefaultTime2Retain;
session->erl = rec->session.iscsi.ERL;
@@ -523,7 +571,7 @@
if (session->replacement_timeout == 0) {
log_error("Cannot set replacement_timeout to zero. Setting "
"120 seconds\n");
- session->replacement_timeout = 120;
+ session->replacement_timeout = DEF_REPLACEMENT_TIMEO;
}
/* OUI and uniqifying number */
@@ -580,7 +628,7 @@
iscsi_conn_t *conn = qtask->conn;
iscsi_session_t *session = conn->session;
- write_mgmt_rsp(qtask, err);
+ mgmt_ipc_write_rsp(qtask, err);
session_conn_destroy(session, conn->id);
if (conn->id == 0)
__session_destroy(session);
@@ -668,7 +716,9 @@
iscsi_conn_t *conn = (iscsi_conn_t*)data;
iscsi_session_t *session = conn->session;
- log_debug(3, "noop out rsp timeout, closing conn...\n");
+ log_warning("Nop-out timedout after %d seconds on connection %d:%d "
+ "state (%d). Dropping session.", conn->noop_out_timeout,
+ session->id, conn->id, conn->state);
/* XXX: error handle */
__conn_error_handle(session, conn);
}
@@ -730,7 +780,7 @@
* iSCSI default, unless declared otherwise by the
* target during login
*/
- conn->max_xmit_dlength = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH;
+ conn->max_xmit_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN;
conn->hdrdgst_en = conn_rec->iscsi.HeaderDigest;
conn->datadgst_en = conn_rec->iscsi.DataDigest;
@@ -876,14 +926,14 @@
pid = scan_host(session);
if (pid == 0) {
- write_mgmt_rsp(qtask, MGMT_IPC_OK);
+ mgmt_ipc_write_rsp(qtask, MGMT_IPC_OK);
exit(0);
} else if (pid > 0) {
- close(qtask->u.login.mgmt_ipc_fd);
+ close(qtask->mgmt_ipc_fd);
need_reap();
free(qtask);
} else
- write_mgmt_rsp(qtask, MGMT_IPC_ERR_INTERNAL);
+ mgmt_ipc_write_rsp(qtask, MGMT_IPC_ERR_INTERNAL);
}
static void
@@ -1070,8 +1120,10 @@
log_warning("connection%d:%d is operational now",
session->id, conn->id);
} else {
+ session->sync_qtask = NULL;
+
__session_online_devs(session);
- write_mgmt_rsp(c->qtask, MGMT_IPC_OK);
+ mgmt_ipc_write_rsp(c->qtask, MGMT_IPC_OK);
log_warning("connection%d:%d is operational after recovery "
"(%d attempts)", session->id, conn->id,
session->reopen_cnt);
@@ -1240,7 +1292,7 @@
case STATE_LOGOUT_REQUESTED:
/* read incomming PDU */
if (!iscsi_io_recv_pdu(conn, &hdr, ISCSI_DIGEST_NONE,
- conn->data, DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH,
+ conn->data, ISCSI_DEF_MAX_RECV_SEG_LEN,
ISCSI_DIGEST_NONE, 0)) {
return;
}
@@ -1261,17 +1313,17 @@
}
break;
case STATE_XPT_WAIT:
- recvpool_put(conn, conn->recv_handle);
+ recvpool_put(conn, (void *)conn->recv_handle);
log_debug(1, "ignoring incomming PDU in XPT_WAIT. "
"let connection re-establish or fail");
break;
case STATE_CLEANUP_WAIT:
- recvpool_put(conn, conn->recv_handle);
+ recvpool_put(conn, (void *)conn->recv_handle);
log_debug(1, "ignoring incomming PDU in XPT_WAIT. "
"let connection cleanup");
break;
default:
- recvpool_put(conn, conn->recv_handle);
+ recvpool_put(conn, (void *)conn->recv_handle);
log_error("Invalid state. Dropping PDU.\n");
}
}
@@ -1544,8 +1596,14 @@
break;
case STATE_IN_LOGIN:
if (session->r_stage == R_STAGE_SESSION_REOPEN) {
- session_conn_reopen(conn, &session->reopen_qtask,
- STOP_CONN_RECOVER);
+ queue_task_t *qtask;
+
+ if (session->sync_qtask)
+ qtask = session->sync_qtask;
+ else
+ qtask = &session->reopen_qtask;
+
+ session_conn_reopen(conn, qtask, STOP_CONN_RECOVER);
return;
}
@@ -1579,7 +1637,7 @@
iscsi_conn_t *conn = item->context;
iscsi_session_t *session = conn->session;
- log_warning("detected iSCSI connection %d:%d error (%d) "
+ log_warning("Kernel reported iSCSI connection %d:%d error (%d) "
"state (%d)", session->id, conn->id, error,
conn->state);
__conn_error_handle(session, conn);
@@ -1820,8 +1878,8 @@
actor_timer(&conn->connect_timer, conn->login_timeout*1000,
__connect_timedout, qtask);
- qtask->u.login.rsp.command = MGMT_IPC_SESSION_LOGIN;
- qtask->u.login.rsp.err = MGMT_IPC_OK;
+ qtask->rsp.command = MGMT_IPC_SESSION_LOGIN;
+ qtask->rsp.err = MGMT_IPC_OK;
return MGMT_IPC_OK;
}
@@ -1879,7 +1937,8 @@
goto destroy_session;
}
- qtask->u.login.rsp.command = MGMT_IPC_SESSION_SYNC;
+ session->sync_qtask = qtask;
+ qtask->rsp.command = MGMT_IPC_SESSION_SYNC;
session_conn_reopen(&session->conn[0], qtask, STOP_CONN_RECOVER);
log_debug(3, "Started sync iSCSI session %d", session->id);
@@ -1898,9 +1957,10 @@
mgmt_ipc_err_e rc = MGMT_IPC_OK;
conn = &session->conn[0];
- if (conn->state == STATE_XPT_WAIT &&
+ if (session->sync_qtask ||
+ (conn->state == STATE_XPT_WAIT &&
(session->r_stage == R_STAGE_NO_CHANGE ||
- session->r_stage == R_STAGE_SESSION_REDIRECT)) {
+ session->r_stage == R_STAGE_SESSION_REDIRECT))) {
log_error("session in invalid state for logout. "
"Try again later\n");
return MGMT_IPC_ERR_INTERNAL;
@@ -1911,7 +1971,7 @@
/* FIXME: implement Logout Request */
qtask->conn = conn;
- qtask->u.login.rsp.command = MGMT_IPC_SESSION_LOGOUT;
+ qtask->rsp.command = MGMT_IPC_SESSION_LOGOUT;
conn->logout_qtask = qtask;
switch (conn->state) {
Index: usr/isns_proto.h
===================================================================
--- usr/isns_proto.h (revision 0)
+++ usr/isns_proto.h (revision 779)
@@ -0,0 +1,200 @@
+/*
+ * iSNS protocol data types
+ *
+ * Copyright (C) 2006 FUJITA Tomonori <tomof@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef ISNS_PROTO_H
+#define ISNS_PROTO_H
+
+#define ISNS_PORT 3205
+#define ISNS_ALIGN 4
+
+struct isns_hdr {
+ uint16_t version;
+ uint16_t function;
+ uint16_t length;
+ uint16_t flags;
+ uint16_t transaction;
+ uint16_t sequence;
+ uint32_t pdu[0];
+} __attribute__ ((packed));
+
+struct isns_tlv {
+ uint32_t tag;
+ uint32_t length;
+ uint32_t value[0];
+} __attribute__ ((packed));
+
+/* Commands and responses (4.1.3) */
+#define ISNS_FUNC_DEV_ATTR_REG 0x0001
+#define ISNS_FUNC_DEV_ATTR_QRY 0x0002
+#define ISNS_FUNC_DEV_GET_NEXT 0x0003
+#define ISNS_FUNC_DEV_DEREG 0x0004
+#define ISNS_FUNC_SCN_REG 0x0005
+#define ISNS_FUNC_SCN_DEREG 0x0006
+#define ISNS_FUNC_SCN_EVENT 0x0007
+#define ISNS_FUNC_SCN 0x0008
+#define ISNS_FUNC_DD_REG 0x0009
+#define ISNS_FUNC_DD_DEREG 0x000a
+#define ISNS_FUNC_DDS_REG 0x000b
+#define ISNS_FUNC_DDS_DEREG 0x000c
+#define ISNS_FUNC_ESI 0x000d
+#define ISNS_FUNC_HEARTBEAT 0x000e
+
+#define ISNS_FUNC_DEV_ATTR_REG_RSP 0x8001
+#define ISNS_FUNC_DEV_ATTR_QRY_RSP 0x8002
+#define ISNS_FUNC_DEV_GET_NEXT_RSP 0x8003
+#define ISNS_FUNC_DEV_DEREG_RSP 0x8004
+#define ISNS_FUNC_SCN_REG_RSP 0x8005
+#define ISNS_FUNC_SCN_DEREG_RSP 0x8006
+#define ISNS_FUNC_SCN_EVENT_RSP 0x8007
+#define ISNS_FUNC_SCN_RSP 0x8008
+#define ISNS_FUNC_DD_REG_RSP 0x8009
+#define ISNS_FUNC_DD_DEREG_RSP 0x800a
+#define ISNS_FUNC_DDS_REG_RSP 0x800b
+#define ISNS_FUNC_DDS_DEREG_RSP 0x800c
+#define ISNS_FUNC_ESI_RSP 0x800d
+
+/* iSNSP flags (5.1.4) */
+#define ISNS_FLAG_CLIENT (1U << 15)
+#define ISNS_FLAG_SERVER (1U << 14)
+#define ISNS_FLAG_AUTH (1U << 13)
+#define ISNS_FLAG_REPLACE (1U << 12)
+#define ISNS_FLAG_LAST_PDU (1U << 11)
+#define ISNS_FLAG_FIRST_PDU (1U << 10)
+
+/* Response Status Codes (5.4) */
+#define ISNS_STATUS_SUCCESS 0
+#define ISNS_STATUS_UNKNOWN_ERROR 1
+#define ISNS_STATUS_FORMAT_ERROR 2
+#define ISNS_STATUS_INVALID_REGISTRATION 3
+#define ISNS_STATUS_RESERVED 4
+#define ISNS_STATUS_INVALID_QUERY 5
+#define ISNS_STATUS_SOURCE_UNKNOWN 6
+#define ISNS_STATUS_SOURCE_ABSENT 7
+#define ISNS_STATUS_SOURCE_UNAUTHORIZED 8
+#define ISNS_STATUS_NO_SUCH_ENTRY 9
+#define ISNS_STATUS_VERSION_NOT_SUPPORTED 10
+#define ISNS_STATUS_INTERNAL_ERROR 11
+#define ISNS_STATUS_BUSY 12
+#define ISNS_STATUS_OPTION_NOT_UNDERSTOOD 13
+#define ISNS_STATUS_INVALID_UPDATE 14
+#define ISNS_STATUS_MESSAGE_NOT_SUPPORTED 15
+#define ISNS_STATUS_SCN_EVENT_REJECTED 16
+#define ISNS_STATUS_SCN_REGISTRATION_REJECTED 17
+#define ISNS_STATUS_ATTRIBUTE_NOT_IMPLEMENTED 18
+#define ISNS_STATUS_FC_DOMAIN_ID_NOT_AVAILABLE 19
+#define ISNS_STATUS_FC_DOMAIN_ID_NOT_ALLOCATED 20
+#define ISNS_STATUS_ESI_NOT_AVAILABLE 21
+#define ISNS_STATUS_INVALIDE_DEREGISTRATION 22
+#define ISNS_STATUS_REGISTRATION_NOT_SUPPORTED 23
+
+/* Node type (5.4.2) */
+#define ISNS_NODE_CONTROL (1U << 2)
+#define ISNS_NODE_INITIATOR (1U << 1)
+#define ISNS_NODE_TARGET (1U << 0)
+
+/* Attributes (6.1) */
+#define ISNS_ATTR_DELIMITER 0
+#define ISNS_ATTR_ENTITY_IDENTIFIER 1
+#define ISNS_ATTR_ENTITY_PROTOCOL 2
+#define ISNS_ATTR_MANAGEMENT_IP_ADDRESS 3
+#define ISNS_ATTR_TIMESTAMP 4
+#define ISNS_ATTR_PROTOCOL_VERSION_RANGE 5
+#define ISNS_ATTR_REGISTRATION_PERIOD 6
+#define ISNS_ATTR_ENTITY_INDEX 7
+#define ISNS_ATTR_ENTITY_NEXT_INDEX 8
+#define ISNS_ATTR_ISAKMP_PHASE1 11
+#define ISNS_ATTR_CERTIFICATE 12
+#define ISNS_ATTR_PORTAL_IP_ADDRESS 16
+#define ISNS_ATTR_PORTAL_PORT 17
+#define ISNS_ATTR_PORTAL_SYMBOLIC_NAME 18
+#define ISNS_ATTR_ESI_INTERVAL 19
+#define ISNS_ATTR_ESI_PORT 20
+#define ISNS_ATTR_PORTAL_INDEX 22
+#define ISNS_ATTR_SCN_PORT 23
+#define ISNS_ATTR_PORTAL_NEXT_INDEX 24
+#define ISNS_ATTR_PORTAL_SECURITY_BITMAP 27
+#define ISNS_ATTR_PORTAL_ISAKMP_PHASE1 28
+#define ISNS_ATTR_PORTAL_ISAKMP_PHASE2 29
+#define ISNS_ATTR_PORTAL_CERTIFICATE 31
+#define ISNS_ATTR_ISCSI_NAME 32
+#define ISNS_ATTR_ISCSI_NODE_TYPE 33
+#define ISNS_ATTR_ISCSI_ALIAS 34
+#define ISNS_ATTR_ISCSI_SCN_BITMAP 35
+#define ISNS_ATTR_ISCSI_NODE_INDEX 36
+#define ISNS_ATTR_WWNN_TOKEN 37
+#define ISNS_ATTR_ISCSI_NODE_NEXT_INDEX 38
+#define ISNS_ATTR_ISCSI_AUTHMETHOD 42
+#define ISNS_ATTR_PG_ISCSI_NAME 48
+#define ISNS_ATTR_PG_PORTAL_IP_ADDRESS 49
+#define ISNS_ATTR_PG_PORTAL_PORT 50
+#define ISNS_ATTR_PG_TAG 51
+#define ISNS_ATTR_PG_INDEX 52
+#define ISNS_ATTR_PG_NEXT_INDEX 53
+#define ISNS_ATTR_FC_PORT_NAME_WWPN 64
+#define ISNS_ATTR_PORT_ID 65
+#define ISNS_ATTR_PORT_TYPE 66
+#define ISNS_ATTR_SYMBOLIC_PORT_NAME 67
+#define ISNS_ATTR_FABRIC_PORT_NAME 68
+#define ISNS_ATTR_HARD_ADDRESS 69
+#define ISNS_ATTR_PORT_IP_ADDRESS 70
+#define ISNS_ATTR_CLASS_OF_SERVICE 71
+#define ISNS_ATTR_FC4_TYPES 72
+#define ISNS_ATTR_FC4_DESCRIPOTR 73
+#define ISNS_ATTR_FC4_FEATURES 74
+#define ISNS_ATTR_IFCP_SCN_BITMAP 75
+#define ISNS_ATTR_PORT_ROLE 76
+#define ISNS_ATTR_PERMANENT_PORT_NAME 77
+#define ISNS_ATTR_FC4_TYPE_CODE 95
+#define ISNS_ATTR_FC_NODE_NAME_WWNN 96
+#define ISNS_ATTR_SYMBOLIC_NODE_NAME 97
+#define ISNS_ATTR_NODE_IP_ADDRESS 98
+#define ISNS_ATTR_NODE_IPA 99
+#define ISNS_ATTR_PORXY_ISCSI_NAME 101
+#define ISNS_ATTR_SWITCH_NAME 128
+#define ISNS_ATTR_PREFERRED_ID 129
+#define ISNS_ATTR_ASSIGNED_ID 130
+#define ISNS_ATTR_VIRTUAL_FABRIC_ID 131
+#define ISNS_ATTR_ISNS_SERVER_VENDOR_OUI 256
+#define ISNS_ATTR_DD_SET_ID 2049
+#define ISNS_ATTR_DD_SET_SYM_NAME 2050
+#define ISNS_ATTR_DD_SET_STATUS 2051
+#define ISNS_ATTR_DD_SET_NEXT_ID 2052
+#define ISNS_ATTR_DD_ID 2065
+#define ISNS_ATTR_DD_SYMBOLIC_NAME 2066
+#define ISNS_ATTR_DD_MEMBER_ISCSI_INDEX 2067
+#define ISNS_ATTR_DD_MEMBER_ISCSI_NAME 2068
+#define ISNS_ATTR_DD_MEMBER_FC_PORT_NAME 2069
+#define ISNS_ATTR_DD_MEMBER_PORTAL_INDEX 2070
+#define ISNS_ATTR_DD_MEMBER_IP_ADDR 2071
+#define ISNS_ATTR_DD_MEMBER_TCP_UDP 2072
+#define ISNS_ATTR_DD_FEATURES 2078
+#define ISNS_ATTR_DD_ID_NEXT_ID 2079
+
+/* SCN flags (6.4.4) */
+#define ISNS_SCN_FLAG_INITIATOR (1U << 24)
+#define ISNS_SCN_FLAG_TARGET (1U << 25)
+#define ISNS_SCN_FLAG_MANAGEMENT (1U << 26)
+#define ISNS_SCN_FLAG_OBJECT_REMOVE (1U << 27)
+#define ISNS_SCN_FLAG_OBJECT_ADDED (1U << 28)
+#define ISNS_SCN_FLAG_OBJECT_UPDATED (1U << 29)
+#define ISNS_SCN_FLAG_DD_REMOVED (1U << 30)
+#define ISNS_SCN_FLAG_DD_ADDED (1U << 31)
+#endif
Index: usr/netlink.c
===================================================================
--- usr/netlink.c (revision 754)
+++ usr/netlink.c (revision 779)
@@ -52,11 +52,11 @@
static int ctldev_handle(void);
#define NLM_BUF_DEFAULT_MAX \
- (NLMSG_SPACE(DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH + \
+ (NLMSG_SPACE(ISCSI_DEF_MAX_RECV_SEG_LEN + \
sizeof(struct iscsi_hdr)))
#define PDU_SENDBUF_DEFAULT_MAX \
- (DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH + sizeof(struct iscsi_hdr))
+ (ISCSI_DEF_MAX_RECV_SEG_LEN + sizeof(struct iscsi_hdr))
#define NLM_SETPARAM_DEFAULT_MAX \
(NI_MAXHOST + 1 + sizeof(struct iscsi_uevent))
Index: usr/discovery.c
===================================================================
--- usr/discovery.c (revision 754)
+++ usr/discovery.c (revision 779)
@@ -667,8 +667,8 @@
config->send_async_text : -1;
session->conn[0].hdrdgst_en = ISCSI_DIGEST_NONE;
session->conn[0].datadgst_en = ISCSI_DIGEST_NONE;
- session->conn[0].max_recv_dlength = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH;
- session->conn[0].max_xmit_dlength = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH;
+ session->conn[0].max_recv_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN;
+ session->conn[0].max_xmit_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN;
session->reopen_cnt = config->reopen_max;
Index: usr/initiator.h
===================================================================
--- usr/initiator.h (revision 754)
+++ usr/initiator.h (revision 779)
@@ -120,7 +120,7 @@
struct iscsi_session *session;
iscsi_login_context_t login_context;
struct queue_task *logout_qtask;
- char data[DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH];
+ char data[ISCSI_DEF_MAX_RECV_SEG_LEN];
char host[NI_MAXHOST]; /* scratch */
iscsi_conn_state_e state;
actor_t connect_timer;
@@ -186,22 +186,9 @@
typedef struct queue_task {
iscsi_conn_t *conn;
- union {
- /* iSCSI requests originated via IPC */
- struct ipcreq_login {
- iscsiadm_req_t req;
- iscsiadm_rsp_t rsp;
- int mgmt_ipc_fd;
- } login;
- struct ipcreq_logout {
- iscsiadm_req_t req;
- iscsiadm_rsp_t rsp;
- int mgmt_ipc_fd;
- } logout;
- /* iSCSI requests originated via CTL */
- struct ctlreq_recv_pdu {
- } recv_pdu;
- } u;
+ iscsiadm_req_t req;
+ iscsiadm_rsp_t rsp;
+ int mgmt_ipc_fd;
} queue_task_t;
typedef enum iscsi_provider_status_e {
@@ -283,6 +270,9 @@
iscsi_session_r_stage_e r_stage;
uint32_t replacement_timeout;
+ /* sync up fields */
+ queue_task_t *sync_qtask;
+
/* session's processing */
actor_t mainloop;
queue_t *queue;
@@ -368,4 +358,10 @@
extern mgmt_ipc_err_e iscsi_sync_session(node_rec_t *rec, queue_task_t
*tsk, uint32_t sid);
+/* isns.c */
+extern int isns_init(void);
+extern void isns_handle(int);
+extern void isns_exit(void);
+extern int isns_dev_attr_query_task(queue_task_t *qtask);
+
#endif /* INITIATOR_H */
Index: usr/login.c
===================================================================
--- usr/login.c (revision 754)
+++ usr/login.c (revision 779)
@@ -1079,7 +1079,7 @@
if ((conn->hdrdgst_en != ISCSI_DIGEST_NONE) ||
(conn->datadgst_en != ISCSI_DIGEST_NONE) ||
(conn->max_recv_dlength !=
- DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH))
+ ISCSI_DEF_MAX_RECV_SEG_LEN))
conn->next_stage =
ISCSI_OP_PARMS_NEGOTIATION_STAGE;
else
Index: usr/isns.c
===================================================================
--- usr/isns.c (revision 0)
+++ usr/isns.c (revision 779)
@@ -0,0 +1,794 @@
+/*
+ * iSNS functions
+ *
+ * Copyright (C) 2006 FUJITA Tomonori <tomof@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "initiator.h"
+#include "idbm.h"
+#include "log.h"
+#include "util.h"
+#include "isns_proto.h"
+
+enum isns_task_state {
+ ISNS_TASK_WAIT_CONN,
+ ISNS_TASK_SEND_PDU,
+ ISNS_TASK_RECV_PDU,
+};
+
+struct isns_task {
+ int state;
+ int fd;
+ int len;
+ char data[ISCSI_DEF_MAX_RECV_SEG_LEN];
+ int transaction;
+ int done;
+ int retry;
+ queue_task_t *qtask;
+};
+
+static actor_t isns_actor;
+static queue_t *isns_queue = NULL;
+static struct sockaddr_storage ss;
+static uint16_t transaction;
+
+static char isns_address[NI_MAXHOST];
+static int isns_port = 3205, isns_listen_port, max_retry = 10000;
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#define get_hdr_param(hdr, function, length, flags, transaction, sequence) \
+{ \
+ function = ntohs(hdr->function); \
+ length = ntohs(hdr->length); \
+ flags = ntohs(hdr->flags); \
+ transaction = ntohs(hdr->transaction); \
+ sequence = ntohs(hdr->sequence); \
+}
+
+/* use io.c */
+static int set_non_blocking(int fd)
+{
+ int res = fcntl(fd, F_GETFL);
+
+ if (res == -1)
+ log_warning("unable to get fd flags %m");
+ else {
+ res = fcntl(fd, F_SETFL, res | O_NONBLOCK);
+ if (res)
+ log_warning("unable to set fd flags %m");
+ }
+
+ return res;
+}
+
+static void
+isns_hdr_init(struct isns_hdr *hdr, uint16_t function, uint16_t length,
+ uint16_t flags, uint16_t trans, uint16_t sequence)
+{
+ hdr->version = htons(0x0001);
+ hdr->function = htons(function);
+ hdr->length = htons(length);
+ hdr->flags = htons(flags);
+ hdr->transaction = htons(trans);
+ hdr->sequence = htons(sequence);
+}
+
+static int
+isns_tlv_set(struct isns_tlv **tlv, uint32_t tag, uint32_t length, void *value)
+{
+ if (length)
+ memcpy((*tlv)->value, value, length);
+ if (length % ISNS_ALIGN)
+ length += (ISNS_ALIGN - (length % ISNS_ALIGN));
+
+ (*tlv)->tag = htonl(tag);
+ (*tlv)->length = htonl(length);
+
+ length += sizeof(struct isns_tlv);
+ *tlv = (struct isns_tlv *) ((char *) *tlv + length);
+
+ return length;
+}
+
+static void build_dev_reg_req(struct isns_task *task)
+{
+ struct isns_hdr *hdr = (struct isns_hdr *) task->data;
+ struct isns_tlv *tlv = (struct isns_tlv *) hdr->pdu;
+ struct sockaddr_storage lss;
+ static uint8_t ip[16];
+ char eid[NI_MAXHOST];
+ char *name = dconfig->initiator_name;
+ char *alias = dconfig->initiator_alias;
+ socklen_t slen = sizeof(lss);
+ int i;
+ uint16_t flags = 0, length = 0;
+ uint32_t addr;
+ uint32_t port;
+ uint32_t node = htonl(ISNS_NODE_INITIATOR);
+ uint32_t type = htonl(2);
+
+ memset(hdr, 0, sizeof(task->data));
+
+ getsockname(task->fd, (struct sockaddr *) &lss, &slen);
+ getnameinfo((struct sockaddr *) &lss, sizeof(lss), eid, sizeof(eid),
+ NULL, 0, 0);
+
+ switch (lss.ss_family) {
+ case AF_INET:
+ addr = (((struct sockaddr_in *) &lss)->sin_addr.s_addr);
+
+ ip[10] = ip[11] = 0xff;
+ ip[15] = 0xff & (addr >> 24);
+ ip[14] = 0xff & (addr >> 16);
+ ip[13] = 0xff & (addr >> 8);
+ ip[12] = 0xff & addr;
+ port = ((struct sockaddr_in *) &lss)->sin_port;
+ break;
+ case AF_INET6:
+ for (i = 0; i < ARRAY_SIZE(ip); i++)
+ ip[i] = ((struct sockaddr_in6 *) &lss)->sin6_addr.s6_addr[i];
+ break;
+ port = ((struct sockaddr_in6 *) &lss)->sin6_port;
+ }
+
+ port = htonl(ntohs(port));
+
+ length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(name), name);
+ length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_IDENTIFIER,
+ strlen(eid), eid);
+ length += isns_tlv_set(&tlv, 0, 0, 0);
+ length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_IDENTIFIER,
+ strlen(eid), eid);
+
+ length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_PROTOCOL,
+ sizeof(type), &type);
+ length += isns_tlv_set(&tlv, ISNS_ATTR_PORTAL_IP_ADDRESS,
+ sizeof(ip), &ip);
+ length += isns_tlv_set(&tlv, ISNS_ATTR_PORTAL_PORT,
+ sizeof(port), &port);
+ flags = ISNS_FLAG_REPLACE;
+
+ port = htonl(isns_listen_port);
+ length += isns_tlv_set(&tlv, ISNS_ATTR_ESI_PORT,
+ sizeof(port), &port);
+
+ length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(name), name);
+ length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NODE_TYPE,
+ sizeof(node), &node);
+ if(alias)
+ length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_ALIAS,
+ strlen(alias), alias);
+
+ flags |= ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU;
+ task->transaction = ++transaction;
+ isns_hdr_init(hdr, ISNS_FUNC_DEV_ATTR_REG, length, flags,
+ task->transaction, 0);
+
+ task->len = length + sizeof(*hdr);
+}
+
+static int isns_connect(void)
+{
+ int err;
+ int fd;
+
+ fd = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP);
+ if (fd < 0) {
+ log_error("can't create socket %m");
+ return -errno;
+ }
+
+ err = set_non_blocking(fd);
+ if (err) {
+ log_error("can't set non-blocking %m");
+ close(fd);
+ return -errno;
+ }
+
+ err = connect(fd, (struct sockaddr *) &ss, sizeof(ss));
+ if (err && errno != EINPROGRESS) {
+ log_error("can't connect %m");
+ close(fd);
+ return -errno;
+ }
+ return fd;
+}
+
+static int isns_send_pdu(struct isns_task *task)
+{
+ int err;
+
+ err = write(task->fd, task->data + task->done, task->len - task->done);
+ if (err < 0) {
+ if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS) {
+ log_error("send fail %m");
+ return -1;
+ }
+ } else
+ task->done += err;
+
+ return 0;
+}
+static void isns_free_task(struct isns_task *task)
+{
+ close(task->fd);
+ free(task);
+}
+
+static int isns_recv_pdu(struct isns_task *task)
+{
+ struct isns_hdr *hdr = (struct isns_hdr *) task->data;
+ uint16_t function, length, flags, transaction, sequence;
+ int err, size;
+
+ if (task->done < sizeof(*hdr))
+ size = sizeof(*hdr) - task->done;
+ else
+ size = task->len + sizeof(*hdr) - task->done;
+
+ err = read(task->fd, task->data + task->done, size);
+ if (err <= 0) {
+ if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS) {
+ log_error("send fail %m");
+ return -1;
+ }
+ } else {
+ task->done += err;
+
+ if (task->done == sizeof(*hdr)) {
+ get_hdr_param(hdr, function, length, flags, transaction,
+ sequence);
+ task->len = length;
+ }
+ }
+ return 0;
+}
+
+static void add_new_target_node(char *targetname, uint8_t *ip, int port,
+ int tag)
+{
+ int err;
+ node_rec_t rec;
+ idbm_t *db;
+ char dst[INET6_ADDRSTRLEN];
+
+ memset(dst, 0, sizeof(dst));
+ if (!memcmp(ip, dst, 10) && ip[10] == 0xff && ip[11] == 0xff)
+ inet_ntop(AF_INET, ip + 12, dst, sizeof(dst));
+ else
+ inet_ntop(AF_INET6, ip, dst, sizeof(dst));
+
+ log_debug(1, "add a new target node:%s %s,%d %d",
+ targetname, dst, port, tag);
+
+ db = idbm_init(dconfig->config_file);
+
+ idbm_node_setup_defaults(&rec);
+ strncpy(rec.name, targetname, TARGET_NAME_MAXLEN);
+ rec.conn[0].port = port;
+ rec.tpgt = tag;
+ strncpy(rec.conn[0].address, dst, NI_MAXHOST);
+ err = idbm_new_node(db, &rec);
+ if (err)
+ log_error("Could not add new target node:%s %s,%d",
+ targetname, dst, port);
+
+ idbm_terminate(db);
+}
+
+static int qry_rsp_handle(struct isns_hdr *hdr)
+{
+ struct isns_tlv *tlv;
+ uint16_t function, length, flags, transaction, sequence;
+ uint32_t port, tag, status;
+ uint8_t *addr;
+ char *name;
+
+ get_hdr_param(hdr, function, length, flags, transaction, sequence);
+
+ status = (uint32_t) (*hdr->pdu);
+ if (status)
+ return status;
+
+ /* skip status */
+ tlv = (struct isns_tlv *) ((char *) hdr->pdu + 4);
+ length -= 4;
+
+ /* check node type in the message key*/
+ if ((ntohl(tlv->tag) != ISNS_ATTR_ISCSI_NODE_TYPE) ||
+ ntohl(*(tlv->value)) != ISNS_NODE_TARGET)
+ return EINVAL;
+
+ /* 12 + 8 bytes */
+ length -= (sizeof(*tlv) + 4 + 8);
+ if (length <= 0) {
+ log_error("No target found.");
+ return EINVAL;
+ }
+
+ tlv = (struct isns_tlv *) ((char *) tlv + 20);
+
+ name = NULL;
+ addr = NULL;
+ port = tag = 0;
+
+ /* FIXME: this assume the exact order. */
+ while (length) {
+ uint32_t vlen = ntohl(tlv->length);
+
+ switch (ntohl(tlv->tag)) {
+ case ISNS_ATTR_PG_ISCSI_NAME:
+ if (name && addr) {
+ add_new_target_node(name, addr, port, tag);
+ name = NULL;
+ addr = NULL;
+ }
+ name = (char *) tlv->value;
+ break;
+ case ISNS_ATTR_ISCSI_NODE_TYPE:
+ if (ntohl(*(tlv->value)) != ISNS_NODE_TARGET)
+ name = NULL;
+ break;
+ case ISNS_ATTR_PG_PORTAL_IP_ADDRESS:
+ addr = (uint8_t *) tlv->value;
+ break;
+ case ISNS_ATTR_PG_PORTAL_PORT:
+ port = ntohl(tlv->value[0]);
+ break;
+ case ISNS_ATTR_PG_TAG:
+ tag = ntohl(tlv->value[0]);
+ break;
+ case ISNS_ATTR_ISCSI_NAME:
+ case ISNS_ATTR_PORTAL_IP_ADDRESS:
+ case ISNS_ATTR_PORTAL_PORT:
+ break;
+ default:
+ log_error("unexpected type %d", ntohl(tlv->tag));
+ break;
+ }
+
+ length -= (sizeof(*tlv) + vlen);
+ tlv = (struct isns_tlv *) ((char *) tlv->value + vlen);
+ }
+
+ if (name && addr)
+ add_new_target_node(name, addr, port, tag);
+
+ return 0;
+}
+
+static void send_mgmt_rsp(struct isns_task *task, int err)
+{
+ mgmt_ipc_write_rsp(task->qtask,
+ err ? MGMT_IPC_ERR_ISNS_UNAVAILABLE : MGMT_IPC_OK);
+}
+
+static int isns_task_done(struct isns_task *task)
+{
+ struct isns_hdr *hdr = (struct isns_hdr *) task->data;
+ uint16_t function, length, flags, transaction, sequence;
+ uint32_t status = (uint32_t) (*hdr->pdu);
+ char *payload = (char *) hdr + sizeof(*hdr);
+ int finished = 1;
+
+ get_hdr_param(hdr, function, length, flags, transaction,
+ sequence);
+
+ if (function & 0x8000 && status)
+ log_error("error isns response %x %x", function, status);
+
+ switch (function) {
+ case ISNS_FUNC_DEV_ATTR_REG_RSP:
+ break;
+ case ISNS_FUNC_DEV_ATTR_QRY_RSP:
+ if (!status)
+ qry_rsp_handle((struct isns_hdr *)task->data);
+ send_mgmt_rsp(task, status);
+ break;
+ case ISNS_FUNC_ESI:
+ memmove(payload + 4, payload, length);
+ *((uint32_t *) payload) = 0;
+
+ length += 4;
+ flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU |
+ ISNS_FLAG_FIRST_PDU;
+ isns_hdr_init(hdr, ISNS_FUNC_ESI_RSP, length, flags,
+ transaction, 0);
+ task->state = ISNS_TASK_SEND_PDU;
+ task->len = length + sizeof(*hdr);
+ task->done = 0;
+
+ queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL);
+ actor_schedule(&isns_actor);
+ finished = 0;
+ break;
+ default:
+ log_error("unexpected function %d", function);
+ break;
+ }
+
+ return finished;
+}
+
+int isns_dev_attr_query_task(queue_task_t *qtask)
+{
+ int fd;
+ struct isns_hdr *hdr;
+ struct isns_tlv *tlv;
+ char *name = dconfig->initiator_name;
+ uint16_t flags, length = 0;
+ uint32_t node = htonl(ISNS_NODE_TARGET);
+ struct isns_task *task;
+
+ if (!strlen(isns_address))
+ return MGMT_IPC_ERR_ISNS_UNAVAILABLE;
+
+ fd = isns_connect();
+ if (fd < 0) {
+ log_error("%s %m", __FUNCTION__);
+ return MGMT_IPC_ERR_ISNS_UNAVAILABLE;
+ }
+
+ task = malloc(sizeof(*task));
+ if (!task) {
+ log_error("%s %m", __FUNCTION__);
+ close(fd);
+ return MGMT_IPC_ERR_NOMEM;
+ }
+ memset(task, 0, sizeof(*task));
+
+ task->qtask = qtask;
+ task->fd = fd;
+
+ hdr = (struct isns_hdr *) task->data;
+ tlv = (struct isns_tlv *) hdr->pdu;
+
+ memset(hdr, 0, sizeof(task->data));
+
+ length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(name), name);
+ length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NODE_TYPE,
+ sizeof(node), &node);
+ length += isns_tlv_set(&tlv, 0, 0, 0);
+ length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, 0, 0);
+ length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NODE_TYPE, 0, 0);
+ length += isns_tlv_set(&tlv, ISNS_ATTR_PORTAL_IP_ADDRESS, 0, 0);
+ length += isns_tlv_set(&tlv, ISNS_ATTR_PORTAL_PORT, 0, 0);
+ length += isns_tlv_set(&tlv, ISNS_ATTR_PG_ISCSI_NAME, 0, 0);
+ length += isns_tlv_set(&tlv, ISNS_ATTR_PG_PORTAL_IP_ADDRESS, 0, 0);
+ length += isns_tlv_set(&tlv, ISNS_ATTR_PG_PORTAL_PORT, 0, 0);
+ length += isns_tlv_set(&tlv, ISNS_ATTR_PG_TAG, 0, 0);
+
+ flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU;
+ task->transaction = ++transaction;
+ isns_hdr_init(hdr, ISNS_FUNC_DEV_ATTR_QRY, length, flags,
+ task->transaction, 0);
+
+ task->len = length + sizeof(*hdr);
+ task->state = ISNS_TASK_SEND_PDU;
+
+ qtask->rsp.command = MGMT_IPC_ISNS_DEV_ATTR_QUERY;
+
+ queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL);
+ actor_schedule(&isns_actor);
+
+ return MGMT_IPC_OK;
+}
+
+void isns_handle(int listen_fd)
+{
+ struct sockaddr_storage from;
+ socklen_t slen = sizeof(from);
+ int fd;
+ struct isns_task *task;
+
+ fd = accept(listen_fd, (struct sockaddr *) &from, &slen);
+ if (fd < 0) {
+ log_error("%s: accept error %m", __FUNCTION__);
+ return;
+ }
+
+ task = malloc(sizeof(*task));
+ if (!task) {
+ log_error("%s %m", __FUNCTION__);
+ close(fd);
+ return;
+ }
+
+ memset(task, 0, sizeof(*task));
+ task->state = ISNS_TASK_RECV_PDU;
+ task->fd = fd;
+
+ queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL);
+ actor_schedule(&isns_actor);
+}
+
+static void isns_poll(queue_item_t *item)
+{
+ int err, finished;
+ struct pollfd pfd;
+ struct isns_task *task = item->context;
+ struct isns_hdr *hdr = (struct isns_hdr *) task->data;
+ uint16_t function = ntohs(hdr->function);
+
+ pfd.fd = task->fd;
+ switch (task->state) {
+ case ISNS_TASK_WAIT_CONN:
+ case ISNS_TASK_SEND_PDU:
+ pfd.events = POLLOUT;
+ break;
+ case ISNS_TASK_RECV_PDU:
+ pfd.events = POLLIN;
+ }
+
+ err = poll(&pfd, 1, 1);
+ if (err > 0) {
+ switch (task->state) {
+ case ISNS_TASK_WAIT_CONN:
+ task->state = ISNS_TASK_SEND_PDU;
+ case ISNS_TASK_SEND_PDU:
+ err = isns_send_pdu(task);
+ if (err)
+ goto abort_task;
+ else {
+
+ if (task->done == task->len) {
+ task->state = ISNS_TASK_RECV_PDU;
+ task->done = task->len = 0;
+
+ if (function == ISNS_FUNC_ESI_RSP)
+ goto free_task;
+ }
+
+ queue_produce(isns_queue, EV_CONN_POLL, task, 0,
+ NULL);
+ actor_schedule(&isns_actor);
+ }
+ break;
+ case ISNS_TASK_RECV_PDU:
+ err = isns_recv_pdu(task);
+ if (err)
+ goto abort_task;
+ else {
+ if (task->done ==
+ task->len + sizeof(struct isns_hdr)) {
+ finished = isns_task_done(task);
+ if (finished)
+ goto free_task;
+ } else {
+ /* need to read more */
+ queue_produce(isns_queue, EV_CONN_POLL,
+ task, 0, NULL);
+ actor_schedule(&isns_actor);
+ }
+ }
+ }
+ } else if (!err) {
+ /* FIXME */
+ if (task->retry++ > max_retry) {
+ log_error("abort task");
+ goto abort_task;
+ } else {
+ queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL);
+ actor_schedule(&isns_actor);
+ }
+ }
+
+ return;
+abort_task:
+ if (task->qtask)
+ send_mgmt_rsp(task, 1);
+free_task:
+ isns_free_task(task);
+}
+
+static void isns_control(void *data)
+{
+ int count = isns_queue->count, i;
+ int err;
+ unsigned char item_buf[sizeof(queue_item_t) + EVENT_PAYLOAD_MAX];
+ queue_item_t *item = (queue_item_t *)(void *)item_buf;
+
+ for (i = 0; i < count; i++) {
+ err = queue_consume(isns_queue, EVENT_PAYLOAD_MAX, item);
+ if (err == QUEUE_IS_EMPTY) {
+ log_debug(4, "%d items flushed while mainloop "
+ "was processing", count - i);
+ break;
+ }
+
+ switch (item->event_type) {
+ case EV_CONN_POLL:
+ isns_poll(item);
+ break;
+ default:
+ log_error("%d unknown event type", item->event_type);
+ break;
+ }
+ }
+}
+
+static int isns_dev_register(void)
+{
+ struct isns_task *task;
+
+ task = malloc(sizeof(*task));
+ if (!task)
+ return -ENOMEM;
+ memset(task, 0, sizeof(*task));
+
+ task->fd = isns_connect();
+ if (task->fd < 0) {
+ free(task);
+ return -ENOMEM;
+ }
+
+ task->state = ISNS_TASK_WAIT_CONN;
+ build_dev_reg_req(task);
+ queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL);
+
+ actor_new(&isns_actor, isns_control, NULL);
+ actor_schedule(&isns_actor);
+
+ return 0;
+}
+
+static int isns_listen_init(int *listen_fd)
+{
+ int fd, opt, err;
+ struct sockaddr_storage lss;
+ socklen_t slen;
+
+ fd = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP);
+ if (fd < 0) {
+ log_error("%s %m", __FUNCTION__);
+ return -errno;
+ }
+
+ opt = 1;
+ if (ss.ss_family == AF_INET6) {
+ err = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt,
+ sizeof(opt));
+ if (err)
+ log_error("%s %m", __FUNCTION__);
+ goto out;
+ }
+
+ err = listen(fd, 5);
+ if (err) {
+ log_error("%s %m", __FUNCTION__);
+ goto out;
+ }
+
+ slen = sizeof(lss);
+ err = getsockname(fd, (struct sockaddr *) &lss, &slen);
+ if (err) {
+ log_error("%s %m", __FUNCTION__);
+ goto out;
+ }
+
+ if (lss.ss_family == AF_INET6)
+ isns_listen_port = ((struct sockaddr_in6 *) &lss)->sin6_port;
+ else
+ isns_listen_port = ((struct sockaddr_in *) &lss)->sin_port;
+
+ isns_listen_port = ntohs(isns_listen_port);
+out:
+ if (err) {
+ close(fd);
+ return -1;
+ } else {
+ *listen_fd = fd;
+ return 0;
+ }
+}
+
+int isns_init(void)
+{
+ char buf[2048], port[NI_MAXSERV];
+ int fd = -1, err;
+ FILE *f;
+
+ f = fopen(dconfig->config_file, "r");
+ if (!f)
+ return -EIO;
+
+ while (fgets(buf, sizeof(buf), f)) {
+ /* FIXME */
+ if (buf[strlen(buf) - 1] == '\n')
+ buf[strlen(buf) - 1] = '\0';
+ if (!strncmp(buf, "isns.address = ", 15))
+ strncpy(isns_address, buf + 15, sizeof(isns_address));
+ else if (!strncmp(buf, "isns.port = ", 12))
+ isns_port = atoi(buf + 12);
+ }
+
+ fclose(f);
+
+ if (!strlen(isns_address))
+ return -1;
+
+ isns_queue = queue_create(4, 4, NULL, NULL);
+ if (!isns_queue) {
+ log_error("can't create queue %m");
+ return -ENOMEM;
+ }
+
+ snprintf(port, sizeof(port), "%d", isns_port);
+ err = resolve_address(isns_address, port, &ss);
+ if (err) {
+ log_error("can't resolve address %m, %s", isns_address);
+ goto free_queue;
+ }
+
+ err = isns_listen_init(&fd);
+ if (err)
+ goto free_queue;
+
+ isns_dev_register();
+ return fd;
+
+free_queue:
+ queue_destroy(isns_queue);
+ return err;
+}
+
+void isns_exit(void)
+{
+ int err, count, i;
+ unsigned char item_buf[sizeof(queue_item_t) + EVENT_PAYLOAD_MAX];
+ queue_item_t *item = (queue_item_t *)(void *)item_buf;
+
+ if (!isns_queue)
+ return;
+
+ count = isns_queue->count;
+ /*
+ * TODO: Add some code to gracefully shutdown.
+ */
+ for (i = 0; i < count; i++) {
+ err = queue_consume(isns_queue, EVENT_PAYLOAD_MAX, item);
+ if (err == QUEUE_IS_EMPTY) {
+ log_debug(4, "%d items flushed while mainloop "
+ "was processing", count - i);
+ break;
+ }
+
+ log_debug(4, "Dropping event type %d\n", item->event_type);
+ switch (item->event_type) {
+ case EV_CONN_POLL:
+ isns_free_task(item->context);
+ continue;
+ default:
+ log_error("%d unknown event type", item->event_type);
+ continue;
+ }
+ }
+
+ queue_destroy(isns_queue);
+}
Index: usr/iscsid.c
===================================================================
--- usr/iscsid.c (revision 754)
+++ usr/iscsid.c (revision 779)
@@ -154,6 +154,7 @@
char *initiatorname_file = INITIATOR_NAME_FILE;
char *pid_file = PID_FILE;
int ch, longindex;
+ int isns_fd;
uid_t uid = 0;
gid_t gid = 0;
struct sigaction sa_old;
@@ -318,7 +319,9 @@
}
actor_init();
- event_loop(ipc, control_fd, mgmt_ipc_fd);
+ isns_fd = isns_init();
+ event_loop(ipc, control_fd, mgmt_ipc_fd, isns_fd);
+ isns_exit();
log_debug(1, "daemon stopping");
return 0;
}
Index: usr/iscsistart.c
===================================================================
--- usr/iscsistart.c (revision 754)
+++ usr/iscsistart.c (revision 779)
@@ -328,7 +328,7 @@
* Start Main Event Loop
*/
actor_init();
- event_loop(ipc, control_fd, mgmt_ipc_fd);
+ event_loop(ipc, control_fd, mgmt_ipc_fd, -1);
ipc->ctldev_close();
mgmt_ipc_close(mgmt_ipc_fd);
Index: usr/iscsiadm.c
===================================================================
--- usr/iscsiadm.c (revision 754)
+++ usr/iscsiadm.c (revision 779)
@@ -99,9 +99,9 @@
printf("\
iscsiadm -m discovery [ -dhV ] [ -t type -p ip:port [ -l ] ] | [ -p ip:port ] \
[ -o operation ] [ -n name ] [ -v value ]\n\
-iscsiadm -m node [ -dhV ] [ -L all,manual,automatic ] [ -U all,manual,automatic ] [ -S ] [ [ -T targetname -p ip:port | -M sysdir ] [ -l | -u ] ] \
+iscsiadm -m node [ -dhV ] [ -L all,manual,automatic ] [ -U all,manual,automatic ] [ -S ] [ [ -T targetname -p ip:port ] [ -l | -u ] ] \
[ [ -o operation ] [ -n name ] [ -v value ] [ -p ip:port ] ]\n\
-iscsiadm -m session [ -dhV ] [ -r sessionid [ -i | -R | -u | -s ] [ -o operation ] [ -n name ] [ -v value ] ]\n");
+iscsiadm -m session [ -dhV ] [ -r sessionid | sysfsdir [ -i | -R | -u | -s ] [ -o operation ] [ -n name ] [ -v value ] ]\n");
}
exit(status == 0 ? 0 : -1);
}
@@ -256,11 +256,13 @@
struct session_mgmt_fn *mgmt = data;
char *mode = mgmt->mode;
idbm_t *db = mgmt->db;
+ iscsi_provider_t *p;
node_rec_t rec;
int rc = 0;
/* for now skip qlogic and other HW and offload drivers */
- if (!get_transport_by_sid(sid))
+ p = get_transport_by_sid(sid);
+ if (!p)
return 0;
if (idbm_node_read(db, &rec, targetname, address, port)) {
@@ -272,6 +274,11 @@
targetname, address, port);
return 0;
}
+
+ /* multiple drivers could be connected to the same portal */
+ if (strcmp(rec.transport_name, p->name))
+ return 0;
+
/*
* we always skip on boot because if the user killed this on
* they would not be able to do anything
@@ -726,6 +733,21 @@
return rc;
}
+static int isns_dev_attr_query(idbm_t *db)
+{
+ iscsiadm_req_t req;
+ iscsiadm_rsp_t rsp;
+ int err;
+
+ memset(&req, 0, sizeof(iscsiadm_req_t));
+ req.command = MGMT_IPC_ISNS_DEV_ATTR_QUERY;
+
+ err = do_iscsid(&ipc_fd, &req, &rsp);
+ if (!err)
+ idbm_for_each_node(db, NULL, print_node_info);
+ return err;
+}
+
static int
verify_mode_params(int argc, char **argv, char *allowed, int skip_m)
{
@@ -874,6 +896,57 @@
return rc;
}
+static int parse_sid(char *session)
+{
+ struct stat statb;
+ char sys_session[64], *start, *last;
+ int sid = -1, len;
+
+ if (stat(session, &statb)) {
+ log_debug(1, "Could not stat %s failed with %d",
+ session, errno);
+ if (index(session, '/')) {
+ log_error("%s is an invalid session path\n",
+ session);
+ exit(1);
+ }
+ return atoi(session);
+ }
+
+ if (!S_ISDIR(statb.st_mode)) {
+ log_error("%s is not a directory", session);
+ exit(1);
+ }
+
+ /*
+ * Given sysfs_device is a directory name of the form:
+ *
+ * /sys/devices/platform/hostH/sessionS/targetH:B:I/H:B:I:L
+ * /sys/devices/platform/hostH/sessionS/targetH:B:I
+ * /sys/devices/platform/hostH/sessionS
+ *
+ * We want to set sys_session to sessionS
+ */
+ last = NULL;
+ start = strstr(session, "session");
+ if (start && strncmp(start, "session", 7) == 0) {
+ len = strlen(start);
+ last = index(start, '/');
+ /*
+ * If '/' not found last is NULL.
+ */
+ if (last)
+ len = last - start;
+ strncpy(sys_session, start, len);
+ } else {
+ log_error("Unable to find session in %s", session);
+ exit(1);
+ }
+
+ sscanf(sys_session, "session%d", &sid);
+ return sid;
+}
+
int
main(int argc, char **argv)
{
@@ -926,7 +999,7 @@
value = optarg;
break;
case 'r':
- sid = atoi(optarg);
+ sid = parse_sid(optarg);
if (sid < 0) {
log_error("invalid sid '%s'",
optarg);
@@ -1028,9 +1101,10 @@
rc = -1;
goto out;
} else if (type == DISCOVERY_TYPE_ISNS) {
- log_error("iSNS discovery is not fully "
- "implemented yet.");
- rc = -1;
+ if ((rc = isns_dev_attr_query(db)) > 0) {
+ iscsid_handle_error(rc);
+ rc = -1;
+ }
goto out;
} else if (type < 0) {
if (ip) {
Index: etc/iscsid.conf
===================================================================
--- etc/iscsid.conf (revision 754)
+++ etc/iscsid.conf (revision 779)
@@ -7,6 +7,13 @@
# and man page for iscsiadm for details on the --op command.
#
+################
+# iSNS settings
+################
+# Address of iSNS server
+#isns.address = 192.168.0.1
+#isns.port = 3205
+
#*****************
# Startup settings
#*****************
@@ -124,8 +131,8 @@
# in an iSCSI PDU from a target, edit the following line.
#
# The value is the number of bytes in the range of 512 to (2^24-1) and
-# the default is 65536
-node.conn[0].iscsi.MaxRecvDataSegmentLength = 65536
+# the default is 131072
+node.conn[0].iscsi.MaxRecvDataSegmentLength = 131072
# To allow the targets to control the setting of the digest checking,
# with the initiator requesting a preference of enabling the checking, uncomment# one or both of the following lines:
@@ -151,3 +158,8 @@
# The default is to never use DataDigests and to allow the target to control
# the setting of the HeaderDigest checking with the initiator requesting
# a preference of disabling the checking.
+
+#
+# To enable the iSNS server
+#isns.address = 192.168.0.1
+#isns.port = 3205
Index: etc/initd/initd.suse
===================================================================
--- etc/initd/initd.suse (revision 754)
+++ etc/initd/initd.suse (revision 779)
@@ -69,7 +69,7 @@
else
echo -n "Starting iSCSI initiator service: "
modprobe iscsi_tcp
- modprobe ib_iser
+ modprobe -q ib_iser
startproc $DAEMON $ARGS
RETVAL=$?
rc_status -v
@@ -89,7 +89,7 @@
if [ "$RETVAL" == "0" ]; then
rm -f $PID_FILE
modprobe -r iscsi_tcp
- modprobe -r ib_iser
+ modprobe -q -r ib_iser
rc_failed 0
else
rc_failed 1
Index: Makefile
===================================================================
--- Makefile (revision 754)
+++ Makefile (revision 779)
@@ -15,7 +15,8 @@
initddir = $(etcdir)/init.d
MANPAGES = doc/iscsid.8 doc/iscsiadm.8 doc/iscsi_discovery.8
-PROGRAMS = usr/iscsid usr/iscsiadm utils/iscsi_discovery
+PROGRAMS = usr/iscsid usr/iscsiadm utils/iscsi_discovery \
+ utils/fwparam_ibft/fwparam_ibft utils/iscsi-iname
INSTALL = install
ETCFILES = etc/iscsid.conf
@@ -26,6 +27,8 @@
all:
$(MAKE) -C usr
$(MAKE) -C kernel
+ $(MAKE) -C utils
+ $(MAKE) -C utils/fwparam_ibft
@echo
@echo "Compilation complete Output file"
@echo "----------------------------------- ----------------"
@@ -34,22 +37,25 @@
@echo "Built iSCSI over TCP kernel module: kernel/iscsi_tcp.ko"
@echo "Built iSCSI daemon: usr/iscsid"
@echo "Built management application: usr/iscsiadm"
+ @echo "Built utility: utils/fwparam_ibft/fwparam_ibft"
@echo
@echo Read README file for detailed information.
clean:
+ $(MAKE) -C utils clean
$(MAKE) -C usr clean
$(MAKE) -C kernel clean
+ $(MAKE) -C utils/fwparam_ibft clean
# this is for safety
# now -jXXX will still be safe
# note that make may still execute the blocks in parallel
.NOTPARALLEL: install_usr install_programs install_initd \
install_initd_suse install_initd_redhat install_initd_debian \
- install_etc install_doc install_kernel
+ install_etc install_doc install_kernel install_iname
install: install_kernel install_programs install_doc install_etc \
- install_initd
+ install_initd install_iname
install_programs: $(PROGRAMS)
$(INSTALL) -d $(DESTDIR)$(sbindir)
@@ -93,4 +99,13 @@
install_kernel:
$(MAKE) -C kernel install_kernel
+install_iname:
+ if [ ! -f /etc/iscsi/initiatorname.iscsi ]; then \
+ echo "InitiatorName=`/sbin/iscsi-iname`" > /etc/iscsi/initiatorname.iscsi ; \
+ echo "***************************************************" ; \
+ echo "Setting InitiatorName to `cat /etc/iscsi/initiatorname.iscsi`" ; \
+ echo "To override edit /etc/iscsi/initiatorname.iscsi" ; \
+ echo "***************************************************" ; \
+ fi
+
# vim: ft=make tw=72 sw=4 ts=4:
Index: README
===================================================================
--- README (revision 754)
+++ README (revision 779)
@@ -4,7 +4,7 @@
=================================================================
- November 8, 2006
+ Jan 26, 2007
Contents
========
@@ -16,11 +16,9 @@
- 5. Open-iSCSI Configuration Utility
- 6. Configuration
- 7. Getting Started
-- 8. TBD
-- Appendix A. SendTargets snapshot.
+- 8. iSCSI System Info
-
1. In This Release
==================
@@ -36,7 +34,6 @@
1.1. Features
- highly optimized and very small-footprint data path;
- - multiple outstanding R2Ts;
- persistent configuration database;
- SendTargets discovery;
- CHAP;
@@ -151,6 +148,15 @@
The utility presents set of operations that a user can perform
on iSCSI nodes, sessions, connections, and discovery records.
+Open-iscsi does not use the term node as defined by the iSCSI RFC,
+where a node is a single iSCSI initiator or target. Open-iscsi uses the
+term node to refer to a portal on a target, so tools like iscsiadm
+require that --targetname and --portal argument be used when in node mode.
+
+For session mode, a session id (sid) is used. The sid of a session can be
+found by running iscsiadm -m session -i. The session id is not currently
+persistent and is partially determined by when the session is setup.
+
Note that some of the iSCSI Node and iSCSI Discovery operations
do not require iSCSI daemon (iscsid) loaded.
@@ -211,7 +217,8 @@
-h, --help display this help and exit
- Usage examples (using the one-letter options):
+ Usage examples using the one-letter options (see iscsiadm man page
+ for long options):
1) SendTargets iSCSI Discovery:
@@ -278,12 +285,17 @@
7. Getting Started
==================
-There are three steps needed to set up a system to use iscsi storage:
-1. automate iscsi startup using the init script.
-2. discover targets.
-3. automate target logins for future system reboots.
+There are three steps needed to set up a system to use iSCSI storage:
+7.1. iSCSI startup using the init script or manual startup.
+7.2. Discover targets.
+7.3. Automate target logins for future system reboots.
-1. automate iscsi startup using the init script
+The init scripts will start the iSCSI daemon and log into any
+connections or nodes that are set up for automatic login. If your distro
+does not have a init script, then you will have to start the daemon
+and log into the targets manually.
+
+7.1.1 iSCSI startup using the init script
-----------------------------------------------
Red Hat or Fedora:
@@ -312,8 +324,11 @@
will usually get you started.
-Other:
-------
+7.1.2 Manual Startup:
+---------------------
+
+7.1.2.1 Starting up the iSCSI daemon (iscsid) and loading modules:
+-----------------------------------------------------------------
If there is no initd script, you must start the tools by hand. First load the
iscsi modules with:
@@ -328,26 +343,70 @@
./iscsid -d8 -f &
-and use configuration utility to add/remove/update Discovery records,
-iSCSI Node records or monitor active iSCSI sessions (see above or the
-iscsiadm man files).
+7.1.2.2 Logging into Targets:
+---------------------------
+Use the configuration utility, iscsiadm, to add/remove/update Discovery
+records, iSCSI Node records or monitor active iSCSI sessions (see above or the
+iscsiadm man files and see section 7.2 below for how to discover targets).
- ./iscsiadm
+ ./iscsiadm -m node
+will print out the nodes that have been discovered as:
-To login:
+ 10.15.85.19:3260,3 iqn.1992-08.com.netapp:sn.33615311
+ 10.15.84.19:3260,2 iqn.1992-08.com.netapp:sn.33615311
+The format is:
+
+ip:port,target_portal_group_tag targetname
+
+where targetname is the name of the target and ip_address:port is the address
+and port of the portal. target_portal_group_tag, is the portal group tag of
+the portal, and is not used in iscsiadm commands.
+
+To login, take the ip, port and targetname from above and run:
+
./iscsiadm -m node -T targetname -p ip:port -l
-where targetname is the name of the target amd ip_address:port is the address
-and port of the portal of a discovered or manually added iSCSI Target Node
-(for iscsiadm usage examples see previous sections).
+In this example we would run
-2. discover targets
--------------------
-Once iscsi is up, you can perform discovery to targets using:
-iscsiadm -m discovery -t sendtargets -p 192.168.1.1:3260
+ ./iscsiadm -m node -T iqn.1992-08.com.netapp:sn.33615311 -p 10.15.84.19:3260 -l
+ Note: drop the portal group tag from the "iscsiadm -m node" output.
+
+7.2. Discover Targets
+---------------------
+Once the iSCSI service is running, you can perform discovery using
+SendTarget with:
+
+iscsiadm -m discovery -t sendtargets -p ip:port
+
+where "ip" is the address of the portal and port is the port.
+
+Or you can you perform discovery using iSNS by setting the address
+of the iSNS server in iscsid.conf with the "isns.address" value and
+running:
+
+iscsiadm -m discovery -t isns
+
+Both commands will print out the list of all discovered targets and their
+portals:
+
+# iscsiadm -m discovery -t st -p 10.15.85.19:3260
+10.15.85.19:3260,3 iqn.1992-08.com.netapp:sn.33615311
+10.15.84.19:3260,2 iqn.1992-08.com.netapp:sn.33615311
+
+Note: this prints out every node in the db including the ones just discovered.
+This is a bug and will change in future releases.
+
+The format for the output is:
+
+ip:port,target_portal_group_tag targetname
+
+In this example, for the first target the ip address is 10.15.85.19.
+The port is 3260. The target portal group is 3, and the target name
+is iqn.1992-08.com.netapp:sn.33615311.
+
While discovery targets are kept in the discovery db, they are
usefull only for re-discovery. The discovered targets (a.k.a. nodes)
are stored as records in the node db.
@@ -356,12 +415,16 @@
into the discovered nodes (making LUs from those nodes available as
storage), it is better to automate the login to the nodes we need.
-3. automate target logins for future system reboots
----------------------------------------------------
-Note: this may only work for Red Hat, Fedora and SUSE configurations
+If you wish to log into a target manually now, see section
+"7.1.2.2 Logging in targets" above.
-To automate login to a node, use the following with the record ID of the
-node discovered in the discovery above:
+7.3. Automate Target Logins for Future System Statups
+-----------------------------------------------------
+Note: this may only work for distros with init scripts.
+
+To automate login to a node, use the following with the record ID
+(record ID is the targetname and portal) of the node discovered in the
+discovery above:
iscsiadm -m node -T targetname -p ip:port --op update -n node.conn[0].startup -v automatic
Or to set the "node.conn[0].statup" attribute to "startup" as default for
@@ -369,36 +432,37 @@
node.conn[0].startup = automatic
-To login to all the automated nodes, simply restart the iscsi service
-e.g /etc/init.d/open-iscsi restart
+Setting this in iscsid.conf, will not affect existing nodes. It will only
+affect nodes that are discovered after setting the value.
+To login to all the automated nodes, simply restart the iscsi service:
+e.g /etc/init.d/open-iscsi restart. On your next startup the nodes will
+be logged into autmotically.
-8. TBD
-======
-To be completed:
+8. iSCSI System Info
+====================
- - Kernel tracing and Troubleshooting
- - Immediate and not-so-immediate plans
- - Useful scripts
- - White paper on Open-iSCSI design
+To get information about the running sessions: including the session and
+device state, session ids (sid) for session mode, and some of the
+negioated parameters, run:
+ iscsiadm -m session -i
-Appendix A. SendTargets iSCSI Discovery session snapshot.
-=========================================================
+If you are looking for something shorter like just the sid to node mapping
+run:
--bash-2.05b# ./iscsiadm -m discovery -tst -p 10.16.16.223:3260
-[02f611] 10.16.16.223:3260,1 iqn.2002-07.com.ttechnologies.target.a
-[01acd1] 17.1.1.223:3260,1 iqn.2002-07.com.ttechnologies.target.a
--bash-2.05b#
--bash-2.05b# ./iscsiadm -m node
-[02f611] 10.16.16.223:3260,1 iqn.2002-07.com.ttechnologies.target.a
-[01acd1] 17.1.1.223:3260,1 iqn.2002-07.com.ttechnologies.target.a
--bash-2.05b#
--bash-2.05b# ./iscsiadm -m discovery -tst -p 10.16.16.227:3260
-[02fb91] 10.16.16.227:3260,1 iqn.2001-04.com.example:storage.disk2.sys1.xyz
--bash-2.05b#
--bash-2.05b# ./iscsiadm -m node
-[02f611] 10.16.16.223:3260,1 iqn.2002-07.com.ttechnologies.target.a
-[02fb91] 10.16.16.227:3260,1 iqn.2001-04.com.example:storage.disk2.sys1.xyz
-[01acd1] 17.1.1.223:3260,1 iqn.2002-07.com.ttechnologies.target.a
+ iscsiadm -m session
+
+This will print the list of running sessions with the format:
+
+kernel module: [sid] ip:port,target_portal_group_tag targetname
+
+# iscsiadm -m session
+tcp: [2] 10.15.84.19:3260,2 iqn.1992-08.com.netapp:sn.33615311
+tcp: [3] 10.15.85.19:3260,3 iqn.1992-08.com.netapp:sn.33615311
+
+For example this first node is using the iscsi_tcp kernel module, has
+session id (sid) 2, is connected to a portal with address and port
+10.15.84.19:3260 in portal group 2 on the target,
+iqn.1992-08.com.netapp:sn.33615311.