forked from pool/open-iscsi
10547 lines
299 KiB
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.
|